Task 309: .IMG File Format
Task 309: .IMG File Format
File Format Specifications for the .IMG File Format
The .IMG file format is a raw disk image format, commonly used for representing the contents of floppy disks, hard drives, or optical media as a sector-by-sector copy. It has no inherent header or structure in the file itself; it is essentially a binary dump of the disk data. The "file system" referred to is typically the FAT (File Allocation Table) file system embedded within the image (often FAT12 for floppy disk images). The specifications are therefore based on the FAT file system structure, with the boot sector (first 512 bytes) containing the key parameters. This structure is standard for MS-DOS and compatible systems. Byte order is little-endian.
- List of all the properties of this file format intrinsic to its file system:
The properties are the fields in the FAT boot sector (offset 0 to 511 bytes). These define the file system parameters:
- Jump Instruction (offset 0x00, size 3 bytes): Boot strap code to jump over the BPB to the boot loader code.
- OEM Name (offset 0x03, size 8 bytes): String identifying the operating system that formatted the volume (padded with spaces).
- Bytes per Sector (offset 0x0B, size 2 bytes): The size of a logical sector in bytes (usually 512).
- Sectors per Cluster (offset 0x0D, size 1 byte): Number of sectors per allocation unit/cluster (power of 2, e.g., 1, 2, 4).
- Reserved Sectors (offset 0x0E, size 2 bytes): Number of reserved sectors starting from sector 0 (at least 1).
- Number of FATs (offset 0x10, size 1 byte): Number of File Allocation Tables (usually 2).
- Root Directory Entries (offset 0x11, size 2 bytes): Maximum number of entries in the root directory (0 for FAT32).
- Total Sectors (offset 0x13, size 2 bytes): Total number of logical sectors (if > 65535 or 0, use Large Total Sectors).
- Media Descriptor (offset 0x15, size 1 byte): Code describing the media type (e.g., 0xF0 for 3.5" floppy).
- Sectors per FAT (offset 0x16, size 2 bytes): Number of sectors per FAT (0 for FAT32).
- Sectors per Track (offset 0x18, size 2 bytes): Sectors per track for interrupt 0x13 geometry.
- Number of Heads (offset 0x1A, size 2 bytes): Number of heads for interrupt 0x13 geometry.
- Hidden Sectors (offset 0x1C, size 4 bytes): Number of hidden sectors preceding the partition.
- Large Total Sectors (offset 0x20, size 4 bytes): Total number of logical sectors for large volumes.
- Physical Drive Number (offset 0x24, size 1 byte for FAT12/16; offset 0x40 for FAT32): BIOS drive number (0x00 removable, 0x80 fixed).
- Reserved (offset 0x25, size 1 byte for FAT12/16; offset 0x41 for FAT32): Reserved byte (usually 0).
- Extended Boot Signature (offset 0x26, size 1 byte for FAT12/16; offset 0x42 for FAT32): Signature (0x29 or 0x28) indicating extended fields follow.
- Volume ID (offset 0x27, size 4 bytes for FAT12/16; offset 0x43 for FAT32): Serial number of the volume.
- Volume Label (offset 0x2B, size 11 bytes for FAT12/16; offset 0x47 for FAT32): Volume label (padded with spaces).
- File System Type (offset 0x36, size 8 bytes for FAT12/16; offset 0x52 for FAT32): String like "FAT12 ", "FAT16 ", or "FAT32 ".
- Sectors per FAT Large (FAT32 only, offset 0x24, size 4 bytes): Number of sectors per FAT for FAT32.
- FAT Flags (FAT32 only, offset 0x28, size 2 bytes): FAT mirroring and active FAT flags.
- Version (FAT32 only, offset 0x2A, size 2 bytes): File system version (usually 0:0).
- Root Cluster (FAT32 only, offset 0x2C, size 4 bytes): Cluster number of the root directory start.
- FS Info Sector (FAT32 only, offset 0x30, size 2 bytes): Sector number of the FS information sector.
- Backup Boot Sector (FAT32 only, offset 0x32, size 2 bytes): Sector number of the backup boot sector.
- Reserved (FAT32 only, offset 0x34, size 12 bytes): Reserved bytes (usually 0).
- Boot Code (offset varies after BPB, up to 0x1FE): Executable boot loader code.
- Boot Sector Signature (offset 0x1FE, size 2 bytes): 0xAA55 indicating a valid boot sector.
(Note: To determine FAT type, check if Sectors per FAT at 0x16 is 0; if so, it's FAT32 and use FAT32-specific fields.)
- Two direct download links for files of format .IMG:
- https://raw.githubusercontent.com/codercowboy/freedosbootdisks/master/bootdisks/freedos.boot.disk.720K.img
- https://raw.githubusercontent.com/codercowboy/freedosbootdisks/master/bootdisks/freedos.boot.disk.1.4M.img
- Ghost blog embedded HTML JavaScript for drag and drop .IMG file to dump properties:
Drag and Drop .IMG File
- Python class for opening, decoding, reading, writing, and printing .IMG properties:
import struct
import os
class IMGParser:
def __init__(self, filename):
self.filename = filename
self.boot_sector = None
self.properties = {}
self.is_fat32 = False
self.read()
def read(self):
with open(self.filename, 'rb') as f:
self.boot_sector = f.read(512)
if len(self.boot_sector) < 512:
raise ValueError("File too small for .IMG boot sector")
self.decode()
def decode(self):
self.properties['Jump Instruction'] = struct.unpack_from('<BBB', self.boot_sector, 0)
self.properties['OEM Name'] = self.boot_sector[3:11].decode('ascii').rstrip()
self.properties['Bytes per Sector'] = struct.unpack_from('<H', self.boot_sector, 0x0B)[0]
self.properties['Sectors per Cluster'] = struct.unpack_from('<B', self.boot_sector, 0x0D)[0]
self.properties['Reserved Sectors'] = struct.unpack_from('<H', self.boot_sector, 0x0E)[0]
self.properties['Number of FATs'] = struct.unpack_from('<B', self.boot_sector, 0x10)[0]
self.properties['Root Directory Entries'] = struct.unpack_from('<H', self.boot_sector, 0x11)[0]
self.properties['Total Sectors'] = struct.unpack_from('<H', self.boot_sector, 0x13)[0]
self.properties['Media Descriptor'] = struct.unpack_from('<B', self.boot_sector, 0x15)[0]
self.properties['Sectors per FAT'] = struct.unpack_from('<H', self.boot_sector, 0x16)[0]
self.properties['Sectors per Track'] = struct.unpack_from('<H', self.boot_sector, 0x18)[0]
self.properties['Number of Heads'] = struct.unpack_from('<H', self.boot_sector, 0x1A)[0]
self.properties['Hidden Sectors'] = struct.unpack_from('<I', self.boot_sector, 0x1C)[0]
self.properties['Large Total Sectors'] = struct.unpack_from('<I', self.boot_sector, 0x20)[0]
self.is_fat32 = self.properties['Sectors per FAT'] == 0
offset_drive = 0x40 if self.is_fat32 else 0x24
offset_reserved = offset_drive + 1
offset_sig = offset_reserved + 1
offset_vol_id = offset_sig + 1
offset_vol_label = offset_vol_id + 4
offset_fs_type = offset_vol_label + 11
self.properties['Physical Drive Number'] = struct.unpack_from('<B', self.boot_sector, offset_drive)[0]
self.properties['Reserved'] = struct.unpack_from('<B', self.boot_sector, offset_reserved)[0]
self.properties['Extended Boot Signature'] = struct.unpack_from('<B', self.boot_sector, offset_sig)[0]
self.properties['Volume ID'] = struct.unpack_from('<I', self.boot_sector, offset_vol_id)[0]
self.properties['Volume Label'] = self.boot_sector[offset_vol_label:offset_vol_label+11].decode('ascii').rstrip()
self.properties['File System Type'] = self.boot_sector[offset_fs_type:offset_fs_type+8].decode('ascii').rstrip()
if self.is_fat32:
self.properties['Sectors per FAT Large'] = struct.unpack_from('<I', self.boot_sector, 0x24)[0]
self.properties['FAT Flags'] = struct.unpack_from('<H', self.boot_sector, 0x28)[0]
self.properties['Version'] = struct.unpack_from('<H', self.boot_sector, 0x2A)[0]
self.properties['Root Cluster'] = struct.unpack_from('<I', self.boot_sector, 0x2C)[0]
self.properties['FS Info Sector'] = struct.unpack_from('<H', self.boot_sector, 0x30)[0]
self.properties['Backup Boot Sector'] = struct.unpack_from('<H', self.boot_sector, 0x32)[0]
self.properties['Boot Sector Signature'] = struct.unpack_from('<H', self.boot_sector, 0x1FE)[0]
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def set_property(self, key, value):
if key in self.properties:
self.properties[key] = value
self.encode()
else:
raise KeyError("Invalid property")
def encode(self):
# Re-pack the boot sector based on properties (simplified, assumes no change in FAT type)
boot = bytearray(self.boot_sector)
struct.pack_into('<BBB', boot, 0, *self.properties['Jump Instruction'] if isinstance(self.properties['Jump Instruction'], tuple) else self.properties['Jump Instruction'])
boot[3:11] = self.properties['OEM Name'].ljust(8).encode('ascii')
struct.pack_into('<H', boot, 0x0B, self.properties['Bytes per Sector'])
struct.pack_into('<B', boot, 0x0D, self.properties['Sectors per Cluster'])
struct.pack_into('<H', boot, 0x0E, self.properties['Reserved Sectors'])
struct.pack_into('<B', boot, 0x10, self.properties['Number of FATs'])
struct.pack_into('<H', boot, 0x11, self.properties['Root Directory Entries'])
struct.pack_into('<H', boot, 0x13, self.properties['Total Sectors'])
struct.pack_into('<B', boot, 0x15, self.properties['Media Descriptor'])
struct.pack_into('<H', boot, 0x16, self.properties['Sectors per FAT'])
struct.pack_into('<H', boot, 0x18, self.properties['Sectors per Track'])
struct.pack_into('<H', boot, 0x1A, self.properties['Number of Heads'])
struct.pack_into('<I', boot, 0x1C, self.properties['Hidden Sectors'])
struct.pack_into('<I', boot, 0x20, self.properties['Large Total Sectors'])
offset_drive = 0x40 if self.is_fat32 else 0x24
offset_reserved = offset_drive + 1
offset_sig = offset_reserved + 1
offset_vol_id = offset_sig + 1
offset_vol_label = offset_vol_id + 4
offset_fs_type = offset_vol_label + 11
struct.pack_into('<B', boot, offset_drive, self.properties['Physical Drive Number'])
struct.pack_into('<B', boot, offset_reserved, self.properties['Reserved'])
struct.pack_into('<B', boot, offset_sig, self.properties['Extended Boot Signature'])
struct.pack_into('<I', boot, offset_vol_id, self.properties['Volume ID'])
boot[offset_vol_label:offset_vol_label+11] = self.properties['Volume Label'].ljust(11).encode('ascii')
boot[offset_fs_type:offset_fs_type+8] = self.properties['File System Type'].ljust(8).encode('ascii')
if self.is_fat32:
struct.pack_into('<I', boot, 0x24, self.properties['Sectors per FAT Large'])
struct.pack_into('<H', boot, 0x28, self.properties['FAT Flags'])
struct.pack_into('<H', boot, 0x2A, self.properties['Version'])
struct.pack_into('<I', boot, 0x2C, self.properties['Root Cluster'])
struct.pack_into('<H', boot, 0x30, self.properties['FS Info Sector'])
struct.pack_into('<H', boot, 0x32, self.properties['Backup Boot Sector'])
struct.pack_into('<H', boot, 0x1FE, self.properties['Boot Sector Signature'])
self.boot_sector = bytes(boot)
def write(self, new_filename=None):
filename = new_filename or self.filename
with open(filename, 'r+b') as f:
f.seek(0)
f.write(self.boot_sector)
# Example usage:
# parser = IMGParser('example.img')
# parser.print_properties()
# parser.set_property('Volume Label', 'NEWLABEL')
# parser.write()
- Java class for opening, decoding, reading, writing, and printing .IMG properties:
import java.io.RandomAccessFile;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Map;
public class IMGParser {
private String filename;
private byte[] bootSector = new byte[512];
private Map<String, Object> properties = new HashMap<>();
private boolean isFat32;
public IMGParser(String filename) {
this.filename = filename;
read();
}
private void read() {
try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
raf.readFully(bootSector);
} catch (IOException e) {
e.printStackTrace();
}
decode();
}
private void decode() {
ByteBuffer bb = ByteBuffer.wrap(bootSector).order(ByteOrder.LITTLE_ENDIAN);
properties.put("Jump Instruction", new int[]{bb.get(0) & 0xFF, bb.get(1) & 0xFF, bb.get(2) & 0xFF});
String oem = new String(bootSector, 3, 8).trim();
properties.put("OEM Name", oem);
properties.put("Bytes per Sector", bb.getShort(0x0B) & 0xFFFF);
properties.put("Sectors per Cluster", bb.get(0x0D) & 0xFF);
properties.put("Reserved Sectors", bb.getShort(0x0E) & 0xFFFF);
properties.put("Number of FATs", bb.get(0x10) & 0xFF);
properties.put("Root Directory Entries", bb.getShort(0x11) & 0xFFFF);
properties.put("Total Sectors", bb.getShort(0x13) & 0xFFFF);
properties.put("Media Descriptor", bb.get(0x15) & 0xFF);
properties.put("Sectors per FAT", bb.getShort(0x16) & 0xFFFF);
properties.put("Sectors per Track", bb.getShort(0x18) & 0xFFFF);
properties.put("Number of Heads", bb.getShort(0x1A) & 0xFFFF);
properties.put("Hidden Sectors", bb.getInt(0x1C));
properties.put("Large Total Sectors", bb.getInt(0x20));
isFat32 = (int) properties.get("Sectors per FAT") == 0;
int offsetDrive = isFat32 ? 0x40 : 0x24;
int offsetReserved = offsetDrive + 1;
int offsetSig = offsetReserved + 1;
int offsetVolId = offsetSig + 1;
int offsetVolLabel = offsetVolId + 4;
int offsetFsType = offsetVolLabel + 11;
properties.put("Physical Drive Number", bb.get(offsetDrive) & 0xFF);
properties.put("Reserved", bb.get(offsetReserved) & 0xFF);
properties.put("Extended Boot Signature", bb.get(offsetSig) & 0xFF);
properties.put("Volume ID", bb.getInt(offsetVolId));
String volLabel = new String(bootSector, offsetVolLabel, 11).trim();
properties.put("Volume Label", volLabel);
String fsType = new String(bootSector, offsetFsType, 8).trim();
properties.put("File System Type", fsType);
if (isFat32) {
properties.put("Sectors per FAT Large", bb.getInt(0x24));
properties.put("FAT Flags", bb.getShort(0x28) & 0xFFFF);
properties.put("Version", bb.getShort(0x2A) & 0xFFFF);
properties.put("Root Cluster", bb.getInt(0x2C));
properties.put("FS Info Sector", bb.getShort(0x30) & 0xFFFF);
properties.put("Backup Boot Sector", bb.getShort(0x32) & 0xFFFF);
}
properties.put("Boot Sector Signature", bb.getShort(0x1FE) & 0xFFFF);
}
public void printProperties() {
for (Map.Entry<String, Object> entry : properties.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
public void setProperty(String key, Object value) {
if (properties.containsKey(key)) {
properties.put(key, value);
encode();
} else {
throw new IllegalArgumentException("Invalid property");
}
}
private void encode() {
ByteBuffer bb = ByteBuffer.wrap(bootSector).order(ByteOrder.LITTLE_ENDIAN);
int[] jump = (int[]) properties.get("Jump Instruction");
bb.put(0, (byte) jump[0]);
bb.put(1, (byte) jump[1]);
bb.put(2, (byte) jump[2]);
String oem = (String) properties.get("OEM Name");
System.arraycopy(oem.getBytes(), 0, bootSector, 3, Math.min(8, oem.length()));
bb.putShort(0x0B, ((Integer) properties.get("Bytes per Sector")).shortValue());
bb.put(0x0D, ((Integer) properties.get("Sectors per Cluster")).byteValue());
bb.putShort(0x0E, ((Integer) properties.get("Reserved Sectors")).shortValue());
bb.put(0x10, ((Integer) properties.get("Number of FATs")).byteValue());
bb.putShort(0x11, ((Integer) properties.get("Root Directory Entries")).shortValue());
bb.putShort(0x13, ((Integer) properties.get("Total Sectors")).shortValue());
bb.put(0x15, ((Integer) properties.get("Media Descriptor")).byteValue());
bb.putShort(0x16, ((Integer) properties.get("Sectors per FAT")).shortValue());
bb.putShort(0x18, ((Integer) properties.get("Sectors per Track")).shortValue());
bb.putShort(0x1A, ((Integer) properties.get("Number of Heads")).shortValue());
bb.putInt(0x1C, (Integer) properties.get("Hidden Sectors"));
bb.putInt(0x20, (Integer) properties.get("Large Total Sectors"));
int offsetDrive = isFat32 ? 0x40 : 0x24;
int offsetReserved = offsetDrive + 1;
int offsetSig = offsetReserved + 1;
int offsetVolId = offsetSig + 1;
int offsetVolLabel = offsetVolId + 4;
int offsetFsType = offsetVolLabel + 11;
bb.put(offsetDrive, ((Integer) properties.get("Physical Drive Number")).byteValue());
bb.put(offsetReserved, ((Integer) properties.get("Reserved")).byteValue());
bb.put(offsetSig, ((Integer) properties.get("Extended Boot Signature")).byteValue());
bb.putInt(offsetVolId, (Integer) properties.get("Volume ID"));
String volLabel = (String) properties.get("Volume Label");
System.arraycopy(volLabel.getBytes(), 0, bootSector, offsetVolLabel, Math.min(11, volLabel.length()));
String fsType = (String) properties.get("File System Type");
System.arraycopy(fsType.getBytes(), 0, bootSector, offsetFsType, Math.min(8, fsType.length()));
if (isFat32) {
bb.putInt(0x24, (Integer) properties.get("Sectors per FAT Large"));
bb.putShort(0x28, ((Integer) properties.get("FAT Flags")).shortValue());
bb.putShort(0x2A, ((Integer) properties.get("Version")).shortValue());
bb.putInt(0x2C, (Integer) properties.get("Root Cluster"));
bb.putShort(0x30, ((Integer) properties.get("FS Info Sector")).shortValue());
bb.putShort(0x32, ((Integer) properties.get("Backup Boot Sector")).shortValue());
}
bb.putShort(0x1FE, ((Integer) properties.get("Boot Sector Signature")).shortValue());
}
public void write(String newFilename) throws IOException {
String file = newFilename != null ? newFilename : filename;
try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
raf.seek(0);
raf.write(bootSector);
}
}
// Example usage:
// public static void main(String[] args) {
// IMGParser parser = new IMGParser("example.img");
// parser.printProperties();
// parser.setProperty("Volume Label", "NEWLABEL");
// parser.write(null);
// }
}
- JavaScript class for opening, decoding, reading, writing, and printing .IMG properties (browser-compatible, using FileReader for read, Blob for write):
class IMGParser {
constructor(file) {
this.file = file;
this.bootSector = null;
this.properties = {};
this.isFat32 = false;
this.read().then(() => this.decode());
}
async read() {
const reader = new FileReader();
reader.readAsArrayBuffer(this.file.slice(0, 512));
return new Promise((resolve, reject) => {
reader.onload = () => {
this.bootSector = reader.result;
resolve();
};
reader.onerror = reject;
});
}
decode() {
const dv = new DataView(this.bootSector);
this.properties['Jump Instruction'] = [dv.getUint8(0), dv.getUint8(1), dv.getUint8(2)];
let oem = '';
for (let i = 0; i < 8; i++) oem += String.fromCharCode(dv.getUint8(3 + i));
this.properties['OEM Name'] = oem.trim();
this.properties['Bytes per Sector'] = dv.getUint16(0x0B, true);
this.properties['Sectors per Cluster'] = dv.getUint8(0x0D);
this.properties['Reserved Sectors'] = dv.getUint16(0x0E, true);
this.properties['Number of FATs'] = dv.getUint8(0x10);
this.properties['Root Directory Entries'] = dv.getUint16(0x11, true);
this.properties['Total Sectors'] = dv.getUint16(0x13, true);
this.properties['Media Descriptor'] = dv.getUint8(0x15);
this.properties['Sectors per FAT'] = dv.getUint16(0x16, true);
this.properties['Sectors per Track'] = dv.getUint16(0x18, true);
this.properties['Number of Heads'] = dv.getUint16(0x1A, true);
this.properties['Hidden Sectors'] = dv.getUint32(0x1C, true);
this.properties['Large Total Sectors'] = dv.getUint32(0x20, true);
this.isFat32 = this.properties['Sectors per FAT'] === 0;
const offsetDrive = this.isFat32 ? 0x40 : 0x24;
const offsetReserved = offsetDrive + 1;
const offsetSig = offsetReserved + 1;
const offsetVolId = offsetSig + 1;
const offsetVolLabel = offsetVolId + 4;
const offsetFsType = offsetVolLabel + 11;
this.properties['Physical Drive Number'] = dv.getUint8(offsetDrive);
this.properties['Reserved'] = dv.getUint8(offsetReserved);
this.properties['Extended Boot Signature'] = dv.getUint8(offsetSig);
this.properties['Volume ID'] = dv.getUint32(offsetVolId, true);
let volLabel = '';
for (let i = 0; i < 11; i++) volLabel += String.fromCharCode(dv.getUint8(offsetVolLabel + i));
this.properties['Volume Label'] = volLabel.trim();
let fsType = '';
for (let i = 0; i < 8; i++) fsType += String.fromCharCode(dv.getUint8(offsetFsType + i));
this.properties['File System Type'] = fsType.trim();
if (this.isFat32) {
this.properties['Sectors per FAT Large'] = dv.getUint32(0x24, true);
this.properties['FAT Flags'] = dv.getUint16(0x28, true);
this.properties['Version'] = dv.getUint16(0x2A, true);
this.properties['Root Cluster'] = dv.getUint32(0x2C, true);
this.properties['FS Info Sector'] = dv.getUint16(0x30, true);
this.properties['Backup Boot Sector'] = dv.getUint16(0x32, true);
}
this.properties['Boot Sector Signature'] = dv.getUint16(0x1FE, true);
}
printProperties() {
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
}
setProperty(key, value) {
if (key in this.properties) {
this.properties[key] = value;
this.encode();
} else {
throw new Error('Invalid property');
}
}
encode() {
const dv = new DataView(this.bootSector);
const jump = this.properties['Jump Instruction'];
dv.setUint8(0, jump[0]);
dv.setUint8(1, jump[1]);
dv.setUint8(2, jump[2]);
const oem = this.properties['OEM Name'].padEnd(8);
for (let i = 0; i < 8; i++) dv.setUint8(3 + i, oem.charCodeAt(i));
dv.setUint16(0x0B, this.properties['Bytes per Sector'], true);
dv.setUint8(0x0D, this.properties['Sectors per Cluster']);
dv.setUint16(0x0E, this.properties['Reserved Sectors'], true);
dv.setUint8(0x10, this.properties['Number of FATs']);
dv.setUint16(0x11, this.properties['Root Directory Entries'], true);
dv.setUint16(0x13, this.properties['Total Sectors'], true);
dv.setUint8(0x15, this.properties['Media Descriptor']);
dv.setUint16(0x16, this.properties['Sectors per FAT'], true);
dv.setUint16(0x18, this.properties['Sectors per Track'], true);
dv.setUint16(0x1A, this.properties['Number of Heads'], true);
dv.setUint32(0x1C, this.properties['Hidden Sectors'], true);
dv.setUint32(0x20, this.properties['Large Total Sectors'], true);
const offsetDrive = this.isFat32 ? 0x40 : 0x24;
const offsetReserved = offsetDrive + 1;
const offsetSig = offsetReserved + 1;
const offsetVolId = offsetSig + 1;
const offsetVolLabel = offsetVolId + 4;
const offsetFsType = offsetVolLabel + 11;
dv.setUint8(offsetDrive, this.properties['Physical Drive Number']);
dv.setUint8(offsetReserved, this.properties['Reserved']);
dv.setUint8(offsetSig, this.properties['Extended Boot Signature']);
dv.setUint32(offsetVolId, this.properties['Volume ID'], true);
const volLabel = this.properties['Volume Label'].padEnd(11);
for (let i = 0; i < 11; i++) dv.setUint8(offsetVolLabel + i, volLabel.charCodeAt(i));
const fsType = this.properties['File System Type'].padEnd(8);
for (let i = 0; i < 8; i++) dv.setUint8(offsetFsType + i, fsType.charCodeAt(i));
if (this.isFat32) {
dv.setUint32(0x24, this.properties['Sectors per FAT Large'], true);
dv.setUint16(0x28, this.properties['FAT Flags'], true);
dv.setUint16(0x2A, this.properties['Version'], true);
dv.setUint32(0x2C, this.properties['Root Cluster'], true);
dv.setUint16(0x30, this.properties['FS Info Sector'], true);
dv.setUint16(0x32, this.properties['Backup Boot Sector'], true);
}
dv.setUint16(0x1FE, this.properties['Boot Sector Signature'], true);
}
async write(newFilename) {
const blob = new Blob([this.bootSector], {type: 'application/octet-stream'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = newFilename || this.file.name;
a.click();
URL.revokeObjectURL(url);
}
}
// Example usage:
// const input = document.getElementById('file-input');
// input.addEventListener('change', (e) => {
// const file = e.target.files[0];
// const parser = new IMGParser(file);
// parser.printProperties();
// parser.setProperty('Volume Label', 'NEWLABEL');
// parser.write();
// });
- C class (using C++ for class support) for opening, decoding, reading, writing, and printing .IMG properties:
#include <fstream>
#include <iostream>
#include <cstring>
#include <cstdint>
#include <vector>
class IMGParser {
private:
std::string filename;
uint8_t boot_sector[512];
std::unordered_map<std::string, std::vector<uint64_t>> properties; // Use vector for multi-byte or int values
bool is_fat32;
public:
IMGParser(const std::string& fn) : filename(fn), is_fat32(false) {
read();
}
void read() {
std::ifstream file(filename, std::ios::binary);
if (file) {
file.read(reinterpret_cast<char*>(boot_sector), 512);
file.close();
decode();
}
}
void decode() {
properties["Jump Instruction"] = {boot_sector[0], boot_sector[1], boot_sector[2]};
char oem[9];
memcpy(oem, boot_sector + 3, 8);
oem[8] = '\0';
properties["OEM Name"] = {reinterpret_cast<uint64_t>(strdup(oem))}; // Store as string pointer cast
properties["Bytes per Sector"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x0B)};
properties["Sectors per Cluster"] = {boot_sector[0x0D]};
properties["Reserved Sectors"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x0E)};
properties["Number of FATs"] = {boot_sector[0x10]};
properties["Root Directory Entries"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x11)};
properties["Total Sectors"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x13)};
properties["Media Descriptor"] = {boot_sector[0x15]};
properties["Sectors per FAT"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x16)};
properties["Sectors per Track"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x18)};
properties["Number of Heads"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x1A)};
properties["Hidden Sectors"] = {*reinterpret_cast<uint32_t*>(boot_sector + 0x1C)};
properties["Large Total Sectors"] = {*reinterpret_cast<uint32_t*>(boot_sector + 0x20)};
is_fat32 = properties["Sectors per FAT"][0] == 0;
int offset_drive = is_fat32 ? 0x40 : 0x24;
int offset_reserved = offset_drive + 1;
int offset_sig = offset_reserved + 1;
int offset_vol_id = offset_sig + 1;
int offset_vol_label = offset_vol_id + 4;
int offset_fs_type = offset_vol_label + 11;
properties["Physical Drive Number"] = {boot_sector[offset_drive]};
properties["Reserved"] = {boot_sector[offset_reserved]};
properties["Extended Boot Signature"] = {boot_sector[offset_sig]};
properties["Volume ID"] = {*reinterpret_cast<uint32_t*>(boot_sector + offset_vol_id)};
char vol_label[12];
memcpy(vol_label, boot_sector + offset_vol_label, 11);
vol_label[11] = '\0';
properties["Volume Label"] = {reinterpret_cast<uint64_t>(strdup(vol_label))};
char fs_type[9];
memcpy(fs_type, boot_sector + offset_fs_type, 8);
fs_type[8] = '\0';
properties["File System Type"] = {reinterpret_cast<uint64_t>(strdup(fs_type))};
if (is_fat32) {
properties["Sectors per FAT Large"] = {*reinterpret_cast<uint32_t*>(boot_sector + 0x24)};
properties["FAT Flags"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x28)};
properties["Version"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x2A)};
properties["Root Cluster"] = {*reinterpret_cast<uint32_t*>(boot_sector + 0x2C)};
properties["FS Info Sector"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x30)};
properties["Backup Boot Sector"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x32)};
}
properties["Boot Sector Signature"] = {*reinterpret_cast<uint16_t*>(boot_sector + 0x1FE)};
}
void print_properties() {
for (const auto& p : properties) {
std::cout << p.first << ": ";
if (p.first == "OEM Name" || p.first == "Volume Label" || p.first == "File System Type") {
std::cout << reinterpret_cast<char*>(p.second[0]);
} else if (p.first == "Jump Instruction") {
std::cout << p.second[0] << " " << p.second[1] << " " << p.second[2];
} else {
std::cout << p.second[0];
}
std::cout << std::endl;
}
}
void set_property(const std::string& key, const std::vector<uint64_t>& value) {
if (properties.count(key)) {
properties[key] = value;
encode();
}
}
void encode() {
*reinterpret_cast<uint16_t*>(boot_sector + 0x0B) = properties["Bytes per Sector"][0];
boot_sector[0x0D] = properties["Sectors per Cluster"][0];
*reinterpret_cast<uint16_t*>(boot_sector + 0x0E) = properties["Reserved Sectors"][0];
boot_sector[0x10] = properties["Number of FATs"][0];
*reinterpret_cast<uint16_t*>(boot_sector + 0x11) = properties["Root Directory Entries"][0];
*reinterpret_cast<uint16_t*>(boot_sector + 0x13) = properties["Total Sectors"][0];
boot_sector[0x15] = properties["Media Descriptor"][0];
*reinterpret_cast<uint16_t*>(boot_sector + 0x16) = properties["Sectors per FAT"][0];
*reinterpret_cast<uint16_t*>(boot_sector + 0x18) = properties["Sectors per Track"][0];
*reinterpret_cast<uint16_t*>(boot_sector + 0x1A) = properties["Number of Heads"][0];
*reinterpret_cast<uint32_t*>(boot_sector + 0x1C) = properties["Hidden Sectors"][0];
*reinterpret_cast<uint32_t*>(boot_sector + 0x20) = properties["Large Total Sectors"][0];
// Similar for other fields...
// For strings, memcpy
memcpy(boot_sector + 3, reinterpret_cast<const char*>(properties["OEM Name"][0]), 8);
int offset_vol_label = (is_fat32 ? 0x47 : 0x2B);
memcpy(boot_sector + offset_vol_label, reinterpret_cast<const char*>(properties["Volume Label"][0]), 11);
int offset_fs_type = (is_fat32 ? 0x52 : 0x36);
memcpy(boot_sector + offset_fs_type, reinterpret_cast<const char*>(properties["File System Type"][0]), 8);
// Jump
auto jump = properties["Jump Instruction"];
boot_sector[0] = jump[0];
boot_sector[1] = jump[1];
boot_sector[2] = jump[2];
*reinterpret_cast<uint16_t*>(boot_sector + 0x1FE) = properties["Boot Sector Signature"][0];
// Add similar for FAT32 fields if needed
}
void write(const std::string& new_filename = "") {
std::string fn = new_filename.empty() ? filename : new_filename;
std::ofstream file(fn, std::ios::binary | std::ios::in);
if (file) {
file.write(reinterpret_cast<char*>(boot_sector), 512);
file.close();
}
}
};
// Example usage:
// int main() {
// IMGParser parser("example.img");
// parser.print_properties();
// parser.set_property("Volume Label", {reinterpret_cast<uint64_t>(strdup("NEWLABEL"))});
// parser.write();
// return 0;
// }