Task 035: .ARC File Format
Task 035: .ARC File Format
File Format Specifications for the .ARC File Format
The .ARC file format is an early archive format developed by System Enhancement Associates (SEA). It is a lossless data compression and archival format used to store multiple files in a single container. The format does not have a central directory; instead, it consists of a sequence of file entries, each with a header followed by the (possibly compressed) data, terminated by an end marker.
1. List of Properties Intrinsic to the File Format
The intrinsic properties of the .ARC file format include the following structural elements and fields:
- Magic Number: A fixed byte value of 0x1A (26 in decimal) at the start of each entry header, used to identify the format.
- Compression Method: A 1-byte value indicating the compression algorithm applied to the file data. Common values include:
- 0: End of archive marker.
- 1: Stored (no compression, obsolete).
- 2: Stored (no compression).
- 3: Packed (RLE encoding).
- 4: Squeezed (Huffman coding after RLE).
- 5: Crunched (obsolete LZW variant).
- 6: Crunched (after packing, obsolete).
- 7: Crunched (after packing, faster hash).
- 8: Crunched (dynamic LZW after RLE).
- 9: Squashed (LZW variant without RLE).
- 10: Crushed (specific to PAK archiver).
- 11: Distilled (specific to PAK archiver).
- File Name: A 13-byte field containing the ASCII file name, null-terminated, including the extension period (e.g., "FILE.TXT\0").
- Compressed Size: A 4-byte little-endian unsigned integer representing the size of the compressed file data.
- Modification Timestamp: A 4-byte little-endian value in MS-DOS format, encoding date and time as follows:
- Bits 25-31: Year minus 1980 (7 bits).
- Bits 21-24: Month (4 bits, 1-12).
- Bits 16-20: Day (5 bits, 1-31).
- Bits 11-15: Hour (5 bits, 0-23).
- Bits 5-10: Minute (6 bits, 0-59).
- Bits 0-4: Second divided by 2 (5 bits, 0-29 representing 0-58 seconds).
- CRC-16: A 2-byte little-endian unsigned integer representing the cyclic redundancy check of the original (uncompressed) file data, using the polynomial x^16 + x^12 + x^5 + 1.
- Original Size: A 4-byte little-endian unsigned integer representing the size of the uncompressed file data.
- End Marker: A header with magic 0x1A and method 0, indicating the end of the archive (no additional fields or data).
- Entry Structure: Each entry is 29 bytes for the header followed by the compressed data. The archive is a concatenation of such entries.
- Extensibility: Supports informational and control items in later versions (methods 20-39), but these are optional and not core to basic functionality.
2. Two Direct Download Links for .ARC Files
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .ARC File Dump
Drag and drop a .ARC file here
4. Python Class for .ARC File Handling
import struct
import os
class ArcFile:
def __init__(self, filename):
self.filename = filename
self.entries = []
def read_and_print(self):
with open(self.filename, 'rb') as f:
data = f.read()
offset = 0
while offset < len(data):
if data[offset] != 0x1A:
break
method = data[offset + 1]
if method == 0:
break
filename = data[offset + 2:offset + 15].decode('ascii').rstrip('\x00')
compressed_size, = struct.unpack('<I', data[offset + 15:offset + 19])
timestamp, = struct.unpack('<I', data[offset + 19:offset + 23])
crc, = struct.unpack('<H', data[offset + 23:offset + 25])
original_size, = struct.unpack('<I', data[offset + 25:offset + 29])
print(f"File: {filename}")
print(f"Method: {method}")
print(f"Compressed Size: {compressed_size}")
print(f"Timestamp (MS-DOS): {timestamp}")
print(f"CRC: {crc}")
print(f"Original Size: {original_size}")
print("---")
self.entries.append((filename, method, compressed_size, timestamp, crc, original_size))
offset += 29 + compressed_size
def write(self, output_filename):
with open(output_filename, 'wb') as f:
for entry in self.entries:
filename, method, compressed_size, timestamp, crc, original_size = entry
header = struct.pack('<BB13sIIIH', 0x1A, method, filename.encode('ascii').ljust(13, b'\x00'), compressed_size, timestamp, crc, original_size)
f.write(header)
# Note: Data not written here; assumes data is not stored in class for simplicity. Extend for full write support.
f.write(b'\x1A\x00') # End marker
5. Java Class for .ARC File Handling
import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
public class ArcFile {
private String filename;
private ByteBuffer buffer;
public ArcFile(String filename) {
this.filename = filename;
}
public void readAndPrint() throws IOException {
try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
FileChannel channel = raf.getChannel();
buffer = ByteBuffer.allocate((int) channel.size());
channel.read(buffer);
buffer.flip();
}
int offset = 0;
while (offset < buffer.limit()) {
byte magic = buffer.get(offset);
if (magic != 0x1A) break;
byte method = buffer.get(offset + 1);
if (method == 0) break;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 13; i++) {
byte b = buffer.get(offset + 2 + i);
if (b == 0) break;
sb.append((char) b);
}
String fileName = sb.toString();
int compressedSize = buffer.getInt(offset + 15);
int timestamp = buffer.getInt(offset + 19);
short crc = buffer.getShort(offset + 23);
int originalSize = buffer.getInt(offset + 25);
System.out.println("File: " + fileName);
System.out.println("Method: " + method);
System.out.println("Compressed Size: " + compressedSize);
System.out.println("Timestamp (MS-DOS): " + timestamp);
System.out.println("CRC: " + crc);
System.out.println("Original Size: " + originalSize);
System.out.println("---");
offset += 29 + compressedSize;
}
}
public void write(String outputFilename) throws IOException {
// For write, would require storing entries and data; simplified to rewrite header structure only.
try (FileOutputStream fos = new FileOutputStream(outputFilename)) {
// Assume entries parsed; extend for full implementation.
fos.write(0x1A);
// Add entry headers and data here.
fos.write(0x00); // End marker simplified.
}
}
}
6. JavaScript Class for .ARC File Handling
class ArcFile {
constructor(filename) {
this.filename = filename;
}
async readAndPrint() {
const response = await fetch(this.filename); // Assuming local or CORS-allowed.
const buffer = await response.arrayBuffer();
const view = new DataView(buffer);
let offset = 0;
while (offset < buffer.byteLength) {
const magic = view.getUint8(offset);
if (magic !== 0x1A) break;
const method = view.getUint8(offset + 1);
if (method === 0) break;
let filename = '';
for (let i = 0; i < 13; i++) {
const byte = view.getUint8(offset + 2 + i);
if (byte === 0) break;
filename += String.fromCharCode(byte);
}
const compressedSize = view.getUint32(offset + 15, true);
const timestamp = view.getUint32(offset + 19, true);
const crc = view.getUint16(offset + 23, true);
const originalSize = view.getUint32(offset + 25, true);
console.log(`File: ${filename}`);
console.log(`Method: ${method}`);
console.log(`Compressed Size: ${compressedSize}`);
console.log(`Timestamp (MS-DOS): ${timestamp}`);
console.log(`CRC: ${crc}`);
console.log(`Original Size: ${originalSize}`);
console.log('---');
offset += 29 + compressedSize;
}
}
write(output) {
// Write functionality would require Blob or fs; simplified console log for demonstration.
console.log('Writing ARC file...');
// Implement with Blob for browser download.
}
}
7. C++ Class for .ARC File Handling
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <cstring>
struct ArcEntry {
uint8_t method;
char filename[14];
uint32_t compressed_size;
uint32_t timestamp;
uint16_t crc;
uint32_t original_size;
};
class ArcFile {
private:
std::string filename;
std::vector<ArcEntry> entries;
public:
ArcFile(const std::string& fn) : filename(fn) {}
void readAndPrint() {
std::ifstream file(filename, std::ios::binary | std::ios::ate);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> buffer(size);
if (file.read(buffer.data(), size)) {
size_t offset = 0;
while (offset < size) {
if (static_cast<uint8_t>(buffer[offset]) != 0x1A) break;
uint8_t method = static_cast<uint8_t>(buffer[offset + 1]);
if (method == 0) break;
ArcEntry entry;
entry.method = method;
std::memcpy(entry.filename, &buffer[offset + 2], 13);
entry.filename[13] = '\0';
std::memcpy(&entry.compressed_size, &buffer[offset + 15], 4);
std::memcpy(&entry.timestamp, &buffer[offset + 19], 4);
std::memcpy(&entry.crc, &buffer[offset + 23], 2);
std::memcpy(&entry.original_size, &buffer[offset + 25], 4);
std::cout << "File: " << entry.filename << std::endl;
std::cout << "Method: " << static_cast<int>(method) << std::endl;
std::cout << "Compressed Size: " << entry.compressed_size << std::endl;
std::cout << "Timestamp (MS-DOS): " << entry.timestamp << std::endl;
std::cout << "CRC: " << entry.crc << std::endl;
std::cout << "Original Size: " << entry.original_size << std::endl;
std::cout << "---" << std::endl;
entries.push_back(entry);
offset += 29 + entry.compressed_size;
}
}
}
void write(const std::string& output_filename) {
std::ofstream file(output_filename, std::ios::binary);
for (const auto& entry : entries) {
file.put(0x1A);
file.put(entry.method);
file.write(entry.filename, 13);
file.write(reinterpret_cast<const char*>(&entry.compressed_size), 4);
file.write(reinterpret_cast<const char*>(&entry.timestamp), 4);
file.write(reinterpret_cast<const char*>(&entry.crc), 2);
file.write(reinterpret_cast<const char*>(&entry.original_size), 4);
// Data not written; extend for full support.
}
file.put(0x1A);
file.put(0);
}
};