static int dir_sorter(AnnotatedFSO a, AnnotatedFSO b) { var fname_len = Math.Max(a.FName.Length, b.FName.Length); var afn = a.FName.PadRight(fname_len); var bfn = b.FName.PadRight(fname_len); var fn_cmp = string.CompareOrdinal(afn, bfn); if (fn_cmp != 0) { return(fn_cmp); } var ext_len = Math.Max(a.Ext == null ? 0 : a.Ext.Length, b.Ext == null ? 0 : b.Ext.Length); var aext = (a.Ext ?? "").PadRight(ext_len); var bext = (b.Ext ?? "").PadRight(ext_len); return(string.CompareOrdinal(aext, bext)); }
private static AnnotatedFSO doscache_add(FileSystemInfo d, AnnotatedFSO parent, Dictionary <string, List <AnnotatedFSO> > doscache, bool is_root = false) { var dosname = build_dosname(d, is_root, out var dn2); if (!doscache.TryGetValue(dn2, out var dcl)) { dcl = new List <AnnotatedFSO>(); doscache[dn2] = dcl; } var new_afso = new AnnotatedFSO(); new_afso.fsi = d; new_afso.Parent = parent; new_afso.FName = dosname[0]; new_afso.Ext = dosname[1]; dcl.Add(new_afso); var di = d as DirectoryInfo; if (di != null) { foreach (var c in di.GetFiles()) { new_afso.Children.Add(doscache_add(c, new_afso, doscache)); } foreach (var c in di.GetDirectories()) { new_afso.Children.Add(doscache_add(c, new_afso, doscache)); } if (Program.boot_catalog_d != null && Program.boot_catalog_d.Equals(di.FullName)) { new_afso.Children.Add(doscache_add(new BootCatalogFileInfo(), new_afso, doscache)); } } return(new_afso); }
private static void build_path_table(AnnotatedFSO afso, List <byte> ret, bool lsb) { var id_len = afso.Identifier.Length; var pt_len = align(8 + id_len, 2); int cur_idx = ret.Count; ret.Add((byte)id_len); ret.Add(0); ret.AddRange(ToByteArray(afso.lba, 4, lsb)); ret.AddRange(ToByteArray(afso.Parent == null ? 1 : afso.Parent.dir_idx, 2, lsb)); foreach (var c in afso.Identifier) { ret.Add((byte)c); } while (ret.Count < (cur_idx + pt_len)) { ret.Add(0); } }
private static void build_dir_tree(AnnotatedFSO cur_afso, List <byte> dir_tree, int base_lba, Dictionary <int, AnnotatedFSO> parent_dir_map) { int cur_lba = base_lba + dir_tree.Count / 2048; cur_afso.lba = cur_lba; int cur_count = dir_tree.Count; // Add "." and ".." entries, store their location for future patching up parent_dir_map[dir_tree.Count] = cur_afso; var b1 = build_dir_entry("\0", 0, 0, null, cur_afso.Parent == null); add_dir_entry(b1, dir_tree); parent_dir_map[dir_tree.Count] = cur_afso.Parent ?? cur_afso; var b2 = build_dir_entry("\u0001", 0, 0); add_dir_entry(b2, dir_tree); cur_afso.Children.Sort(dir_sorter); foreach (var c in cur_afso.Children) { if (c.fsi is FileInfo) { add_dir_entry(build_dir_entry(c.Identifier, c.lba, (int)((FileInfo)c.fsi).Length, c.fsi), dir_tree); } else { add_dir_entry(build_dir_entry(c.Identifier, c.lba, c.len, c.fsi), dir_tree); } } cur_afso.len = (int)align(dir_tree.Count - cur_count); align(dir_tree); }
static int sort_func(AnnotatedFSO a, AnnotatedFSO b) { if (a.Parent == null && b.Parent == null) { return(0); } else if (a.Parent == null) { return(-1); } else if (b.Parent == null) { return(1); } int pcompare = sort_func(a.Parent, b.Parent); if (pcompare != 0) { return(pcompare); } return(a.Identifier.CompareTo(b.Identifier)); }
static void Main(string[] args) { if (!parse_args(args)) { show_usage(); return; } var d = new System.IO.DirectoryInfo(src_dir); var o = new System.IO.BinaryWriter(new System.IO.FileStream(ofname, System.IO.FileMode.Create)); // Write 32 kiB of zeros to the system area o.Write(new byte[32 * 1024]); List <VolumeDescriptor> voldescs = new List <VolumeDescriptor>(); // Add a primary volume descriptor var pvd = new PrimaryVolumeDescriptor(); voldescs.Add(pvd); ElToritoBootDescriptor bvd = null; if (bootEntries.Count > 0) { bvd = new ElToritoBootDescriptor(); voldescs.Add(bvd); } voldescs.Add(new VolumeDescriptorSetTerminator()); // Generate directory tree List <AnnotatedFSO> files, dirs; var afso = AnnotatedFSO.BuildAFSOTree(d, out dirs, out files); // Allocate space for files + directories int cur_lba = 0x10 + voldescs.Count; List <AnnotatedFSO> output_order = new List <AnnotatedFSO>(); AnnotatedFSO afso_bc = null; foreach (var file in files) { if (file.fsi is BootCatalogFileInfo) { afso_bc = file; continue; } var fi = file.fsi as FileInfo; var l = align(fi.Length); var lbal = l / 2048; file.lba = cur_lba; file.len = (int)fi.Length; cur_lba += (int)lbal; output_order.Add(file); foreach (var bfe in bootEntries) { if (bfe.ffname != null && bfe.boot_table && bfe.ffname == fi.FullName) { file.needs_boot_table = true; bfe.afso_boot_file = file; } } } // create boot catalog List <byte> bc = new List <byte>(); var bc_lba = cur_lba; if (bootEntries.Count > 0) { // Validation entry List <byte> val_ent = new List <byte>(); val_ent.Add(0x01); if (bootEntries[0].type == BootEntry.BootType.BIOS) { val_ent.Add(0); } else { val_ent.Add(0xef); } val_ent.Add(0); val_ent.Add(0); //val_ent.AddRange(Encoding.ASCII.GetBytes("isomake".PadRight(24))); for (int i = 0; i < 24; i++) { val_ent.Add(0); } var cs = elt_checksum(val_ent, 0xaa55); val_ent.Add((byte)(cs & 0xff)); val_ent.Add((byte)((cs >> 8) & 0xff)); val_ent.Add(0x55); val_ent.Add(0xaa); bc.AddRange(val_ent); // default entry List <byte> def_ent = new List <byte>(); if (bootEntries[0].bootable) { def_ent.Add(0x88); } else { def_ent.Add(0x00); } switch (bootEntries[0].etype) { case BootEntry.EmulType.Floppy: def_ent.Add(0x02); break; case BootEntry.EmulType.Hard: def_ent.Add(0x04); break; case BootEntry.EmulType.NoEmul: def_ent.Add(0x00); break; default: throw new NotSupportedException(); } def_ent.AddRange(BitConverter.GetBytes((ushort)bootEntries[0].load_seg)); def_ent.Add(0x0); def_ent.Add(0x0); def_ent.AddRange(BitConverter.GetBytes((ushort)bootEntries[0].sector_count)); if (bootEntries[0].afso_boot_file != null) { def_ent.AddRange(BitConverter.GetBytes(bootEntries[0].afso_boot_file.lba)); } else { def_ent.AddRange(BitConverter.GetBytes((int)0)); } for (int i = 0; i < 20; i++) { def_ent.Add(0); } bc.AddRange(def_ent); for (int idx = 1; idx < bootEntries.Count; idx++) { // section header List <byte> sh = new List <byte>(); if (idx == bootEntries.Count - 1) { sh.Add(0x91); } else { sh.Add(0x90); } if (bootEntries[idx].type == BootEntry.BootType.BIOS) { sh.Add(0x0); } else { sh.Add(0xef); } sh.AddRange(BitConverter.GetBytes((ushort)1)); for (int i = 0; i < 28; i++) { sh.Add(0); } //sh.AddRange(Encoding.ASCII.GetBytes(bootEntries[idx].type.ToString().PadRight(28))); bc.AddRange(sh); // section entry List <byte> se = new List <byte>(); if (bootEntries[idx].bootable) { se.Add(0x88); } else { se.Add(0x00); } switch (bootEntries[idx].etype) { case BootEntry.EmulType.Floppy: se.Add(0x02); break; case BootEntry.EmulType.Hard: se.Add(0x04); break; case BootEntry.EmulType.NoEmul: se.Add(0x00); break; default: throw new NotSupportedException(); } se.AddRange(BitConverter.GetBytes((ushort)bootEntries[idx].load_seg)); se.Add(0); se.Add(0); se.AddRange(BitConverter.GetBytes((ushort)bootEntries[idx].sector_count)); if (bootEntries[idx].afso_boot_file != null) { se.AddRange(BitConverter.GetBytes((int)bootEntries[idx].afso_boot_file.lba)); } else { se.AddRange(BitConverter.GetBytes((int)0)); } se.Add(0); for (int i = 0; i < 19; i++) { se.Add(0); } bc.AddRange(se); } afso_bc.lba = bc_lba; afso_bc.len = bc.Count; cur_lba += (int)align(bc.Count) / 2048; } // create root dir first entry continuation area containing rockridge attribute if required rr_ce = new List <byte>(); if (do_rr) { // Use a spare sector to contain the 'ER' field as is it too big for the system use area // This is later referenced in a 'CE' entry rr_ce_lba = cur_lba++; // Add SUSP ER field to identify RR rr_ce.Add(0x45); rr_ce.Add(0x52); // "ER" var ext_id = "IEEE_P1282"; var ext_desc = "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS."; var ext_src = "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION."; rr_ce.Add((byte)(8 + ext_id.Length + ext_desc.Length + ext_src.Length)); // length rr_ce.Add(1); // version rr_ce.Add((byte)ext_id.Length); // LEN_ID rr_ce.Add((byte)ext_desc.Length); // LEN_DES rr_ce.Add((byte)ext_src.Length); // LEN_SRC rr_ce.Add(1); // EXT_VER rr_ce.AddRange(Encoding.ASCII.GetBytes(ext_id)); rr_ce.AddRange(Encoding.ASCII.GetBytes(ext_desc)); rr_ce.AddRange(Encoding.ASCII.GetBytes(ext_src)); } // build directory tree from most deeply nested to most shallow, so that we automatically patch up // the appropriate child lbas as we go List <byte> dir_tree = new List <byte>(); var dt_lba = cur_lba; Dictionary <int, AnnotatedFSO> parent_dir_map = new Dictionary <int, AnnotatedFSO>(); for (int i = 7; i >= 0; i--) { var afso_level = afso[i]; foreach (var cur_afso in afso_level) { build_dir_tree(cur_afso, dir_tree, dt_lba, parent_dir_map); } } // patch up parent lbas foreach (var kvp in parent_dir_map) { var lba = kvp.Value.lba; var len = kvp.Value.len; var lba_b = int_lsb_msb(lba); var len_b = int_lsb_msb(len); insert_bytes(lba_b, kvp.Key + 2, dir_tree); insert_bytes(len_b, kvp.Key + 10, dir_tree); } // And root directory entry var root_entry = build_dir_entry("\0", afso[0][0].lba, afso[0][0].len, d); cur_lba += dir_tree.Count / 2048; // create path table var path_table_l_lba = cur_lba; byte[] path_table_l = build_path_table(afso, true); var path_table_b_lba = path_table_l_lba + (int)align(path_table_l.Length) / 2048; byte[] path_table_b = build_path_table(afso, false); cur_lba = path_table_b_lba + (int)align(path_table_b.Length) / 2048; // Set pvd entries pvd.WriteString("VolumeIdentifier", "UNNAMED"); pvd.WriteInt("VolumeSpaceSize", cur_lba); pvd.WriteInt("VolumeSetSize", 1); pvd.WriteInt("VolumeSequenceNumber", 1); pvd.WriteInt("LogicalBlockSize", 2048); pvd.WriteInt("PathTableSize", path_table_l.Length); pvd.WriteInt("LocTypeLPathTable", path_table_l_lba); pvd.WriteInt("LocTypeMPathTable", path_table_b_lba); pvd.WriteBytes("RootDir", root_entry); if (bootEntries.Count > 0) { bvd.BootCatalogAddress = bc_lba; } // Write out volume descriptors foreach (var vd in voldescs) { vd.Write(o); } // files foreach (var f in output_order) { o.Seek(f.lba * 2048, SeekOrigin.Begin); var fin = new BinaryReader(((FileInfo)f.fsi).OpenRead()); var b = fin.ReadBytes(f.len); if (f.needs_boot_table) { // patch in the eltorito boot info table to offset 8 // first get 32 bit checksum from offset 64 onwards uint csum = elt_checksum32(b, 64); insert_bytes(BitConverter.GetBytes((int)0x10), 8, b); insert_bytes(BitConverter.GetBytes((int)f.lba), 12, b); insert_bytes(BitConverter.GetBytes((int)f.len), 16, b); insert_bytes(BitConverter.GetBytes(csum), 20, b); } o.Write(b, 0, f.len); } // directory records o.Seek(dt_lba * 2048, SeekOrigin.Begin); o.Write(dir_tree.ToArray(), 0, dir_tree.Count); // path tables o.Seek(path_table_l_lba * 2048, SeekOrigin.Begin); o.Write(path_table_l, 0, path_table_l.Length); o.Seek(path_table_b_lba * 2048, SeekOrigin.Begin); o.Write(path_table_b, 0, path_table_b.Length); // boot catalog if (bootEntries.Count > 0) { o.Seek(bc_lba * 2048, SeekOrigin.Begin); o.Write(bc.ToArray(), 0, bc.Count); } // rr es field continuation area if (rr_ce != null) { o.Seek(rr_ce_lba * 2048, SeekOrigin.Begin); o.Write(rr_ce.ToArray(), 0, rr_ce.Count); } // Align to sector size o.Seek(0, SeekOrigin.End); while ((o.BaseStream.Position % 2048) != 0) { o.Write((byte)0); } // Add 300k padding - required for VirtualBox it seems // According to 'man xorriso': // "This is a traditional remedy for a traditional bug in block device read drivers." for (int i = 0; i < 300 * 1024 / 4; i++) { o.Write((int)0); } o.Close(); }