// Package boot animation as a zip with STORE (no compression) method // Android bootanimation.zip MUST use store method, not deflate const fs = require('fs'); const path = require('path'); const baseDir = 'C:\\Users\\PCOEM\\AeThexOS\\bootanimation'; const outFile = 'C:\\Users\\PCOEM\\AeThexOS\\bootanimation.zip'; // Collect all files to include const files = []; // desc.txt files.push({ name: 'desc.txt', path: path.join(baseDir, 'desc.txt') }); // part0 PNGs const part0 = fs.readdirSync(path.join(baseDir, 'part0')).sort(); for (const f of part0) { files.push({ name: 'part0/' + f, path: path.join(baseDir, 'part0', f) }); } // part1 PNGs const part1 = fs.readdirSync(path.join(baseDir, 'part1')).sort(); for (const f of part1) { files.push({ name: 'part1/' + f, path: path.join(baseDir, 'part1', f) }); } // Build ZIP manually with STORE method (no compression) const entries = []; let offset = 0; for (const file of files) { const data = fs.readFileSync(file.path); const nameBytes = Buffer.from(file.name, 'utf8'); // Local file header (30 bytes + name length) const localHeader = Buffer.alloc(30 + nameBytes.length); localHeader.writeUInt32LE(0x04034b50, 0); // Local file header signature localHeader.writeUInt16LE(10, 4); // Version needed (1.0) localHeader.writeUInt16LE(0, 6); // General purpose bit flag localHeader.writeUInt16LE(0, 8); // Compression method: STORE localHeader.writeUInt16LE(0, 10); // Last mod time localHeader.writeUInt16LE(0, 12); // Last mod date // CRC-32 const crc = crc32(data); localHeader.writeUInt32LE(crc, 14); localHeader.writeUInt32LE(data.length, 18); // Compressed size localHeader.writeUInt32LE(data.length, 22); // Uncompressed size localHeader.writeUInt16LE(nameBytes.length, 26); // File name length localHeader.writeUInt16LE(0, 28); // Extra field length nameBytes.copy(localHeader, 30); entries.push({ localHeaderOffset: offset, nameBytes, data, crc }); offset += localHeader.length + data.length; } // Build the full ZIP const centralDirStart = offset; const centralHeaders = []; for (const entry of entries) { const ch = Buffer.alloc(46 + entry.nameBytes.length); ch.writeUInt32LE(0x02014b50, 0); // Central directory header signature ch.writeUInt16LE(10, 4); // Version made by ch.writeUInt16LE(10, 6); // Version needed ch.writeUInt16LE(0, 8); // Flags ch.writeUInt16LE(0, 10); // Compression: STORE ch.writeUInt16LE(0, 12); // Time ch.writeUInt16LE(0, 14); // Date ch.writeUInt32LE(entry.crc, 16); // CRC ch.writeUInt32LE(entry.data.length, 20); // Compressed size ch.writeUInt32LE(entry.data.length, 24); // Uncompressed size ch.writeUInt16LE(entry.nameBytes.length, 28); // Name length ch.writeUInt16LE(0, 30); // Extra length ch.writeUInt16LE(0, 32); // Comment length ch.writeUInt16LE(0, 34); // Disk number start ch.writeUInt16LE(0, 36); // Internal file attributes ch.writeUInt32LE(0, 38); // External file attributes ch.writeUInt32LE(entry.localHeaderOffset, 42); // Relative offset of local header entry.nameBytes.copy(ch, 46); centralHeaders.push(ch); offset += ch.length; } // End of central directory const eocd = Buffer.alloc(22); eocd.writeUInt32LE(0x06054b50, 0); // EOCD signature eocd.writeUInt16LE(0, 4); // Disk number eocd.writeUInt16LE(0, 6); // Disk with central dir eocd.writeUInt16LE(entries.length, 8); // Entries on this disk eocd.writeUInt16LE(entries.length, 10); // Total entries const centralDirSize = offset - centralDirStart; eocd.writeUInt32LE(centralDirSize, 12); // Central dir size eocd.writeUInt32LE(centralDirStart, 16); // Central dir offset eocd.writeUInt16LE(0, 20); // Comment length // Write everything const fd = fs.openSync(outFile, 'w'); for (let i = 0; i < entries.length; i++) { const e = entries[i]; const localHeader = Buffer.alloc(30 + e.nameBytes.length); localHeader.writeUInt32LE(0x04034b50, 0); localHeader.writeUInt16LE(10, 4); localHeader.writeUInt16LE(0, 6); localHeader.writeUInt16LE(0, 8); localHeader.writeUInt16LE(0, 10); localHeader.writeUInt16LE(0, 12); localHeader.writeUInt32LE(e.crc, 14); localHeader.writeUInt32LE(e.data.length, 18); localHeader.writeUInt32LE(e.data.length, 22); localHeader.writeUInt16LE(e.nameBytes.length, 26); localHeader.writeUInt16LE(0, 28); e.nameBytes.copy(localHeader, 30); fs.writeSync(fd, localHeader); fs.writeSync(fd, e.data); } for (const ch of centralHeaders) { fs.writeSync(fd, ch); } fs.writeSync(fd, eocd); fs.closeSync(fd); const stat = fs.statSync(outFile); console.log(`bootanimation.zip created: ${stat.size} bytes (${files.length} files)`); // CRC-32 implementation function crc32(buf) { let crc = 0xFFFFFFFF; for (let i = 0; i < buf.length; i++) { crc ^= buf[i]; for (let j = 0; j < 8; j++) { crc = (crc >>> 1) ^ (crc & 1 ? 0xEDB88320 : 0); } } return (crc ^ 0xFFFFFFFF) >>> 0; }