MakeIsoImages.Mod 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843
  1. MODULE MakeIsoImages;
  2. (*
  3. References:
  4. ECMA-119 Volume and File Structure of CDROM for Information Interchange
  5. IEEE P1282 Rock Ridge Interchange Protocol
  6. Joliet Specification
  7. *)
  8. IMPORT SYSTEM, Files, Streams, Commands, Dates, Strings, UTF8Strings, Utils := CDRecordUtils, ATADisks, Disks;
  9. CONST
  10. MaxLen = 256;
  11. TransferSize = 10; (* Save Image *)
  12. MaxISODepth = 8;
  13. MaxISOPathLength = 255;
  14. IsoLevel1* = 0; IsoLevel2* = 1; Joliet* = 2;
  15. RelaxMaxDepth* = 0; RelaxMaxPathLength* = 1; NoVersion* = 2;
  16. SectorSize* = 2048;
  17. ISO9660Id* = "CD001";
  18. NumSystemSectors* = 16; (* number of unused sectors at the beginning *)
  19. (* volume descriptor *)
  20. Primary* = 0; Supplementary* = 1;
  21. (* pathtables *)
  22. LType = 1; RType = 2;
  23. (* File Flags *)
  24. FFHidden = 1X; FFDirectory = 2X;
  25. (* errors *)
  26. ResErr = 1;
  27. ResOk = 0;
  28. ErrNotEnoughSpace* = 2; (* not enough space on destination volume *)
  29. ErrDestinationInvalid* = 3;
  30. ErrDestinationReadOnly* = 4;
  31. ErrDirNotFound* = 5;
  32. ErrFileNotFound* = 6;
  33. ErrNoIsoImage*= 7;
  34. (* Bootable CD-ROM *)
  35. NumPartitions = 4;
  36. OfsPartitionTable = 446;
  37. ElToritoSysId = "EL TORITO SPECIFICATION";
  38. Platform80x86* = 0X;
  39. PlatformPowerPC* = 1X;
  40. PlatformMac* = 2X;
  41. Bootable = 88X;
  42. NotBootable = 00X;
  43. EmulationNone* = 0X;
  44. Emulation12Floppy* = 1X;
  45. Emulation144Floppy* = 2X;
  46. Emulation288Floppy* = 3X;
  47. EmulationHDD* = 4X;
  48. TYPE
  49. PathTableRecord = RECORD
  50. IdentLen: CHAR;
  51. AttrLen: CHAR;
  52. Lba: ARRAY 4 OF CHAR;
  53. ParentNo: ARRAY 2 OF CHAR;
  54. Ident: ARRAY MaxLen OF CHAR;
  55. END;
  56. PathTableRecordPtr = POINTER TO PathTableRecord;
  57. DirectoryRecord = RECORD
  58. Len: CHAR;
  59. AttrLen: CHAR;
  60. Lba: ARRAY 8 OF CHAR;
  61. Size: ARRAY 8 OF CHAR;
  62. Time: ARRAY 7 OF CHAR;
  63. Flags: CHAR;
  64. UnitSize: CHAR;
  65. GapSize: CHAR;
  66. VolSeqNo: ARRAY 4 OF CHAR;
  67. IdentLen: CHAR;
  68. Ident: ARRAY MaxLen OF CHAR;
  69. END;
  70. DirectoryRecordPtr = POINTER TO DirectoryRecord;
  71. (* Volume Descriptors *)
  72. VolumeDescriptor = ARRAY 2048 OF CHAR;
  73. SetTerminator = RECORD
  74. Type: CHAR;
  75. StdIdent: ARRAY 5 OF CHAR;
  76. Version: CHAR;
  77. Reserved: ARRAY 2041 OF CHAR;
  78. END;
  79. BootRecord = RECORD
  80. Type: CHAR;
  81. StdIdent: ARRAY 5 OF CHAR;
  82. Version: CHAR;
  83. BootSysIdent: ARRAY 32 OF CHAR;
  84. Unused1: ARRAY 32 OF CHAR;
  85. Lba: ARRAY 4 OF CHAR;
  86. Unuesed: ARRAY 1973 OF CHAR;
  87. END;
  88. Partition = RECORD
  89. BootIndicator: CHAR;
  90. Begin: ARRAY 3 OF CHAR;
  91. SysIndicator: CHAR;
  92. End: ARRAY 3 OF CHAR;
  93. StartSec: LONGINT;
  94. NofSecs: LONGINT;
  95. END;
  96. PartitionTable = ARRAY NumPartitions OF Partition;
  97. (* Primary / Supplementary Volume Descriptor *)
  98. PSVolumeDescriptor* = RECORD
  99. Type*: CHAR;
  100. StdIdent*: ARRAY 5 OF CHAR;
  101. Version*: CHAR;
  102. Flags*: CHAR; (* valid only for supplementary volume descriptor *)
  103. SysIdent*: ARRAY 32 OF CHAR;
  104. VolIdent*: ARRAY 32 OF CHAR;
  105. Unused1*: ARRAY 8 OF CHAR;
  106. VolSpaceSize*: ARRAY 8 OF CHAR;
  107. EscSeq*: ARRAY 32 OF CHAR; (* valid only for supplementary volume descriptor *)
  108. VolSetSize*: ARRAY 4 OF CHAR;
  109. VolSeqNo*: ARRAY 4 OF CHAR;
  110. BlockSize*: ARRAY 4 OF CHAR;
  111. PathTableSize*: ARRAY 8 OF CHAR;
  112. LocLPathTable*: ARRAY 4 OF CHAR;
  113. LocOptRPathTable*: ARRAY 4 OF CHAR;
  114. LocMPathTable*: ARRAY 4 OF CHAR;
  115. LocOptMPathTable*: ARRAY 4 OF CHAR;
  116. RootDirRecord*: ARRAY 34 OF CHAR;
  117. VolSetIdent*: ARRAY 128 OF CHAR;
  118. PubIdent*: ARRAY 128 OF CHAR;
  119. DataPrepIdent*: ARRAY 128 OF CHAR;
  120. ApplIdent*: ARRAY 128 OF CHAR;
  121. CopyRightIdent*: ARRAY 37 OF CHAR;
  122. AbstrFileIdent*: ARRAY 37 OF CHAR;
  123. BibFileIdent*: ARRAY 37 OF CHAR;
  124. CreationTime*: ARRAY 17 OF CHAR;
  125. ModificationTime*: ARRAY 17 OF CHAR;
  126. ExpirationTime*: ARRAY 17 OF CHAR;
  127. EffectiveTime*: ARRAY 17 OF CHAR;
  128. FileStructVer*: CHAR;
  129. Unused2*: CHAR;
  130. AppUse*: ARRAY 512 OF CHAR;
  131. Unused3*: ARRAY 653 OF CHAR;
  132. END;
  133. PSVolumeDescriptorPtr = POINTER TO PSVolumeDescriptor;
  134. BootCatalogEntry = ARRAY 32 OF CHAR;
  135. BCValidationEntry = RECORD
  136. HeaderId: CHAR;
  137. PlatformId: CHAR;
  138. Reserved: INTEGER;
  139. IdString: ARRAY 24 OF CHAR;
  140. Checksum: INTEGER;
  141. KeyBytes: ARRAY 2 OF CHAR;
  142. END;
  143. BCInitialDefaultEntry = RECORD
  144. BootIndicator: CHAR;
  145. BootMediaType: CHAR;
  146. LoadSegment: INTEGER;
  147. SystemType: CHAR;
  148. Unused1: CHAR;
  149. SectorCount: INTEGER;
  150. LoadRBA: LONGINT;
  151. Unused2: ARRAY 20 OF CHAR;
  152. END;
  153. String = Strings.String;
  154. Node* = OBJECT
  155. VAR
  156. next*: Node;
  157. name*, fullpath*: String;
  158. shortname: String;
  159. lba: LONGINT;
  160. size*: LONGINT; (* size in bytes *)
  161. END Node;
  162. Directory* = OBJECT(Node)
  163. VAR
  164. parent*, nextdir*: Directory;
  165. subdir*: Directory; (* first subdirectory in this directory *)
  166. content*: Node; (*Pointer to first entry in directory *)
  167. depth*: LONGINT;
  168. fullpath*: String; (* set only if tree is physical *)
  169. no: LONGINT; (* no in pathtable *)
  170. PROCEDURE &New*(parent: Directory; name, fullpath: String; depth: LONGINT);
  171. BEGIN
  172. SELF.parent := parent;
  173. SELF.name := name;
  174. SELF.fullpath := fullpath;
  175. SELF.depth := depth;
  176. END New;
  177. END Directory;
  178. File* = OBJECT(Node)
  179. VAR
  180. fullpath*: String; (* set only if tree is not physical*)
  181. jolietFile: File; (* points to the file in the joliet tree if there is one *)
  182. prevSession*: BOOLEAN; (* file is from a previous session *)
  183. PROCEDURE &New*(name, fullpath: String; size: LONGINT);
  184. BEGIN
  185. SELF.name := name;
  186. SELF.fullpath := fullpath;
  187. SELF.size := size;
  188. END New;
  189. END File;
  190. DirectoryTree* = OBJECT
  191. VAR
  192. root*: Directory;
  193. dircnt*: LONGINT; (* number of directories *)
  194. size*: LONGINT; (* total size of direcotry tree in bytes *)
  195. sizeFiles*: LONGINT; (* total size of associated files in bytes *)
  196. type: LONGINT; (* ISO Level1 / ISO Level 2 / Joliet *)
  197. flags*: SET;
  198. PROCEDURE &New*(root: Directory; type: LONGINT; flags: SET);
  199. BEGIN
  200. (* the parent of the root dir shall be the root dir itself *)
  201. root.parent := root;
  202. SELF.root := root;
  203. SELF.type := type;
  204. SELF.flags := flags;
  205. END New;
  206. (* build up joliet tree from an iso tree *)
  207. PROCEDURE CloneTree(type: LONGINT): DirectoryTree;
  208. VAR
  209. newRoot: Directory;
  210. tree: DirectoryTree;
  211. BEGIN
  212. NEW(newRoot, NIL, root.name, root.fullpath, 0);
  213. NEW(tree, newRoot, type, flags);
  214. tree.dircnt := dircnt;
  215. tree.root := CloneDir(root);
  216. tree.root.parent := tree.root;
  217. tree.BuildRootName();
  218. tree.BuildShortNames(tree.root);
  219. tree.SortTree(tree.root);
  220. RETURN tree;
  221. END CloneTree;
  222. PROCEDURE CloneDir(dir: Directory): Directory;
  223. VAR
  224. cur, tmp, curNew: Node;
  225. newDir, folder: Directory;
  226. newFile: File;
  227. BEGIN
  228. NEW(folder, NIL, dir.name, dir.fullpath, dir.depth);
  229. cur := dir.content;
  230. WHILE cur # NIL DO
  231. IF cur IS Directory THEN
  232. newDir := CloneDir(cur(Directory));
  233. newDir.parent := folder;
  234. tmp:= newDir;
  235. ELSE
  236. NEW(newFile, cur.name, NIL, cur.size);
  237. newFile.lba := cur.lba; (* in case file is from a previous session *)
  238. cur(File).jolietFile := newFile;
  239. tmp := newFile;
  240. END;
  241. IF folder.content = NIL THEN
  242. folder.content := tmp;
  243. ELSE
  244. curNew.next := tmp;
  245. END;
  246. curNew := tmp;
  247. cur := cur.next;
  248. END;
  249. UpdateDirPointers(folder);
  250. RETURN folder;
  251. END CloneDir;
  252. PROCEDURE Build;
  253. BEGIN
  254. dircnt := 1;
  255. BuildTree(root);
  256. BuildRootName();
  257. BuildShortNames(root);
  258. SortTree(root);
  259. END Build;
  260. PROCEDURE BuildFromTree;
  261. BEGIN
  262. dircnt := 0;
  263. CountDirs(root);
  264. BuildRootName();
  265. BuildShortNames(root);
  266. SortTree(root);
  267. END BuildFromTree;
  268. PROCEDURE CountDirs(dir: Directory);
  269. VAR
  270. cur: Directory;
  271. BEGIN
  272. cur := dir;
  273. WHILE cur # NIL DO
  274. INC(dircnt);
  275. IF cur.subdir # NIL THEN
  276. CountDirs(cur.subdir);
  277. END;
  278. cur := cur.nextdir;
  279. END;
  280. END CountDirs;
  281. PROCEDURE AssignFirstDirLba(startLba: LONGINT);
  282. BEGIN
  283. size := AssignDirLba(root, startLba);
  284. size := size * SectorSize;
  285. END AssignFirstDirLba;
  286. PROCEDURE AssignFirstFileLba(startLba: LONGINT);
  287. BEGIN
  288. sizeFiles := AssignFileLba(root, startLba);
  289. sizeFiles := sizeFiles * SectorSize;
  290. END AssignFirstFileLba;
  291. PROCEDURE BuildTree(dir: Directory);
  292. VAR
  293. enumerator: Files.Enumerator;
  294. name, filename, path, mask: ARRAY MaxLen OF CHAR;
  295. time, date, size: LONGINT;
  296. flags: SET;
  297. newDir: Directory;
  298. newFile : File;
  299. cur, tmp : Node;
  300. BEGIN
  301. NEW(enumerator);
  302. COPY(dir.fullpath^, mask);
  303. Strings.Append(mask, "/*");
  304. enumerator.Open(mask, {Files.EnumSize});
  305. WHILE enumerator.HasMoreEntries() DO
  306. IF enumerator.GetEntry(name, flags, time, date, size) THEN
  307. Files.SplitPath(name, path, filename);
  308. IF Files.Directory IN flags THEN
  309. INC(dircnt);
  310. NEW(newDir, dir, Strings.NewString(filename), Strings.NewString(name), dir.depth+1);
  311. BuildTree(newDir);
  312. tmp := newDir;
  313. ELSE
  314. NEW(newFile, Strings.NewString(filename), NIL, size);
  315. tmp := newFile;
  316. END;
  317. IF dir.content = NIL THEN
  318. dir.content := tmp;
  319. ELSE
  320. cur.next := tmp;
  321. END;
  322. cur := tmp;
  323. END;
  324. END;
  325. UpdateDirPointers(dir);
  326. END BuildTree;
  327. PROCEDURE UpdateDirPointers(dir: Directory);
  328. VAR
  329. node: Node;
  330. curDir: Directory;
  331. BEGIN
  332. curDir := NIL;
  333. node := dir.content;
  334. WHILE node # NIL DO
  335. IF node IS Directory THEN
  336. IF curDir = NIL THEN
  337. curDir := node(Directory);
  338. dir.subdir := curDir;
  339. ELSE
  340. curDir.nextdir := node(Directory);
  341. curDir := curDir.nextdir;
  342. END;
  343. curDir.nextdir := NIL;
  344. END;
  345. node := node.next;
  346. END;
  347. END UpdateDirPointers;
  348. PROCEDURE SortTree(dir: Directory);
  349. VAR
  350. cur: Directory;
  351. BEGIN
  352. dir.content := Mergesort(dir.content);
  353. UpdateDirPointers(dir);
  354. cur := dir.subdir;
  355. WHILE cur # NIL DO
  356. SortTree(cur);
  357. cur := cur.nextdir;
  358. END;
  359. END SortTree;
  360. (* Merge Sort *)
  361. PROCEDURE Mergesort(head : Node): Node;
  362. VAR
  363. secondhalf: Node;
  364. BEGIN
  365. IF (head = NIL) OR (head.next = NIL) THEN
  366. RETURN head;
  367. END;
  368. secondhalf := Split(head);
  369. head := Mergesort(head);
  370. secondhalf := Mergesort(secondhalf);
  371. RETURN Merge(head, secondhalf);
  372. END Mergesort;
  373. PROCEDURE Split(head: Node): Node;
  374. VAR
  375. len, i: LONGINT;
  376. node, secondhalf: Node;
  377. BEGIN
  378. node := head;
  379. WHILE node # NIL DO
  380. INC(len, 1);
  381. node := node.next;
  382. END;
  383. node := head;
  384. FOR i:=0 TO (len DIV 2) -2 DO
  385. node := node.next;
  386. END;
  387. secondhalf := node.next;
  388. node.next := NIL;
  389. RETURN secondhalf;
  390. END Split;
  391. PROCEDURE Merge(head1, head2: Node): Node;
  392. BEGIN
  393. IF head1 = NIL THEN RETURN head2 END;
  394. IF head2 = NIL THEN RETURN head1 END;
  395. IF head1.shortname^ < head2.shortname^ THEN
  396. head1.next := Merge(head1.next, head2);
  397. RETURN head1;
  398. ELSE
  399. head2.next := Merge(head1, head2.next);
  400. RETURN head2;
  401. END;
  402. END Merge;
  403. PROCEDURE BuildRootName;
  404. VAR
  405. name, shortname: ARRAY MaxLen OF CHAR;
  406. len, count: LONGINT;
  407. BEGIN
  408. IF type = Joliet THEN
  409. COPY(root.name^, name);
  410. ReplaceNonJolietChars(name);
  411. len := Strings.Min(UTF8Strings.Length(name), 32 DIV 2);
  412. UTF8Strings.Extract(name, 0, len, shortname);
  413. ELSE
  414. count := UTF8Strings.UTF8toASCII(root.name^, CHR(95), name);
  415. Strings.UpperCase(name);
  416. ReplaceNonDChars(name);
  417. len := Strings.Min(Strings.Length(name), 32);
  418. Strings.Copy(name, 0, len, shortname);
  419. END;
  420. root.shortname := Strings.NewString(shortname);
  421. END BuildRootName;
  422. PROCEDURE BuildShortNames(dir: Directory);
  423. VAR
  424. node: Node;
  425. map: NameMap;
  426. shortname, val: ARRAY MaxLen OF CHAR;
  427. count, len: LONGINT;
  428. BEGIN
  429. NEW(map);
  430. node := dir.content;
  431. WHILE node # NIL DO
  432. IF type = Joliet THEN
  433. len := BuildJolietName(node, shortname);
  434. ELSIF type = IsoLevel2 THEN
  435. len := BuildIsoLevel2Name(node, shortname);
  436. ELSE
  437. len := BuildIsoLevel1Name(node, shortname);
  438. END;
  439. count := map.GetCount(shortname);
  440. IF count > 1 THEN
  441. Strings.IntToStr(count, val);
  442. Replace(shortname, len-Strings.Length(val), val);
  443. END;
  444. node.shortname := Strings.NewString(shortname);
  445. IF node IS Directory THEN
  446. BuildShortNames(node(Directory));
  447. END;
  448. node := node.next;
  449. END;
  450. END BuildShortNames;
  451. (* builds the iso level1 name and returns the length without extension *)
  452. PROCEDURE BuildIsoLevel1Name(node: Node; VAR shortname: ARRAY OF CHAR): LONGINT;
  453. VAR
  454. name, file, ext: ARRAY MaxLen OF CHAR;
  455. len, count: LONGINT;
  456. BEGIN
  457. count := UTF8Strings.UTF8toASCII(node.name^, CHR(95), name);
  458. Strings.UpperCase(name);
  459. IF (node IS File) & GetExtension(name, file, ext) THEN
  460. ReplaceNonDChars(file);
  461. ReplaceNonDChars(ext);
  462. len := Strings.Min(Strings.Length(file), 8);
  463. Strings.Copy(file, 0, len, shortname);
  464. Strings.Append(shortname, ".");
  465. ext[3] := 0X;
  466. Strings.Append(shortname, ext);
  467. ELSE
  468. len := Strings.Min(Strings.Length(name), 8);
  469. Strings.Copy(name, 0, len, shortname);
  470. ReplaceNonDChars(shortname);
  471. END;
  472. RETURN len;
  473. END BuildIsoLevel1Name;
  474. (* builds the iso level2 name and returns the length without extension *)
  475. PROCEDURE BuildIsoLevel2Name(node: Node; VAR shortname: ARRAY OF CHAR): LONGINT;
  476. VAR
  477. name, file, ext: ARRAY MaxLen OF CHAR;
  478. len, count: LONGINT;
  479. BEGIN
  480. count := UTF8Strings.UTF8toASCII(node.name^, CHR(95), name);
  481. Strings.UpperCase(name);
  482. IF (node IS File) THEN
  483. IF GetExtension(name, file, ext) THEN
  484. ReplaceNonDChars(file);
  485. ReplaceNonDChars(ext);
  486. len := Strings.Min(Strings.Length(file), 30-Strings.Length(ext)-1);
  487. Strings.Copy(file, 0, len, shortname);
  488. Strings.Append(shortname, ".");
  489. ext[30] := 0X;
  490. Strings.Append(shortname, ext);
  491. ELSE
  492. len := Strings.Min(Strings.Length(name), 30);
  493. Strings.Copy(name, 0, len, shortname);
  494. ReplaceNonDChars(shortname);
  495. END;
  496. ELSE
  497. len := Strings.Min(Strings.Length(name), 31);
  498. Strings.Copy(name, 0, len, shortname);
  499. ReplaceNonDChars(shortname);
  500. END;
  501. RETURN len;
  502. END BuildIsoLevel2Name;
  503. (* builds the joliet name and returns the length without extension *)
  504. (* we do not convert to UCS-2 here but only check name length and replace some chars *)
  505. PROCEDURE BuildJolietName(node: Node; VAR shortname: ARRAY OF CHAR): LONGINT;
  506. VAR
  507. name, file, ext: ARRAY MaxLen OF CHAR;
  508. len: LONGINT;
  509. BEGIN
  510. COPY(node.name^, name);
  511. ReplaceNonJolietChars(name);
  512. IF (node IS File) & GetExtension(name, file, ext) THEN
  513. len := Strings.Min(UTF8Strings.Length(file), 64 - UTF8Strings.Length(ext) -1);
  514. UTF8Strings.Extract(name, 0, len, shortname);
  515. Strings.Append(shortname, ".");
  516. Strings.Append(shortname, ext);
  517. ELSE
  518. len := Strings.Min(UTF8Strings.Length(name), 64);
  519. UTF8Strings.Extract(name, 0, len, shortname);
  520. END;
  521. RETURN len;
  522. END BuildJolietName;
  523. PROCEDURE ReplaceNonJolietChars(VAR str: ARRAY OF CHAR);
  524. VAR
  525. len, i: LONGINT;
  526. BEGIN
  527. len := UTF8Strings.Length(str);
  528. FOR i := 0 TO len -1 DO
  529. CASE str[i] OF
  530. '*', '/', ':', ';', '?': str[i] := '_';
  531. ELSE
  532. END;
  533. END;
  534. END ReplaceNonJolietChars;
  535. (* splits name in file and extension *)
  536. (* returns FALSE in case there is no extension *)
  537. PROCEDURE Replace(VAR src: ARRAY OF CHAR; pos: LONGINT; CONST new: ARRAY OF CHAR);
  538. VAR
  539. len: LONGINT;
  540. BEGIN
  541. len := UTF8Strings.Length(new);
  542. UTF8Strings.Delete(src, pos, len);
  543. UTF8Strings.Insert(new, pos, src);
  544. END Replace;
  545. PROCEDURE ReplaceNonDChars(VAR str: ARRAY OF CHAR);
  546. VAR
  547. i, num: LONGINT;
  548. BEGIN
  549. WHILE str[i] # 0X DO
  550. num := ORD(str[i]);
  551. IF (num < 48) OR ((num > 57) & (num < 65)) OR ((num > 90) & (num <97)) OR (num > 122) THEN
  552. str[i] := CHR(95);
  553. END;
  554. INC(i, 1);
  555. END;
  556. END ReplaceNonDChars;
  557. PROCEDURE GetMaxPathLength(): LONGINT;
  558. BEGIN
  559. RETURN GetMaxPathLengthDir(root);
  560. END GetMaxPathLength;
  561. PROCEDURE GetMaxPathLengthDir(dir : Directory): LONGINT;
  562. VAR
  563. node: Node;
  564. cur: Directory;
  565. max, len: LONGINT;
  566. BEGIN
  567. max := 0;
  568. node := dir.content;
  569. WHILE node # NIL DO
  570. len := UTF8Strings.Length(node.shortname^);
  571. max := Strings.Max(max, len);
  572. node := node.next;
  573. END;
  574. cur := dir.subdir;
  575. WHILE cur # NIL DO
  576. len :=GetMaxPathLengthDir(cur);
  577. max := Strings.Max(max, len);
  578. cur := cur.nextdir;
  579. END;
  580. IF dir.parent # dir THEN (* root directory *)
  581. INC(max); (* relevant directory *)
  582. INC(max, UTF8Strings.Length(dir.shortname^)); (* length of relevant directory identifier *)
  583. END;
  584. RETURN max;
  585. END GetMaxPathLengthDir;
  586. PROCEDURE AssignDirLba(dir: Directory; startsec: LONGINT): LONGINT;
  587. VAR
  588. cur: Directory;
  589. secs, nextsec: LONGINT;
  590. BEGIN
  591. secs := 0;
  592. dir.lba := startsec;
  593. secs := CalcDirLength(dir);
  594. nextsec := startsec + secs;
  595. cur := dir.subdir;
  596. WHILE cur # NIL DO
  597. secs := AssignDirLba(cur, nextsec);
  598. INC(nextsec, secs);
  599. cur := cur.nextdir;
  600. END;
  601. RETURN nextsec - startsec;
  602. END AssignDirLba;
  603. PROCEDURE CalcDirLength(dir: Directory): LONGINT;
  604. VAR
  605. node: Node;
  606. secs, ofs, len: LONGINT;
  607. BEGIN
  608. secs := 0;
  609. node := dir.content;
  610. ofs := 2*22H; (* self and parent reference *)
  611. WHILE node # NIL DO
  612. len := UTF8Strings.Length(node.shortname^);
  613. IF (node IS File) & ~(NoVersion IN flags) THEN INC(len, 2); END;
  614. IF type =Joliet THEN len := 2*len; END;
  615. INC(len, 33);
  616. INC(len, len MOD 2); (* pad to even size *)
  617. IF ofs + len > SectorSize THEN
  618. INC(secs); ofs := 0;
  619. END;
  620. INC(ofs, len);
  621. node := node.next;
  622. END;
  623. INC(secs);
  624. dir.size := secs*SectorSize;
  625. RETURN secs;
  626. END CalcDirLength;
  627. PROCEDURE AssignFileLba(dir: Directory; startsec: LONGINT): LONGINT;
  628. VAR
  629. node: Node;
  630. cur: Directory;
  631. secs, nextsec: LONGINT;
  632. BEGIN
  633. nextsec := startsec;
  634. node := dir.content;
  635. WHILE node # NIL DO
  636. IF (node IS File) & ~node(File).prevSession THEN (* skip files from previous sessions *)
  637. node.lba := nextsec;
  638. IF node(File).jolietFile # NIL THEN node(File).jolietFile.lba := nextsec END;
  639. INC(nextsec, (node.size + SectorSize - 1) DIV SectorSize);
  640. END;
  641. node := node.next;
  642. END;
  643. cur := dir.subdir;
  644. WHILE cur # NIL DO
  645. secs := AssignFileLba(cur, nextsec);
  646. INC(nextsec, secs);
  647. cur := cur.nextdir;
  648. END;
  649. RETURN nextsec - startsec;
  650. END AssignFileLba;
  651. PROCEDURE Write(w: Streams.Writer);
  652. BEGIN
  653. WriteTree(w, root);
  654. END Write;
  655. PROCEDURE WriteTree(w: Streams.Writer; dir: Directory);
  656. VAR
  657. cur: Directory;
  658. BEGIN
  659. WriteDirectory(w, dir);
  660. cur := dir.subdir;
  661. WHILE (cur # NIL) DO
  662. WriteTree(w, cur);
  663. cur := cur.nextdir;
  664. END;
  665. END WriteTree;
  666. PROCEDURE WriteDirectory(w: Streams.Writer; dir: Directory);
  667. VAR
  668. rec: DirectoryRecordPtr;
  669. cur: Node;
  670. ofs: LONGINT;
  671. bufAdr, recAdr: ADDRESS;
  672. buf: POINTER TO ARRAY OF CHAR;
  673. time: Dates.DateTime;
  674. len: LONGINT;
  675. name: ARRAY MaxLen OF CHAR;
  676. BEGIN
  677. time := Dates.Now();
  678. NEW(buf, dir.size);
  679. bufAdr := ADDRESSOF(buf^); recAdr := bufAdr;
  680. (* add record for self reference *)
  681. rec := SYSTEM.VAL(DirectoryRecordPtr, recAdr);
  682. ASSERT(ADDRESSOF(rec^) = SYSTEM.VAL(LONGINT, rec));
  683. rec.Len := 22X;
  684. SetBothByteOrder32(dir.lba, rec.Lba);
  685. SetBothByteOrder32(dir.size, rec.Size);
  686. SetTime(time, 0, rec.Time);
  687. rec.Flags := FFDirectory;
  688. SetBothByteOrder16(1, rec.VolSeqNo);
  689. rec.IdentLen := 1X; rec.Ident[0] := 0X;
  690. INC(recAdr, 22H);
  691. (* add record for parent reference *)
  692. rec := SYSTEM.VAL(DirectoryRecordPtr, recAdr);
  693. rec.Len := 22X;
  694. SetBothByteOrder32(dir.parent.lba, rec.Lba);
  695. SetBothByteOrder32(dir.parent.size, rec.Size);
  696. SetTime(time, 0, rec.Time);
  697. rec.Flags := FFDirectory;
  698. SetBothByteOrder16(1, rec.VolSeqNo);
  699. rec.IdentLen := 1X; rec.Ident[0] := 1X;
  700. INC(recAdr, 22H);
  701. ofs := 2*22H;
  702. (* add an entry for each node in this directory *)
  703. cur := dir.content;
  704. WHILE cur # NIL DO
  705. COPY(cur.shortname^, name);
  706. IF (cur IS File) & ~(NoVersion IN flags) THEN
  707. Strings.Append(name, ";1");
  708. END;
  709. len := GetIdentLen(name);
  710. INC(len, 33);
  711. INC(len, len MOD 2); (* pad to even size *)
  712. IF ofs + len > SectorSize THEN
  713. INC(recAdr, SectorSize-ofs); ofs := 0;
  714. END;
  715. rec := SYSTEM.VAL(DirectoryRecordPtr, recAdr);
  716. ASSERT(recAdr+len <= bufAdr + LEN(buf^));
  717. rec.Len := CHR(len);
  718. SetBothByteOrder32(cur.lba, rec.Lba);
  719. SetBothByteOrder32(cur.size, rec.Size);
  720. SetTime(time, 0, rec.Time);
  721. IF cur IS Directory THEN
  722. rec.Flags := FFDirectory;
  723. ELSE
  724. rec.Flags := 0X;
  725. END;
  726. SetBothByteOrder16(1, rec.VolSeqNo);
  727. rec.IdentLen := CHR(GetIdentLen(name));
  728. IF type = Joliet THEN
  729. ConvertUTF8ToUCS2(name, rec.Ident);
  730. ELSE
  731. COPY(name, rec.Ident);
  732. END;
  733. INC(ofs, len); INC(recAdr, ORD(rec.Len));
  734. cur := cur.next;
  735. END;
  736. w.Bytes(buf^, 0, dir.size);
  737. END WriteDirectory;
  738. PROCEDURE GetIdentLen(CONST name: ARRAY OF CHAR): LONGINT;
  739. VAR
  740. len: LONGINT;
  741. BEGIN
  742. len := UTF8Strings.Length(name);
  743. IF type = Joliet THEN len := 2*len; END;
  744. RETURN len;
  745. END GetIdentLen;
  746. END DirectoryTree;
  747. (* Builds the Directory Tree from an ISO File *)
  748. (* necessary for Multisession ISO *)
  749. ISOReader* = OBJECT
  750. VAR
  751. dev: ATADisks.DeviceATAPI;
  752. tree*: DirectoryTree;
  753. PROCEDURE &New*(dev: ATADisks.DeviceATAPI);
  754. BEGIN
  755. SELF.dev := dev;
  756. END New;
  757. PROCEDURE Read*(startsec: LONGINT): LONGINT;
  758. VAR
  759. voldescr: PSVolumeDescriptor;
  760. res: WORD; treeType: LONGINT;
  761. rootRec: DirectoryRecord;
  762. root: Directory;
  763. tmp, name: ARRAY MaxLen OF CHAR;
  764. BEGIN
  765. IF GetVolumeDescriptor(dev, startsec, voldescr, Supplementary) = ResOk THEN
  766. treeType := Joliet;
  767. ELSIF GetVolumeDescriptor(dev, startsec, voldescr, Primary) # ResOk THEN
  768. RETURN ResErr; (* iso image not found *)
  769. END;
  770. ConvertIdentToUTF8(voldescr.StdIdent, LEN(voldescr.StdIdent), FALSE, tmp);
  771. ASSERT(tmp = ISO9660Id);
  772. SYSTEM.MOVE(ADDRESSOF(voldescr.RootDirRecord), ADDRESSOF(rootRec), 22H);
  773. ConvertIdentToUTF8(voldescr.VolIdent, LEN(voldescr.VolIdent), treeType = Joliet, name);
  774. NEW(root, NIL, Strings.NewString(name), NIL, 0);
  775. root.size := Utils.ConvertLE32Int(rootRec.Size);
  776. root.lba := Utils.ConvertLE32Int(rootRec.Lba);
  777. NEW(tree, root, treeType, {});
  778. tree.dircnt := 1;
  779. res := ReadDir(tree.root);
  780. RETURN ResOk;
  781. END Read;
  782. PROCEDURE ReadDir(parent: Directory): WORD;
  783. VAR
  784. index, size, lba, len: LONGINT; res: WORD;
  785. name : ARRAY MaxLen OF CHAR;
  786. dirRec: DirectoryRecord;
  787. file: File;
  788. dir, curDir: Directory;
  789. cur, tmp: Node;
  790. buf: POINTER TO ARRAY OF CHAR;
  791. BEGIN
  792. NEW(buf, parent.size); INC(tree.size, parent.size);
  793. dev.Transfer(Disks.Read, parent.lba, parent.size DIV SectorSize, buf^, 0, res);
  794. IF res # ResOk THEN
  795. RETURN res;
  796. END;
  797. index := 2*22H; (* skip parent and self reference*)
  798. WHILE buf[index] > 0X DO
  799. len := ORD(buf[index]);
  800. SYSTEM.MOVE(ADDRESSOF(buf[index]), ADDRESSOF(dirRec), len);
  801. size := Utils.ConvertLE32Int(dirRec.Size);
  802. lba := Utils.ConvertLE32Int(dirRec.Lba);
  803. ConvertIdentToUTF8(dirRec.Ident, ORD(dirRec.IdentLen), tree.type = Joliet, name);
  804. RemoveVersion(name);
  805. IF dirRec.Flags # FFHidden THEN
  806. IF dirRec.Flags = FFDirectory THEN
  807. INC(tree.dircnt);
  808. NEW(dir, parent, Strings.NewString(name), NIL, parent.depth+1);
  809. dir.size := size; dir.lba := lba;
  810. res := ReadDir(dir);
  811. IF res # ResOk THEN RETURN res END;
  812. IF parent.subdir = NIL THEN
  813. parent.subdir := dir;
  814. ELSE
  815. curDir.nextdir := dir;
  816. END;
  817. tmp := dir;
  818. curDir := dir;
  819. ELSE
  820. NEW(file, Strings.NewString(name), NIL, size);
  821. INC(tree.sizeFiles, size);
  822. file.prevSession := TRUE;
  823. file.lba := lba;
  824. tmp := file;
  825. END;
  826. tmp.shortname := tmp.name;
  827. IF parent.content = NIL THEN
  828. parent.content := tmp;
  829. ELSE
  830. cur.next := tmp;
  831. END;
  832. cur := tmp;
  833. END;
  834. INC(index, len);
  835. END;
  836. RETURN ResOk;
  837. END ReadDir;
  838. PROCEDURE RemoveVersion(VAR str: ARRAY OF CHAR);
  839. VAR
  840. len: LONGINT;
  841. BEGIN
  842. len := Strings.Length(str);
  843. IF str[len-2] = ';' THEN
  844. str[len-2] := 0X;
  845. ELSIF str[len-1] = ';' THEN
  846. str[len-1] := 0X;
  847. END;
  848. END RemoveVersion;
  849. END ISOReader;
  850. ISOInfo* = OBJECT
  851. VAR
  852. pvd: PSVolumeDescriptorPtr;
  853. PROCEDURE Open*(filename: Strings.String): LONGINT;
  854. VAR
  855. ofs, bytesRead, total: LONGINT;
  856. file: Files.File;
  857. r: Files.Reader;
  858. buf: ARRAY SectorSize OF CHAR;
  859. tmp: ARRAY 256 OF CHAR;
  860. BEGIN
  861. file := Files.Old(filename^);
  862. IF file = NIL THEN RETURN ErrFileNotFound END;
  863. IF file.Length() MOD SectorSize # 0 THEN RETURN ErrNoIsoImage END;
  864. (* search pvd in first 10 sectors after system area *)
  865. ofs := NumSystemSectors*SectorSize;
  866. Files.OpenReader(r, file, ofs);
  867. total := 0;
  868. REPEAT
  869. r.Bytes(buf, 0, SectorSize, bytesRead);
  870. IF bytesRead < SectorSize THEN RETURN ErrNoIsoImage END;
  871. INC(total, SectorSize);
  872. UNTIL (buf[0] = 1X) OR (total > ofs + 10*SectorSize);
  873. IF buf[0] # 1X THEN RETURN ErrNoIsoImage END;
  874. pvd := SYSTEM.VAL(PSVolumeDescriptorPtr, ADDRESSOF(buf[0]));
  875. ConvertIdentToUTF8(pvd.StdIdent, LEN(pvd.StdIdent), FALSE, tmp);
  876. IF tmp # ISO9660Id THEN RETURN ErrNoIsoImage END;
  877. RETURN ResOk;
  878. END Open;
  879. END ISOInfo;
  880. (* queue for level order traversal of tree *)
  881. Queue = OBJECT
  882. VAR
  883. queue: POINTER TO ARRAY OF ANY;
  884. head, tail, size: LONGINT;
  885. PROCEDURE &New*(size: LONGINT);
  886. BEGIN
  887. head := 0; tail := 0;
  888. SELF.size := size;
  889. NEW(queue, size+1);
  890. END New;
  891. PROCEDURE Put(ptr: ANY);
  892. BEGIN
  893. queue[tail] := ptr; INC(tail);
  894. IF tail > size THEN tail := 0; END;
  895. END Put;
  896. PROCEDURE Get(): ANY;
  897. VAR
  898. ptr: ANY;
  899. BEGIN
  900. ptr := queue[head]; INC(head);
  901. IF head > size THEN head := 0; END;
  902. RETURN ptr;
  903. END Get;
  904. PROCEDURE IsEmpty(): BOOLEAN;
  905. BEGIN
  906. RETURN head = tail;
  907. END IsEmpty;
  908. END Queue;
  909. PathTable = OBJECT
  910. VAR
  911. tree: DirectoryTree;
  912. table: POINTER TO ARRAY OF Directory;
  913. size: LONGINT; (* length of pathtable in bytes *)
  914. lbaLType, lbaRType: LONGINT;
  915. PROCEDURE &New*(tree: DirectoryTree);
  916. BEGIN
  917. SELF.tree := tree;
  918. END New;
  919. (* traverse the tree in level order and fill the table *)
  920. PROCEDURE Build;
  921. VAR
  922. queue: Queue;
  923. ptr : ANY;
  924. cur: Directory;
  925. no: LONGINT;
  926. BEGIN
  927. NEW(table, tree.dircnt);
  928. NEW(queue, tree.dircnt);
  929. size := 0;
  930. cur := tree.root;
  931. queue.Put(cur);
  932. WHILE ~queue.IsEmpty() DO
  933. ptr := queue.Get();
  934. cur := ptr(Directory);
  935. table[no] := cur;
  936. INC(no); cur.no := no;
  937. INC(size, CalcRecordLength(cur));
  938. cur := cur.subdir;
  939. WHILE cur # NIL DO
  940. queue.Put(cur);
  941. cur := cur.nextdir;
  942. END;
  943. END;
  944. END Build;
  945. PROCEDURE CalcRecordLength(dir: Directory): LONGINT;
  946. VAR
  947. len: LONGINT;
  948. BEGIN
  949. IF dir.parent = dir THEN (* root directory *)
  950. len := 10;
  951. ELSE
  952. len := UTF8Strings.Length(dir.shortname^);
  953. IF tree.type = Joliet THEN len := 2*len; END;
  954. INC(len, 8);
  955. INC(len, len MOD 2); (* pad to even size *)
  956. END;
  957. RETURN len;
  958. END CalcRecordLength;
  959. PROCEDURE Write (w: Streams.Writer; tableType: LONGINT);
  960. VAR
  961. dir: Directory;
  962. buf: POINTER TO ARRAY OF CHAR;
  963. rec: PathTableRecordPtr;
  964. i, len, bytesWritten : LONGINT;
  965. BEGIN
  966. NEW(buf, MaxLen); (* max record length *)
  967. rec := SYSTEM.VAL(PathTableRecordPtr, ADDRESSOF(buf^));
  968. ASSERT(ADDRESSOF(rec^) = SYSTEM.VAL(LONGINT, rec));
  969. (* first write entry for root record *)
  970. rec.IdentLen := 1X;
  971. IF tableType = LType THEN
  972. Utils.SetLE32(tree.root.lba, rec.Lba);
  973. Utils.SetLE16(1, rec.ParentNo);
  974. ELSE
  975. Utils.SetBE32(tree.root.lba, rec.Lba);
  976. Utils.SetBE16(1, rec.ParentNo);
  977. END;
  978. rec.Ident[0] := 0X;
  979. w.Bytes(buf^, 0, 10); bytesWritten := 10;
  980. (* now write the records for all other entries in the pathtable *)
  981. FOR i:=1 TO tree.dircnt-1 DO
  982. dir := table[i](Directory);
  983. len := UTF8Strings.Length(dir.shortname^);
  984. IF tree.type = Joliet THEN len := 2*len; END;
  985. rec.IdentLen := CHR(len);
  986. INC(len, 8);
  987. INC(len, len MOD 2); (* pad to even size *)
  988. IF tableType = LType THEN
  989. Utils.SetLE32(dir.lba, rec.Lba);
  990. Utils.SetLE16(SHORT(dir.parent.no), rec.ParentNo);
  991. ELSE
  992. Utils.SetBE32(dir.lba, rec.Lba);
  993. Utils.SetBE16(SHORT(dir.parent.no), rec.ParentNo);
  994. END;
  995. IF tree.type = Joliet THEN
  996. ConvertUTF8ToUCS2(dir.shortname^, rec.Ident);
  997. ELSE
  998. COPY(dir.shortname^, rec.Ident);
  999. END;
  1000. ASSERT(len <= MaxLen);
  1001. w.Bytes(buf^, 0, len);
  1002. INC(bytesWritten, len);
  1003. END;
  1004. IF (size MOD SectorSize) # 0 THEN
  1005. Pad(w, SectorSize - bytesWritten MOD SectorSize);
  1006. END;
  1007. END Write;
  1008. END PathTable;
  1009. (* Boot Catalog *)
  1010. BCEntry = POINTER TO RECORD
  1011. next: BCEntry;
  1012. image: String;
  1013. loadRBA, size: LONGINT;
  1014. bootable: BOOLEAN;
  1015. emulation: CHAR;
  1016. id: String;
  1017. platform: CHAR;
  1018. END;
  1019. (* at the moment only the default entry is implemented *)
  1020. BootCatalog* = OBJECT
  1021. VAR
  1022. size: LONGINT; (* size in bytes *)
  1023. sizeImages: LONGINT; (* size of associated images *)
  1024. root: BCEntry;
  1025. PROCEDURE &New*;
  1026. BEGIN
  1027. INC(size, 32); (* validation entry *)
  1028. END New;
  1029. PROCEDURE AddDefaultEntry*(image, id: String; bootable: BOOLEAN; platform, emulation: CHAR);
  1030. VAR
  1031. file: Files.File;
  1032. BEGIN
  1033. file := Files.Old(image^);
  1034. IF file # NIL THEN
  1035. INC(size, 32);
  1036. NEW(root);
  1037. root.size := file.Length();
  1038. root.image := image;
  1039. root.id := id;
  1040. root.bootable := bootable;
  1041. root.platform := platform;
  1042. root.emulation := emulation;
  1043. INC(sizeImages, root.size);
  1044. IF (root.size MOD SectorSize) # 0 THEN
  1045. INC(sizeImages, SectorSize - (root.size MOD SectorSize)); (* Padding *)
  1046. END;
  1047. END;
  1048. END AddDefaultEntry;
  1049. PROCEDURE AssignFirstImageLba(startlba: LONGINT);
  1050. VAR
  1051. cur: BCEntry;
  1052. BEGIN
  1053. cur := root;
  1054. WHILE cur # NIL DO
  1055. cur.loadRBA := startlba;
  1056. INC(startlba, (cur.size + SectorSize - 1) DIV SectorSize);
  1057. cur := cur.next;
  1058. END;
  1059. END AssignFirstImageLba;
  1060. PROCEDURE Write(w: Streams.Writer);
  1061. VAR
  1062. entry: BCValidationEntry; entry2: BCInitialDefaultEntry;
  1063. BEGIN
  1064. ASSERT(size >= 64);
  1065. (* validation entry *)
  1066. entry.HeaderId := 1X; (* header id *)
  1067. entry.PlatformId := root.platform; (* platform id *)
  1068. entry.Reserved := 0; (* reserved *)
  1069. SetStringWithPadding(root.id^, entry.IdString, 0X, FALSE);
  1070. entry.Checksum := 0;(* init checksum to zero *)
  1071. entry.KeyBytes[0] := 55X; entry.KeyBytes[1] := 0AAX; (* key bytes *)
  1072. entry.Checksum := CalcChecksum16(SYSTEM.VAL(BootCatalogEntry, entry)); (* update the checksum *)
  1073. w.Bytes(SYSTEM.VAL(BootCatalogEntry, entry), 0, SIZEOF(BCValidationEntry));
  1074. (* initial / default entry *)
  1075. IF root.bootable THEN
  1076. entry2.BootIndicator := Bootable;
  1077. ELSE
  1078. entry2.BootIndicator := NotBootable;
  1079. END;
  1080. entry2.BootMediaType := root.emulation;
  1081. entry2.LoadSegment := 0; (* use default load segment which is 7C0H *)
  1082. GetSysType(root, entry2.SystemType);
  1083. entry2.Unused1 := 0X;
  1084. entry2.SectorCount := 1;
  1085. entry2.LoadRBA := root.loadRBA;
  1086. w.Bytes(SYSTEM.VAL(BootCatalogEntry, entry2), 0, SIZEOF(BCInitialDefaultEntry));
  1087. IF (size MOD SectorSize) # 0 THEN
  1088. Pad(w, SectorSize - (size MOD SectorSize));
  1089. END;
  1090. END Write;
  1091. PROCEDURE GetSysType(entry: BCEntry; VAR type: CHAR);
  1092. VAR
  1093. file: Files.File;
  1094. r: Files.Reader;
  1095. bytesRead, i: LONGINT;
  1096. buf: POINTER TO ARRAY OF CHAR;
  1097. table: PartitionTable;
  1098. BEGIN
  1099. IF entry.emulation # EmulationHDD THEN type := 0X; RETURN END;
  1100. file := Files.Old(root.image^);
  1101. IF file # NIL THEN
  1102. Files.OpenReader(r, file, 0);
  1103. NEW(buf, SIZEOF(PartitionTable));
  1104. r.Bytes(buf^, 0, SIZEOF(PartitionTable), bytesRead);
  1105. ASSERT(bytesRead = SIZEOF(PartitionTable));
  1106. SYSTEM.MOVE(ADDRESSOF(buf^), ADDRESSOF(table), SIZEOF(PartitionTable));
  1107. FOR i := 0 TO NumPartitions-1 DO
  1108. IF table[i].BootIndicator = Bootable THEN
  1109. type := table[i].SysIndicator;
  1110. END;
  1111. END;
  1112. END;
  1113. END GetSysType;
  1114. PROCEDURE WriteImages(w: Streams.Writer): WORD;
  1115. VAR
  1116. res : WORD;
  1117. cur: BCEntry;
  1118. BEGIN
  1119. cur := root;
  1120. WHILE cur # NIL DO
  1121. res := WriteFile(w, cur.image^);
  1122. IF res # ResOk THEN RETURN res END;
  1123. IF (cur.size MOD SectorSize) # 0 THEN
  1124. Pad(w, SectorSize - (cur.size MOD SectorSize));
  1125. END;
  1126. cur := cur.next;
  1127. END;
  1128. RETURN ResOk;
  1129. END WriteImages;
  1130. PROCEDURE CalcChecksum16(CONST buf: ARRAY OF CHAR): INTEGER;
  1131. VAR
  1132. checksum, i, numWords: LONGINT;
  1133. BEGIN
  1134. checksum := 0;
  1135. numWords := LEN(buf) DIV 2;
  1136. FOR i := 0 TO numWords - 1 DO
  1137. checksum := (checksum + SYSTEM.VAL(INTEGER, buf[i * 2])) MOD 10000H;
  1138. END;
  1139. RETURN SHORT(10000H - checksum);
  1140. END CalcChecksum16;
  1141. END BootCatalog;
  1142. (* NameMap implements a binary search tree for looking up filenames *)
  1143. (* used to build unique short names *)
  1144. Entry = OBJECT
  1145. VAR
  1146. left, right: Entry;
  1147. name: ARRAY MaxLen OF CHAR;
  1148. count: LONGINT;
  1149. PROCEDURE &New*(CONST name: ARRAY OF CHAR);
  1150. BEGIN
  1151. COPY(name, SELF.name);
  1152. SELF.count := 1;
  1153. END New;
  1154. END Entry;
  1155. NameMap = OBJECT
  1156. VAR
  1157. root: Entry;
  1158. PROCEDURE GetCount(CONST str: ARRAY OF CHAR): LONGINT;
  1159. VAR
  1160. entry, tmp: Entry;
  1161. BEGIN
  1162. IF root = NIL THEN
  1163. NEW(root, str);
  1164. RETURN 1;
  1165. END;
  1166. entry := root;
  1167. WHILE (entry # NIL) DO
  1168. IF str = entry.name THEN
  1169. INC(entry.count, 1);
  1170. RETURN entry.count;
  1171. ELSIF entry.name > str THEN
  1172. IF entry.left = NIL THEN
  1173. NEW(tmp, str);
  1174. entry.left := tmp;
  1175. RETURN 1;
  1176. ELSE
  1177. entry := entry.left;
  1178. END;
  1179. ELSE
  1180. IF entry.right = NIL THEN
  1181. NEW(tmp, str);
  1182. entry.right := tmp;
  1183. RETURN 1;
  1184. ELSE
  1185. entry := entry.right;
  1186. END;
  1187. END;
  1188. END;
  1189. END GetCount;
  1190. END NameMap;
  1191. IsoSettings* = RECORD
  1192. isoLevel*: LONGINT;
  1193. padToSize*: LONGINT; (* small images are padded to padToSize sectors *)
  1194. joliet*: BOOLEAN;
  1195. flags*: SET;
  1196. bootCatalog*: BootCatalog;
  1197. startLba*: LONGINT;
  1198. volumeIdent: String;
  1199. END;
  1200. WritingStatus* = OBJECT(Utils.Status);
  1201. VAR
  1202. fileName*: String;
  1203. bytesWritten*: LONGINT;
  1204. END WritingStatus;
  1205. VAR
  1206. onWriteStatusChanged: Utils.StatusProc;
  1207. status: WritingStatus;
  1208. PROCEDURE GetExtension*(CONST name: ARRAY OF CHAR; VAR file, ext: ARRAY OF CHAR) : BOOLEAN;
  1209. BEGIN
  1210. Strings.GetExtension (name, file, ext);
  1211. RETURN ext[0] # 0X
  1212. END GetExtension;
  1213. PROCEDURE Pad(w: Streams.Writer; len: LONGINT);
  1214. VAR
  1215. buf: POINTER TO ARRAY SectorSize OF CHAR;
  1216. i: LONGINT;
  1217. BEGIN
  1218. IF len >= SectorSize THEN
  1219. NEW(buf);
  1220. (* Utils.ClearBuffer(buf^, 0, SectorSize); *) (* memory is cleared on allocation *)
  1221. END;
  1222. WHILE len >= SectorSize DO
  1223. w.Bytes(buf^, 0, SectorSize);
  1224. DEC(len, SectorSize);
  1225. END;
  1226. FOR i:=0 TO len - 1 DO
  1227. w.Char(0X);
  1228. END;
  1229. END Pad;
  1230. PROCEDURE WriteFiles(w: Streams.Writer; dir: Directory): WORD;
  1231. VAR
  1232. node: Node;
  1233. cur: Directory;
  1234. pathname: ARRAY MaxLen OF CHAR;
  1235. res: WORD;
  1236. BEGIN
  1237. node := dir.content;
  1238. WHILE node # NIL DO
  1239. IF (node IS File) & ~node(File).prevSession THEN
  1240. IF node(File).fullpath = NIL THEN
  1241. Files.JoinPath(dir.fullpath^, node.name^, pathname);
  1242. ELSE
  1243. COPY(node(File).fullpath^, pathname);
  1244. END;
  1245. IF node.size > 0 THEN
  1246. res := WriteFile(w, pathname);
  1247. IF res # ResOk THEN RETURN res END;
  1248. IF (node.size MOD SectorSize) # 0 THEN
  1249. Pad(w, SectorSize - (node.size MOD SectorSize));
  1250. END;
  1251. END;
  1252. END;
  1253. node := node.next;
  1254. END;
  1255. cur := dir.subdir;
  1256. WHILE cur # NIL DO
  1257. res := WriteFiles(w, cur);
  1258. IF res # ResOk THEN RETURN res END;
  1259. cur := cur.nextdir;
  1260. END;
  1261. RETURN ResOk;
  1262. END WriteFiles;
  1263. PROCEDURE WriteFile(w: Streams.Writer; CONST pathname: ARRAY OF CHAR): LONGINT;
  1264. VAR
  1265. file: Files.File;
  1266. r: Files.Reader;
  1267. buf: ARRAY 1024 OF CHAR;
  1268. bytesRead, res: LONGINT;
  1269. BEGIN
  1270. res := ErrFileNotFound;
  1271. status.fileName := Strings.NewString(pathname);
  1272. file := Files.Old(pathname);
  1273. IF file # NIL THEN
  1274. Files.OpenReader(r, file, 0);
  1275. REPEAT
  1276. r.Bytes(buf, 0, 1024, bytesRead);
  1277. w.Bytes(buf, 0, bytesRead);
  1278. IF onWriteStatusChanged # NIL THEN
  1279. INC(status.bytesWritten, bytesRead);
  1280. onWriteStatusChanged(status);
  1281. END;
  1282. UNTIL bytesRead <1024;
  1283. res := ResOk;
  1284. END;
  1285. RETURN res;
  1286. END WriteFile;
  1287. PROCEDURE WriteVolumeDescriptor(w: Streams.Writer; descr: VolumeDescriptor);
  1288. BEGIN
  1289. w.Bytes(descr, 0, LEN(descr));
  1290. END WriteVolumeDescriptor;
  1291. (* initialize Set Terminator *)
  1292. PROCEDURE InitSetTerminator(VAR descr: SetTerminator);
  1293. BEGIN
  1294. descr.Type := 0FFX;
  1295. SetStringWithPadding(ISO9660Id, descr.StdIdent, ' ', FALSE);
  1296. descr.Version := 1X;
  1297. END InitSetTerminator;
  1298. (* Initialize Boot Record *)
  1299. PROCEDURE InitBootRecord(VAR descr: BootRecord; lba: LONGINT);
  1300. BEGIN
  1301. descr.Type := 0X;
  1302. SetStringWithPadding(ISO9660Id, descr.StdIdent, 0X, FALSE);
  1303. descr.Version := 1X;
  1304. SetStringWithPadding(ElToritoSysId, descr. BootSysIdent, 0X, FALSE);
  1305. Utils.SetLE32(lba, descr.Lba);
  1306. END InitBootRecord;
  1307. (* initialize Primary / Supplementary volume descriptor *)
  1308. PROCEDURE InitPSVolumeDescriptor(VAR descr: PSVolumeDescriptor; tree: DirectoryTree; table: PathTable; volSize, descrType: LONGINT);
  1309. VAR
  1310. rec: DirectoryRecord;
  1311. time: Dates.DateTime;
  1312. dtBuf: ARRAY 20 OF CHAR;
  1313. srcAdr, dstAdr: ADDRESS;
  1314. ucs2: BOOLEAN;
  1315. BEGIN
  1316. time := Dates.Now();
  1317. ucs2 := FALSE;
  1318. Strings.FormatDateTime("yyyymmddhhnnss00", time, dtBuf);
  1319. SetStringWithPadding(ISO9660Id, descr.StdIdent, ' ', FALSE);
  1320. descr.Version := 1X;
  1321. IF descrType = Primary THEN
  1322. descr.Type := 1X;
  1323. ELSIF descrType = Supplementary THEN
  1324. descr.Type := 2X;
  1325. ucs2 := TRUE;
  1326. END;
  1327. SetStringWithPadding("", descr.SysIdent, ' ', ucs2);
  1328. SetStringWithPadding(tree.root.shortname^, descr.VolIdent, ' ', ucs2);
  1329. SetBothByteOrder32(volSize, descr.VolSpaceSize);
  1330. (* escape sequences *)
  1331. IF descrType = Supplementary THEN
  1332. (* UCS-2 level 1 *)
  1333. descr.EscSeq[0] := 25X; descr.EscSeq[1] := 2FX; descr.EscSeq[2] := 40X;
  1334. END;
  1335. SetBothByteOrder16(1, descr.VolSetSize);
  1336. SetBothByteOrder16(1, descr.VolSeqNo);
  1337. SetBothByteOrder16(SectorSize, descr.BlockSize);
  1338. (* pathtable *)
  1339. SetBothByteOrder32(table.size, descr.PathTableSize);
  1340. Utils.SetLE32(table.lbaLType, descr.LocLPathTable);
  1341. Utils.SetBE32(table.lbaRType, descr.LocMPathTable);
  1342. (* root record *)
  1343. rec.Len := 22X;
  1344. SetBothByteOrder32(tree.root.lba, rec.Lba);
  1345. SetBothByteOrder32(tree.root.size, rec.Size);
  1346. SetTime(time, 0, rec.Time);
  1347. rec.Flags := FFDirectory; (* directory *)
  1348. SetBothByteOrder16(1, rec.VolSeqNo);
  1349. rec.IdentLen := 1X; rec.Ident[0] := 0X;
  1350. srcAdr := ADDRESSOF(rec); dstAdr := ADDRESSOF(descr.RootDirRecord);
  1351. SYSTEM.MOVE(srcAdr, dstAdr, ORD(rec.Len)); INC(dstAdr, 22H);
  1352. SetStringWithPadding("", descr.VolSetIdent, ' ', ucs2);
  1353. SetStringWithPadding("", descr.PubIdent, ' ', ucs2);
  1354. SetStringWithPadding("", descr.DataPrepIdent, ' ', ucs2);
  1355. SetStringWithPadding("", descr.ApplIdent, ' ', ucs2);
  1356. SetStringWithPadding("", descr.CopyRightIdent, ' ', ucs2);
  1357. SetStringWithPadding("", descr.AbstrFileIdent, ' ', ucs2);
  1358. SetStringWithPadding("", descr.BibFileIdent, ' ', ucs2);
  1359. SetStringWithPadding(dtBuf, descr.CreationTime, ' ', FALSE);
  1360. dtBuf := "000000000000000";
  1361. SetStringWithPadding(dtBuf, descr.ModificationTime, ' ', FALSE);
  1362. SetStringWithPadding(dtBuf, descr.ExpirationTime, ' ', FALSE);
  1363. SetStringWithPadding(dtBuf, descr.EffectiveTime, ' ', FALSE);
  1364. descr.FileStructVer := 1X;
  1365. END InitPSVolumeDescriptor;
  1366. PROCEDURE SetBothByteOrder16(x: INTEGER; VAR dst: ARRAY OF CHAR);
  1367. BEGIN
  1368. dst[0] := CHR(x MOD 100H);
  1369. dst[1] := CHR(x DIV 100H MOD 100H);
  1370. dst[2] := dst[1];
  1371. dst[3] := dst[0];
  1372. END SetBothByteOrder16;
  1373. PROCEDURE SetBothByteOrder32(x: LONGINT; VAR dst: ARRAY OF CHAR);
  1374. BEGIN
  1375. dst[0] := CHR(x MOD 100H);
  1376. dst[1] := CHR(x DIV 100H MOD 100H);
  1377. dst[2] := CHR(x DIV 10000H MOD 100H);
  1378. dst[3] := CHR(x DIV 1000000H MOD 100H);
  1379. dst[4] := dst[3];
  1380. dst[5] := dst[2];
  1381. dst[6] := dst[1];
  1382. dst[7] := dst[0];
  1383. END SetBothByteOrder32;
  1384. (* we set the time so that it is displayed correct in bluebottle *)
  1385. PROCEDURE SetTime(time: Dates.DateTime; ofs: LONGINT; VAR dst: ARRAY OF CHAR);
  1386. BEGIN
  1387. dst[0] := CHR(time.year - 1900);
  1388. dst[1] := CHR(time.month + 1);
  1389. dst[2] := CHR(time.day);
  1390. dst[3] := CHR(time.hour);
  1391. dst[4] := CHR(time.minute);
  1392. dst[5] := CHR(time.second);
  1393. dst[6] := CHR(ofs);
  1394. END SetTime;
  1395. PROCEDURE SetStringWithPadding(CONST id: ARRAY OF CHAR; VAR dst : ARRAY OF CHAR; chr : CHAR; ucs2: BOOLEAN);
  1396. VAR
  1397. i, len: LONGINT;
  1398. BEGIN
  1399. i := 0; len := LEN(dst);
  1400. WHILE (id[i] # 0X) & (i < len) DO
  1401. dst[i] := id[i];
  1402. INC(i);
  1403. END;
  1404. (* pad remainder with chr *)
  1405. WHILE i < len DO
  1406. dst[i] := chr;
  1407. INC(i);
  1408. END;
  1409. IF ucs2 THEN
  1410. ConvertUTF8ToUCS2(dst, dst);
  1411. END;
  1412. END SetStringWithPadding;
  1413. PROCEDURE ConvertUTF8ToUCS2(VAR src, dst: ARRAY OF CHAR);
  1414. VAR
  1415. ucs4: ARRAY MaxLen OF LONGINT;
  1416. i, len: LONGINT;
  1417. BEGIN
  1418. UTF8Strings.UTF8toUnicode(src, ucs4, len);
  1419. DEC(len);
  1420. i := 0; len := Strings.Min(len, LEN(dst) DIV 2);
  1421. FOR i := 0 TO len-1 DO
  1422. dst[2*i] := CHR(ucs4[i] DIV 100H MOD 100H);
  1423. dst[2*i+1] := CHR(ucs4[i] MOD 100H);
  1424. END;
  1425. END ConvertUTF8ToUCS2;
  1426. (* converts an identifier to a zero terminated utf8 string *)
  1427. PROCEDURE ConvertIdentToUTF8*(CONST id: ARRAY OF CHAR; len: LONGINT; ucs2: BOOLEAN; VAR str: ARRAY OF CHAR);
  1428. VAR
  1429. i, p, val: LONGINT;
  1430. b: BOOLEAN;
  1431. BEGIN
  1432. ASSERT(len <= LEN(id));
  1433. IF ucs2 THEN
  1434. b := TRUE; i := 0; p := 0;
  1435. WHILE (i < len-1) & b DO
  1436. val := ASH(ORD(id[i]), 8) + ORD(id[i+1]);
  1437. b := UTF8Strings.EncodeChar(val, str, p);
  1438. INC(i, 2)
  1439. END;
  1440. str[p] := 0X;
  1441. ELSE
  1442. WHILE (i < len) & (id[i] # 0X) DO
  1443. str[i] := id[i]; INC(i);
  1444. END;
  1445. str[i] := 0X;
  1446. END;
  1447. Strings.TrimRight(str, ' ');
  1448. END ConvertIdentToUTF8;
  1449. PROCEDURE MakeImageFromDir*(rootDir, isoDest: String; settings: IsoSettings; writeStatusChanged: Utils.StatusProc): WORD;
  1450. VAR
  1451. root: Directory;
  1452. isotree: DirectoryTree;
  1453. BEGIN
  1454. IF ~DirExists(rootDir) THEN
  1455. RETURN ErrDirNotFound;
  1456. END;
  1457. onWriteStatusChanged := writeStatusChanged;
  1458. NEW(root, NIL, settings.volumeIdent, rootDir, 0);
  1459. NEW(isotree, root, settings.isoLevel, settings.flags);
  1460. isotree.Build();
  1461. RETURN MakeImage(isotree, isoDest, settings);
  1462. END MakeImageFromDir;
  1463. PROCEDURE MakeImageFromTree*(root: Directory; isoDest: String; settings: IsoSettings; writeStatusChanged: Utils.StatusProc): WORD;
  1464. VAR
  1465. isotree: DirectoryTree;
  1466. BEGIN
  1467. onWriteStatusChanged := writeStatusChanged;
  1468. NEW(isotree, root, settings.isoLevel, settings.flags);
  1469. isotree.BuildFromTree();
  1470. RETURN MakeImage(isotree, isoDest, settings);
  1471. END MakeImageFromTree;
  1472. PROCEDURE MakeImage*(isotree: DirectoryTree; isoDest: String; settings: IsoSettings): WORD;
  1473. VAR
  1474. fOut: Files.File;
  1475. out: Files.Writer;
  1476. jtree : DirectoryTree;
  1477. isotable, jtable: PathTable;
  1478. lba, freeSpace, padding: LONGINT; res: WORD;
  1479. pdescr, sdescr: PSVolumeDescriptor;
  1480. bdescr: BootRecord;
  1481. tdescr: SetTerminator;
  1482. readOnly: BOOLEAN;
  1483. BEGIN
  1484. lba := settings.startLba + NumSystemSectors;
  1485. INC(lba, 1); (* Primary Volume descriptor *)
  1486. IF settings.joliet THEN
  1487. INC(lba, 1); (* Supplementary Volume Descriptor *)
  1488. END;
  1489. INC(lba, 1); (* Set Terminator Volume Descriptor *)
  1490. IF settings.bootCatalog # NIL THEN
  1491. INC(lba, 1); (* Boot Record *)
  1492. InitBootRecord(bdescr, lba);
  1493. INC(lba, (settings.bootCatalog.size + SectorSize - 1) DIV SectorSize);
  1494. END;
  1495. isotree.AssignFirstDirLba(lba);
  1496. INC(lba, isotree.size DIV SectorSize);
  1497. NEW(isotable, isotree);
  1498. isotable.Build();
  1499. isotable.lbaLType := lba;
  1500. INC(lba, (isotable.size + SectorSize - 1) DIV SectorSize);
  1501. isotable.lbaRType := lba;
  1502. INC(lba, (isotable.size + SectorSize - 1) DIV SectorSize);
  1503. IF settings.joliet THEN
  1504. jtree := isotree.CloneTree(Joliet);
  1505. jtree.AssignFirstDirLba(lba);
  1506. INC(lba, jtree.size DIV SectorSize);
  1507. NEW(jtable, jtree);
  1508. jtable.Build();
  1509. jtable.lbaLType := lba;
  1510. INC(lba, (jtable.size + SectorSize - 1) DIV SectorSize);
  1511. jtable.lbaRType := lba;
  1512. INC(lba, (jtable.size + SectorSize - 1) DIV SectorSize);
  1513. END;
  1514. IF settings.bootCatalog # NIL THEN
  1515. settings.bootCatalog.AssignFirstImageLba(lba);
  1516. INC(lba, settings.bootCatalog.sizeImages DIV SectorSize);
  1517. END;
  1518. isotree.AssignFirstFileLba(lba);
  1519. INC(lba, isotree.sizeFiles DIV SectorSize);
  1520. IF (lba - settings. startLba) < settings.padToSize THEN
  1521. padding := settings.padToSize - lba;
  1522. INC(lba, padding);
  1523. END;
  1524. InitPSVolumeDescriptor(pdescr, isotree, isotable, lba - settings.startLba, Primary);
  1525. IF settings.joliet THEN
  1526. InitPSVolumeDescriptor(sdescr, jtree, jtable, lba - settings.startLba, Supplementary);
  1527. END;
  1528. (* initialize volume descriptor set terminator *)
  1529. InitSetTerminator(tdescr);
  1530. (* check if there is enough space on destination volume *)
  1531. IF (Utils.IsReadOnly(isoDest^, readOnly) # ResOk) OR (Utils.GetFreeSpace(isoDest^, freeSpace) # ResOk) THEN
  1532. RETURN ErrDestinationInvalid;
  1533. END;
  1534. IF readOnly THEN
  1535. RETURN ErrDestinationReadOnly;
  1536. ELSIF ((lba - settings.startLba) * SectorSize) DIV 1024 >= freeSpace THEN
  1537. RETURN ErrNotEnoughSpace;
  1538. END;
  1539. NEW(status);
  1540. (* now write the image *)
  1541. fOut := Files.New(isoDest^);
  1542. IF fOut # NIL THEN
  1543. Files.Register(fOut);
  1544. Files.OpenWriter(out, fOut, 0);
  1545. Pad(out, NumSystemSectors*SectorSize);
  1546. WriteVolumeDescriptor(out, SYSTEM.VAL(VolumeDescriptor, pdescr));
  1547. IF settings.bootCatalog # NIL THEN
  1548. (* Boot Record must reside at sector 17 *)
  1549. WriteVolumeDescriptor(out, SYSTEM.VAL(VolumeDescriptor, bdescr));
  1550. END;
  1551. IF settings.joliet THEN
  1552. WriteVolumeDescriptor(out, SYSTEM.VAL(VolumeDescriptor, sdescr));
  1553. END;
  1554. WriteVolumeDescriptor(out, SYSTEM.VAL(VolumeDescriptor, tdescr));
  1555. IF settings.bootCatalog # NIL THEN
  1556. settings.bootCatalog.Write(out);
  1557. END;
  1558. isotree.Write(out);
  1559. isotable.Write(out, LType);
  1560. isotable.Write(out, RType);
  1561. IF settings.joliet THEN
  1562. jtree.Write(out);
  1563. jtable.Write(out, LType);
  1564. jtable.Write(out, RType);
  1565. END;
  1566. IF settings.bootCatalog # NIL THEN
  1567. res := settings.bootCatalog.WriteImages(out);
  1568. IF res # ResOk THEN RETURN res END;
  1569. END;
  1570. res := WriteFiles(out, isotree.root);
  1571. IF res # ResOk THEN RETURN res END;
  1572. Pad(out, padding*SectorSize);
  1573. END;
  1574. out.Update;
  1575. fOut.Update;
  1576. RETURN ResOk;
  1577. END MakeImage;
  1578. PROCEDURE DirExists(dir: String): BOOLEAN;
  1579. VAR
  1580. file: Files.File;
  1581. BEGIN
  1582. file := Files.Old(dir^);
  1583. IF file # NIL THEN
  1584. RETURN Files.Directory IN file.flags;
  1585. END;
  1586. RETURN FALSE;
  1587. END DirExists;
  1588. (* SaveImage is used to copy data cds *)
  1589. PROCEDURE SaveImage*(dev: ATADisks.DeviceATAPI; startsec: LONGINT; CONST dest: ARRAY OF CHAR; onWriteStatusChanged: Utils.StatusProc): WORD;
  1590. VAR
  1591. sec, size: LONGINT; res: WORD;
  1592. buf: ARRAY TransferSize*SectorSize OF CHAR;
  1593. pvd: PSVolumeDescriptor;
  1594. f: Files.File;
  1595. w: Files.Writer;
  1596. status: WritingStatus;
  1597. BEGIN
  1598. NEW(status);
  1599. sec := startsec + NumSystemSectors;
  1600. IF GetVolumeDescriptor(dev, startsec, pvd, Primary) # ResOk THEN
  1601. RETURN ResErr;
  1602. END;
  1603. size := Utils.ConvertLE32Int(pvd.VolSpaceSize);
  1604. sec := startsec;
  1605. f := Files.New(dest);
  1606. IF f # NIL THEN
  1607. Files.Register(f);
  1608. Files.OpenWriter(w, f, 0);
  1609. WHILE size > TransferSize DO
  1610. dev.Transfer(Disks.Read, sec, TransferSize, buf, 0, res);
  1611. IF res # ResOk THEN RETURN res END;
  1612. w.Bytes(buf, 0, TransferSize*SectorSize);
  1613. IF onWriteStatusChanged # NIL THEN
  1614. INC(status.bytesWritten, TransferSize*SectorSize);
  1615. onWriteStatusChanged(status);
  1616. END;
  1617. DEC(size, TransferSize); INC(sec, TransferSize);
  1618. END;
  1619. IF size > 0 THEN
  1620. dev.Transfer(Disks.Read, sec, size, buf, 0, res);
  1621. IF res # ResOk THEN RETURN res END;
  1622. w.Bytes(buf, 0, size*SectorSize);
  1623. IF onWriteStatusChanged # NIL THEN
  1624. INC(status.bytesWritten, size*SectorSize);
  1625. onWriteStatusChanged(status);
  1626. END;
  1627. END;
  1628. ELSE
  1629. RETURN ResErr;
  1630. END;
  1631. w.Update;
  1632. f.Update;
  1633. RETURN ResOk;
  1634. END SaveImage;
  1635. PROCEDURE GetVolumeDescriptor*(dev: ATADisks.DeviceATAPI; startsec: LONGINT; VAR descr: PSVolumeDescriptor; descrType: LONGINT): WORD;
  1636. VAR
  1637. type: CHAR;
  1638. sec: LONGINT; res: WORD;
  1639. tmp: ARRAY MaxLen OF CHAR;
  1640. buf: ARRAY SectorSize OF CHAR;
  1641. BEGIN
  1642. IF descrType = Primary THEN
  1643. type := 1X;
  1644. ELSE
  1645. type := 2X;
  1646. END;
  1647. sec := startsec + NumSystemSectors;
  1648. (* find descriptor in first 10 sectors following system area *)
  1649. REPEAT
  1650. dev.Transfer(Disks.Read, sec, 1, buf, 0, res);
  1651. IF res # ResOk THEN RETURN res END;
  1652. INC(sec);
  1653. UNTIL (buf[0] = type) OR (sec > startsec + NumSystemSectors + 10);
  1654. IF buf[0] = type THEN
  1655. SYSTEM.MOVE(ADDRESSOF(buf[0]), ADDRESSOF(descr), SIZEOF(PSVolumeDescriptor));
  1656. ConvertIdentToUTF8(descr.StdIdent, LEN(descr.StdIdent), FALSE, tmp);
  1657. IF tmp = ISO9660Id THEN RETURN ResOk END;
  1658. END;
  1659. RETURN ResErr;
  1660. END GetVolumeDescriptor;
  1661. PROCEDURE Make*(context : Commands.Context);
  1662. VAR
  1663. rootDir, isoDest: String;
  1664. res : WORD;
  1665. settings: IsoSettings;
  1666. BEGIN
  1667. context.arg.SkipWhitespace; context.arg.String(isoDest^);
  1668. context.arg.SkipWhitespace; context.arg.String(rootDir^);
  1669. settings.isoLevel := IsoLevel1;
  1670. settings.joliet := TRUE;
  1671. settings.volumeIdent := Strings.NewString("NEW");
  1672. IF DirExists(rootDir) THEN
  1673. res := MakeImageFromDir(rootDir, isoDest, settings, NIL);
  1674. END;
  1675. END Make;
  1676. END MakeIsoImages.
  1677. MakeIsoImages.Make TestIso.ISO Auto0:/Daten/Test/