Task 302: .ICN File Format
Task 302: .ICN File Format
1. List of all the properties of this file format intrinsic to its file system
Based on the ICNS file format specification, the intrinsic properties include the following structural and format-specific attributes:
- File Extension: .icns
- MIME Type: image/x-icns
- Developer: Apple Inc.
- Magic Number: 'icns' (hex: 69 63 6e 73)
- Endianness: Big-endian (most significant byte first) for length fields
- File Structure: Container format consisting of an 8-byte header followed by one or more icon entries (blocks). Each entry has its own header and data.
- Header Details:
- Bytes 0-3: Magic number ('icns')
- Bytes 4-7: Total file length (4 bytes, big-endian, including header)
- Icon Entry Structure:
- Bytes 0-3: OSType (4-byte identifier for icon type, e.g., 'icp4', 'ICON')
- Bytes 4-7: Entry length (4 bytes, big-endian, including the 8-byte entry header)
- Remaining bytes: Icon data (entry length - 8 bytes)
- Supported OSTypes (Icon Types) and Associated Properties:
- 'ICON': 32x32, 1-bit mono icon, uncompressed or RLE
- 'ICN#': 32x32, 1-bit mono icon with 1-bit mask, uncompressed or RLE
- 'icm#': 16x12, 1-bit mono icon with 1-bit mask, uncompressed or RLE
- 'icm4': 16x12, 4-bit color icon, uncompressed or RLE
- 'icm8': 16x12, 8-bit color icon, uncompressed or RLE
- 'ics#': 16x16, 1-bit mono icon with 1-bit mask, uncompressed or RLE
- 'ics4': 16x16, 4-bit color icon, uncompressed or RLE
- 'ics8': 16x16, 8-bit color icon, uncompressed or RLE
- 'is32': 16x16, 24-bit RGB, RLE compressed
- 's8mk': 16x16, 8-bit alpha mask, uncompressed
- 'icl4': 32x32, 4-bit color icon, uncompressed or RLE
- 'icl8': 32x32, 8-bit color icon, uncompressed or RLE
- 'il32': 32x32, 24-bit RGB, RLE compressed
- 'l8mk': 32x32, 8-bit alpha mask, uncompressed
- 'ich#': 48x48, 1-bit mono icon with 1-bit mask, uncompressed or RLE
- 'ich4': 48x48, 4-bit color icon, uncompressed or RLE
- 'ich8': 48x48, 8-bit color icon, uncompressed or RLE
- 'ih32': 48x48, 24-bit RGB, RLE compressed
- 'h8mk': 48x48, 8-bit alpha mask, uncompressed
- 'it32': 128x128, 24-bit RGB, RLE compressed (starts with 4 zero bytes)
- 't8mk': 128x128, 8-bit alpha mask, uncompressed
- 'icp4': 16x16, PNG or JPEG 2000 or 24-bit RGB
- 'icp5': 32x32, PNG or JPEG 2000 or 24-bit RGB
- 'icp6': 48x48, PNG or JPEG 2000 or 24-bit RGB
- Supported Image Sizes: 16x12, 16x16, 32x32, 48x48, 128x128 (and higher in newer formats like 256x256, 512x512, 1024x1024 via PNG/JPEG2000 types)
- Data Formats: Uncompressed bitmap, RLE (Run-Length Encoding) for RGB channels, PNG, JPEG 2000 (JP2)
- Alpha Channels: Supported via 1-bit or 8-bit masks (separate entries like 's8mk') or embedded in PNG/JPEG2000 data (8-bit alpha)
- Optional Entries: 'TOC ' (Table of Contents summarizing contents), 'icnV' (version information)
- Other Properties: Multiple resolutions in one file for scalability; backward compatibility with older macOS versions; no fixed number of entries; file size must match the header length
2. Two direct download links for files of format .ICNS
- https://www.iconarchive.com/download/i45782/tatice/cristal-intense/Apple-multicolor.icns
- https://www.iconarchive.com/download/i45753/tatice/operating-systems/Apple-Rainbow.icns
3. Ghost blog embedded html javascript that allows a user to drag n drop a file of format .ICNS and it will dump to screen all these properties
Drag and drop an .ICNS file here
4. Python class that can open any file of format .ICNS and decode read and write and print to console all the properties from the above list
import struct
import os
class ICNSHandler:
def __init__(self, filepath):
self.filepath = filepath
self.magic = None
self.file_length = None
self.entries = [] # list of (os_type, entry_length, data)
self.read_file()
def read_file(self):
with open(self.filepath, 'rb') as f:
data = f.read()
if len(data) < 8:
raise ValueError("Invalid ICNS file")
self.magic = data[0:4].decode('ascii')
if self.magic != 'icns':
raise ValueError("Not a valid ICNS file")
self.file_length = struct.unpack('>I', data[4:8])[0]
offset = 8
while offset < self.file_length:
if offset + 8 > self.file_length:
raise ValueError("Corrupted ICNS file")
os_type = data[offset:offset+4].decode('ascii')
entry_length = struct.unpack('>I', data[offset+4:offset+8])[0]
if entry_length < 8 or offset + entry_length > self.file_length:
raise ValueError("Corrupted ICNS entry")
entry_data = data[offset+8:offset+entry_length]
self.entries.append((os_type, entry_length, entry_data))
offset += entry_length
def print_properties(self):
print("File Extension: .icns")
print("MIME Type: image/x-icns")
print("Developer: Apple Inc.")
print("Magic Number: 'icns'")
print("Endianness: Big-endian")
print("Total File Length:", self.file_length, "bytes")
print("Icon Entries:")
for os_type, entry_length, _ in self.entries:
size = self.get_size_from_ostype(os_type)
format_ = self.get_format_from_ostype(os_type)
print(f" - OSType: {os_type}, Entry Length: {entry_length} bytes, Size: {size}, Format: {format_}")
print("Supported Sizes: 16x12, 16x16, 32x32, 48x48, 128x128, etc.")
print("Data Formats: Uncompressed bitmap, RLE, PNG, JPEG 2000")
print("Alpha Channels: Supported (1-bit/8-bit masks or embedded)")
def get_size_from_ostype(self, os_type):
sizes = {
'ICON': '32x32', 'ICN#': '32x32', 'icm#': '16x12', 'icm4': '16x12', 'icm8': '16x12',
'ics#': '16x16', 'ics4': '16x16', 'ics8': '16x16', 'is32': '16x16', 's8mk': '16x16',
'icl4': '32x32', 'icl8': '32x32', 'il32': '32x32', 'l8mk': '32x32', 'ich#': '48x48',
'ich4': '48x48', 'ich8': '48x48', 'ih32': '48x48', 'h8mk': '48x48', 'it32': '128x128',
't8mk': '128x128', 'icp4': '16x16', 'icp5': '32x32', 'icp6': '48x48'
}
return sizes.get(os_type, 'Unknown')
def get_format_from_ostype(self, os_type):
formats = {
'ICON': '1-bit mono', 'ICN#': '1-bit mono with mask', 'icm#': '1-bit mono with mask',
'icm4': '4-bit color', 'icm8': '8-bit color', 'ics#': '1-bit mono with mask',
'ics4': '4-bit color', 'ics8': '8-bit color', 'is32': '24-bit RGB RLE', 's8mk': '8-bit mask',
'icl4': '4-bit color', 'icl8': '8-bit color', 'il32': '24-bit RGB RLE', 'l8mk': '8-bit mask',
'ich#': '1-bit mono with mask', 'ich4': '4-bit color', 'ich8': '8-bit color',
'ih32': '24-bit RGB RLE', 'h8mk': '8-bit mask', 'it32': '24-bit RGB RLE', 't8mk': '8-bit mask',
'icp4': 'PNG/JPEG2000/24-bit RGB', 'icp5': 'PNG/JPEG2000/24-bit RGB', 'icp6': 'PNG/JPEG2000/24-bit RGB'
}
return formats.get(os_type, 'Unknown')
def write_file(self, new_filepath=None):
if not new_filepath:
new_filepath = self.filepath
data = b'icns' + struct.pack('>I', self.file_length)
for os_type, entry_length, entry_data in self.entries:
data += os_type.encode('ascii') + struct.pack('>I', entry_length) + entry_data
with open(new_filepath, 'wb') as f:
f.write(data)
# Example usage:
# handler = ICNSHandler('path/to/file.icns')
# handler.print_properties()
# handler.write_file('path/to/new_file.icns')
5. Java class that can open any file of format .ICNS and decode read and write and print to console all the properties from the above list
import java.io.*;
import java.nio.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ICNSHandler {
private String filepath;
private String magic;
private int fileLength;
private List<IconEntry> entries = new ArrayList<>();
private static class IconEntry {
String osType;
int entryLength;
byte[] data;
IconEntry(String osType, int entryLength, byte[] data) {
this.osType = osType;
this.entryLength = entryLength;
this.data = data;
}
}
public ICNSHandler(String filepath) throws IOException {
this.filepath = filepath;
readFile();
}
private void readFile() throws IOException {
byte[] data = Files.readAllBytes(Paths.get(filepath));
ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
magic = new String(data, 0, 4);
if (!magic.equals("icns")) {
throw new IOException("Not a valid ICNS file");
}
fileLength = buffer.getInt(4);
int offset = 8;
while (offset < fileLength) {
String osType = new String(data, offset, 4);
int entryLength = buffer.getInt(offset + 4);
byte[] entryData = new byte[entryLength - 8];
System.arraycopy(data, offset + 8, entryData, 0, entryLength - 8);
entries.add(new IconEntry(osType, entryLength, entryData));
offset += entryLength;
}
}
public void printProperties() {
System.out.println("File Extension: .icns");
System.out.println("MIME Type: image/x-icns");
System.out.println("Developer: Apple Inc.");
System.out.println("Magic Number: 'icns'");
System.out.println("Endianness: Big-endian");
System.out.println("Total File Length: " + fileLength + " bytes");
System.out.println("Icon Entries:");
for (IconEntry entry : entries) {
String size = getSizeFromOSType(entry.osType);
String format = getFormatFromOSType(entry.osType);
System.out.println(" - OSType: " + entry.osType + ", Entry Length: " + entry.entryLength + " bytes, Size: " + size + ", Format: " + format);
}
System.out.println("Supported Sizes: 16x12, 16x16, 32x32, 48x48, 128x128, etc.");
System.out.println("Data Formats: Uncompressed bitmap, RLE, PNG, JPEG 2000");
System.out.println("Alpha Channels: Supported (1-bit/8-bit masks or embedded)");
}
private String getSizeFromOSType(String osType) {
Map<String, String> sizes = new HashMap<>();
sizes.put("ICON", "32x32"); sizes.put("ICN#", "32x32"); sizes.put("icm#", "16x12");
sizes.put("icm4", "16x12"); sizes.put("icm8", "16x12"); sizes.put("ics#", "16x16");
sizes.put("ics4", "16x16"); sizes.put("ics8", "16x16"); sizes.put("is32", "16x16");
sizes.put("s8mk", "16x16"); sizes.put("icl4", "32x32"); sizes.put("icl8", "32x32");
sizes.put("il32", "32x32"); sizes.put("l8mk", "32x32"); sizes.put("ich#", "48x48");
sizes.put("ich4", "48x48"); sizes.put("ich8", "48x48"); sizes.put("ih32", "48x48");
sizes.put("h8mk", "48x48"); sizes.put("it32", "128x128"); sizes.put("t8mk", "128x128");
sizes.put("icp4", "16x16"); sizes.put("icp5", "32x32"); sizes.put("icp6", "48x48");
return sizes.getOrDefault(osType, "Unknown");
}
private String getFormatFromOSType(String osType) {
Map<String, String> formats = new HashMap<>();
formats.put("ICON", "1-bit mono"); formats.put("ICN#", "1-bit mono with mask");
formats.put("icm#", "1-bit mono with mask"); formats.put("icm4", "4-bit color");
formats.put("icm8", "8-bit color"); formats.put("ics#", "1-bit mono with mask");
formats.put("ics4", "4-bit color"); formats.put("ics8", "8-bit color");
formats.put("is32", "24-bit RGB RLE"); formats.put("s8mk", "8-bit mask");
formats.put("icl4", "4-bit color"); formats.put("icl8", "8-bit color");
formats.put("il32", "24-bit RGB RLE"); formats.put("l8mk", "8-bit mask");
formats.put("ich#", "1-bit mono with mask"); formats.put("ich4", "4-bit color");
formats.put("ich8", "8-bit color"); formats.put("ih32", "24-bit RGB RLE");
formats.put("h8mk", "8-bit mask"); formats.put("it32", "24-bit RGB RLE");
formats.put("t8mk", "8-bit mask"); formats.put("icp4", "PNG/JPEG2000/24-bit RGB");
formats.put("icp5", "PNG/JPEG2000/24-bit RGB"); formats.put("icp6", "PNG/JPEG2000/24-bit RGB");
return formats.getOrDefault(osType, "Unknown");
}
public void writeFile(String newFilepath) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write("icns".getBytes());
baos.write(ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(fileLength).array());
for (IconEntry entry : entries) {
baos.write(entry.osType.getBytes());
baos.write(ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(entry.entryLength).array());
baos.write(entry.data);
}
Files.write(Paths.get(newFilepath), baos.toByteArray());
}
// Example usage:
// public static void main(String[] args) throws IOException {
// ICNSHandler handler = new ICNSHandler("path/to/file.icns");
// handler.printProperties();
// handler.writeFile("path/to/new_file.icns");
// }
}
6. Javascript class that can open any file of format .ICNS and decode read and write and print to console all the properties from the above list
const fs = require('fs');
class ICNSHandler {
constructor(filepath) {
this.filepath = filepath;
this.magic = null;
this.fileLength = 0;
this.entries = []; // array of {osType, entryLength, data (Buffer)}
this.readFile();
}
readFile() {
const data = fs.readFileSync(this.filepath);
const view = new DataView(data.buffer);
this.magic = String.fromCharCode(view.getUint8(0), view.getUint8(1), view.getUint8(2), view.getUint8(3));
if (this.magic !== 'icns') {
throw new Error('Not a valid ICNS file');
}
this.fileLength = view.getUint32(4, false); // big-endian
let offset = 8;
while (offset < this.fileLength) {
const osType = String.fromCharCode(view.getUint8(offset), view.getUint8(offset+1), view.getUint8(offset+2), view.getUint8(offset+3));
const entryLength = view.getUint32(offset + 4, false);
const entryData = data.subarray(offset + 8, offset + entryLength);
this.entries.push({osType, entryLength, data: entryData});
offset += entryLength;
}
}
printProperties() {
console.log('File Extension: .icns');
console.log('MIME Type: image/x-icns');
console.log('Developer: Apple Inc.');
console.log("Magic Number: 'icns'");
console.log('Endianness: Big-endian');
console.log('Total File Length:', this.fileLength, 'bytes');
console.log('Icon Entries:');
this.entries.forEach(entry => {
const size = this.getSizeFromOSType(entry.osType);
const format = this.getFormatFromOSType(entry.osType);
console.log(` - OSType: ${entry.osType}, Entry Length: ${entry.entryLength} bytes, Size: ${size}, Format: ${format}`);
});
console.log('Supported Sizes: 16x12, 16x16, 32x32, 48x48, 128x128, etc.');
console.log('Data Formats: Uncompressed bitmap, RLE, PNG, JPEG 2000');
console.log('Alpha Channels: Supported (1-bit/8-bit masks or embedded)');
}
getSizeFromOSType(osType) {
const sizes = {
'ICON': '32x32', 'ICN#': '32x32', 'icm#': '16x12', 'icm4': '16x12', 'icm8': '16x12',
'ics#': '16x16', 'ics4': '16x16', 'ics8': '16x16', 'is32': '16x16', 's8mk': '16x16',
'icl4': '32x32', 'icl8': '32x32', 'il32': '32x32', 'l8mk': '32x32', 'ich#': '48x48',
'ich4': '48x48', 'ich8': '48x48', 'ih32': '48x48', 'h8mk': '48x48', 'it32': '128x128',
't8mk': '128x128', 'icp4': '16x16', 'icp5': '32x32', 'icp6': '48x48'
};
return sizes[osType] || 'Unknown';
}
getFormatFromOSType(osType) {
const formats = {
'ICON': '1-bit mono', 'ICN#': '1-bit mono with mask', 'icm#': '1-bit mono with mask',
'icm4': '4-bit color', 'icm8': '8-bit color', 'ics#': '1-bit mono with mask',
'ics4': '4-bit color', 'ics8': '8-bit color', 'is32': '24-bit RGB RLE', 's8mk': '8-bit mask',
'icl4': '4-bit color', 'icl8': '8-bit color', 'il32': '24-bit RGB RLE', 'l8mk': '8-bit mask',
'ich#': '1-bit mono with mask', 'ich4': '4-bit color', 'ich8': '8-bit color',
'ih32': '24-bit RGB RLE', 'h8mk': '8-bit mask', 'it32': '24-bit RGB RLE', 't8mk': '8-bit mask',
'icp4': 'PNG/JPEG2000/24-bit RGB', 'icp5': 'PNG/JPEG2000/24-bit RGB', 'icp6': 'PNG/JPEG2000/24-bit RGB'
};
return formats[osType] || 'Unknown';
}
writeFile(newFilepath) {
const buffer = Buffer.alloc(this.fileLength);
buffer.write('icns', 0, 4);
buffer.writeUInt32BE(this.fileLength, 4);
let offset = 8;
this.entries.forEach(entry => {
buffer.write(entry.osType, offset, 4);
buffer.writeUInt32BE(entry.entryLength, offset + 4);
entry.data.copy(buffer, offset + 8);
offset += entry.entryLength;
});
fs.writeSync(newFilepath, buffer);
}
}
// Example usage:
// const handler = new ICNSHandler('path/to/file.icns');
// handler.printProperties();
// handler.writeFile('path/to/new_file.icns');
7. C class that can open any file of format .ICNS and decode read and write and print to console all the properties from the above list
(Note: Since C does not have native "classes", this is implemented in C++ for class structure.)
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstdint>
#include <endian.h> // For big-endian conversion, may need <byteswap.h> on some systems
struct IconEntry {
std::string osType;
uint32_t entryLength;
std::vector<uint8_t> data;
};
class ICNSHandler {
private:
std::string filepath;
std::string magic;
uint32_t fileLength;
std::vector<IconEntry> entries;
public:
ICNSHandler(const std::string& filepath) : filepath(filepath) {
readFile();
}
void readFile() {
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file) {
throw std::runtime_error("Could not open file");
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> data(size);
if (!file.read(reinterpret_cast<char*>(data.data()), size)) {
throw std::runtime_error("Could not read file");
}
magic = std::string(data.begin(), data.begin() + 4);
if (magic != "icns") {
throw std::runtime_error("Not a valid ICNS file");
}
fileLength = be32toh(*reinterpret_cast<uint32_t*>(&data[4]));
uint32_t offset = 8;
while (offset < fileLength) {
std::string osType(data.begin() + offset, data.begin() + offset + 4);
uint32_t entryLength = be32toh(*reinterpret_cast<uint32_t*>(&data[offset + 4]));
std::vector<uint8_t> entryData(data.begin() + offset + 8, data.begin() + offset + entryLength);
entries.push_back({osType, entryLength, entryData});
offset += entryLength;
}
}
void printProperties() const {
std::cout << "File Extension: .icns" << std::endl;
std::cout << "MIME Type: image/x-icns" << std::endl;
std::cout << "Developer: Apple Inc." << std::endl;
std::cout << "Magic Number: 'icns'" << std::endl;
std::cout << "Endianness: Big-endian" << std::endl;
std::cout << "Total File Length: " << fileLength << " bytes" << std::endl;
std::cout << "Icon Entries:" << std::endl;
for (const auto& entry : entries) {
std::string size = getSizeFromOSType(entry.osType);
std::string format = getFormatFromOSType(entry.osType);
std::cout << " - OSType: " << entry.osType << ", Entry Length: " << entry.entryLength << " bytes, Size: " << size << ", Format: " << format << std::endl;
}
std::cout << "Supported Sizes: 16x12, 16x16, 32x32, 48x48, 128x128, etc." << std::endl;
std::cout << "Data Formats: Uncompressed bitmap, RLE, PNG, JPEG 2000" << std::endl;
std::cout << "Alpha Channels: Supported (1-bit/8-bit masks or embedded)" << std::endl;
}
std::string getSizeFromOSType(const std::string& osType) const {
if (osType == "ICON" || osType == "ICN#") return "32x32";
if (osType == "icm#" || osType == "icm4" || osType == "icm8") return "16x12";
if (osType == "ics#" || osType == "ics4" || osType == "ics8" || osType == "is32" || osType == "s8mk") return "16x16";
if (osType == "icl4" || osType == "icl8" || osType == "il32" || osType == "l8mk") return "32x32";
if (osType == "ich#" || osType == "ich4" || osType == "ich8" || osType == "ih32" || osType == "h8mk") return "48x48";
if (osType == "it32" || osType == "t8mk") return "128x128";
if (osType == "icp4") return "16x16";
if (osType == "icp5") return "32x32";
if (osType == "icp6") return "48x48";
return "Unknown";
}
std::string getFormatFromOSType(const std::string& osType) const {
if (osType == "ICON") return "1-bit mono";
if (osType == "ICN#" || osType == "icm#" || osType == "ics#" || osType == "ich#") return "1-bit mono with mask";
if (osType == "icm4" || osType == "ics4" || osType == "icl4" || osType == "ich4") return "4-bit color";
if (osType == "icm8" || osType == "ics8" || osType == "icl8" || osType == "ich8") return "8-bit color";
if (osType == "is32" || osType == "il32" || osType == "ih32" || osType == "it32") return "24-bit RGB RLE";
if (osType == "s8mk" || osType == "l8mk" || osType == "h8mk" || osType == "t8mk") return "8-bit mask";
if (osType == "icp4" || osType == "icp5" || osType == "icp6") return "PNG/JPEG2000/24-bit RGB";
return "Unknown";
}
void writeFile(const std::string& newFilepath) const {
std::ofstream file(newFilepath, std::ios::binary);
if (!file) {
throw std::runtime_error("Could not open file for writing");
}
file.write(magic.c_str(), 4);
uint32_t beFileLength = htobe32(fileLength);
file.write(reinterpret_cast<const char*>(&beFileLength), 4);
for (const auto& entry : entries) {
file.write(entry.osType.c_str(), 4);
uint32_t beEntryLength = htobe32(entry.entryLength);
file.write(reinterpret_cast<const char*>(&beEntryLength), 4);
file.write(reinterpret_cast<const char*>(entry.data.data()), entry.data.size());
}
}
};
// Example usage:
// int main() {
// try {
// ICNSHandler handler("path/to/file.icns");
// handler.printProperties();
// handler.writeFile("path/to/new_file.icns");
// } catch (const std::exception& e) {
// std::cerr << e.what() << std::endl;
// }
// return 0;
// }