Tool dedicated to isohacking for Xenosaga on Playstation 2
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

542 lines
20 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. using System;
  2. using System.IO;
  3. using System.Diagnostics;
  4. using System.Text;
  5. using Hack.Xenosaga.Common;
  6. namespace Hack.Xenosaga.Process
  7. {
  8. class Unpack
  9. {
  10. public enum EnumSource { ISO_FILE, PACK_DIR, UNPACK_DIR };
  11. private static BinaryWriter _bw;
  12. private const string _listExtension = ".lst";
  13. private const string _copyright = "Hacked by BahaBulle (c)2016\0";
  14. private const int _sectorSize = 0x800;
  15. private const int _maxSizeFile = 0x40000000;
  16. private struct element
  17. {
  18. public bool isDirectory;
  19. public bool isCompressed;
  20. public byte level;
  21. public string name;
  22. public UInt32 sector;
  23. public UInt32 position;
  24. public UInt32 sizeIn;
  25. public UInt32 sizeOut;
  26. }
  27. #region Private methods
  28. private static int getIdFile(UInt32 position, int indexSize)
  29. {
  30. double d = (position - indexSize) / _maxSizeFile;
  31. return (int)Math.Floor(d) + 1;
  32. }
  33. private static bool readElement(BinaryReader br, ref element e)
  34. {
  35. byte size = br.ReadByte();
  36. if (size == 0)
  37. return false;
  38. if ((size & 0x80) != 0)
  39. {
  40. size -= 2;
  41. e.isDirectory = true;
  42. e.level = br.ReadByte();
  43. e.name = Encoding.ASCII.GetString(br.ReadBytes(size & 0x7F));
  44. }
  45. else
  46. {
  47. size--;
  48. e.isDirectory = false;
  49. e.level = 0;
  50. e.name = Encoding.ASCII.GetString(br.ReadBytes(size & 0x3F));
  51. e.sector = br.ReadUInt16() + (uint)br.ReadByte() * 0x10000;
  52. e.position = e.sector * _sectorSize;
  53. e.sizeIn = br.ReadUInt32();
  54. if ((size & 0x40) != 0)
  55. {
  56. e.sizeOut = br.ReadUInt16() + (uint)br.ReadByte() * 0x10000;
  57. e.isCompressed = true;
  58. }
  59. else
  60. {
  61. e.sizeOut = e.sizeIn;
  62. e.isCompressed = false;
  63. }
  64. }
  65. return true;
  66. }
  67. private static byte readIndex(string asIndexName, listPathElement path)
  68. {
  69. byte bIndexNbSector = 0;
  70. int num = 0;
  71. int level;
  72. using (BinaryReader brIndex = new BinaryReader(File.Open(asIndexName, FileMode.Open)))
  73. {
  74. pathElement current = new pathElement(true);
  75. bIndexNbSector = brIndex.ReadByte();
  76. element e = new element();
  77. while (readElement(brIndex, ref e))
  78. {
  79. level = e.level;
  80. while (e.level-- > 0)
  81. current = current.Parent;
  82. pathElement entry = new pathElement(e.isDirectory) { Name = e.name, Position = e.position, Sector = e.sector, SizeIn = e.sizeIn, SizeOut = e.sizeOut, Id = num++, Level = level, IsCompressed = e.isCompressed };
  83. current.addEntry(entry);
  84. path.addToIndex(entry);
  85. if (e.isDirectory)
  86. current = entry;
  87. }
  88. }
  89. return bIndexNbSector;
  90. }
  91. private static void writeIndex(string as_file, byte ai_nbSector, listPathElement index)
  92. {
  93. index.SortById();
  94. using (BinaryWriter bwIndex = new BinaryWriter(File.Open(Variables.dirPack + as_file, FileMode.Create)))
  95. {
  96. bwIndex.Write(ai_nbSector);
  97. foreach (pathElement entryPath in index.getEntries())
  98. {
  99. if (entryPath.Name != "")
  100. {
  101. if (entryPath.IsDirectory)
  102. {
  103. bwIndex.Write((byte)(entryPath.Name.Length + 0x82));
  104. bwIndex.Write((byte)entryPath.Level);
  105. bwIndex.Write(entryPath.Name.ToCharArray());
  106. }
  107. else
  108. {
  109. if (entryPath.IsCompressed)
  110. bwIndex.Write((byte)(entryPath.Name.Length + 0x41));
  111. else
  112. bwIndex.Write((byte)(entryPath.Name.Length + 1));
  113. bwIndex.Write(entryPath.Name.ToCharArray());
  114. bwIndex.Write((UInt16)entryPath.Sector);
  115. bwIndex.Write((byte)(entryPath.Sector / 0x10000));
  116. bwIndex.Write((UInt32)entryPath.SizeIn);
  117. if (entryPath.IsCompressed)
  118. {
  119. bwIndex.Write((UInt16)entryPath.SizeOut);
  120. bwIndex.Write((byte)(entryPath.SizeOut / 0x10000));
  121. }
  122. }
  123. }
  124. }
  125. bwIndex.Write((byte)0);
  126. int size = (int)bwIndex.BaseStream.Length;
  127. padding(bwIndex, ref size);
  128. }
  129. }
  130. private static void padding(BinaryWriter a_bw, ref int size)
  131. {
  132. char[] hack = _copyright.ToCharArray();
  133. int id = 0;
  134. while (size % _sectorSize != 0)
  135. {
  136. a_bw.Write(hack[id++]);
  137. size++;
  138. if (id >= hack.Length)
  139. id = 0;
  140. }
  141. }
  142. private static int AddFileToStream(string pathname, ref int num, BinaryReader br, int size, bool regroup)
  143. {
  144. if (_bw == null)
  145. _bw = new BinaryWriter(File.Open(string.Format("{0}{1:00}", pathname, num), FileMode.Create));
  146. long pos = _bw.BaseStream.Position;
  147. int size_rest = _maxSizeFile - (int)_bw.BaseStream.Length;
  148. if (!regroup && size > size_rest)
  149. {
  150. _bw.Write(br.ReadBytes(size_rest), 0, size_rest);
  151. _bw.Close();
  152. _bw.Dispose();
  153. num++;
  154. _bw = new BinaryWriter(File.Open(string.Format("{0}{1:00}", pathname, num), FileMode.Create));
  155. _bw.Write(br.ReadBytes(size - size_rest), 0, size - size_rest);
  156. }
  157. else
  158. _bw.Write(br.ReadBytes(size), 0, size);
  159. padding(_bw, ref size);
  160. return size;
  161. }
  162. private static EnumSource findFile(pathElement entry, string numberFile)
  163. {
  164. // Check if the file is in the INSERT directory
  165. if (File.Exists(Variables.dirPack + entry.Name))
  166. return EnumSource.PACK_DIR;
  167. // Check if the file is in the UNPACK directory
  168. if (File.Exists(Variables.dirUnpack + numberFile + entry.FullPath))
  169. return EnumSource.UNPACK_DIR;
  170. // Otherwise, get the file from iso files
  171. return EnumSource.ISO_FILE;
  172. }
  173. #endregion
  174. #region Public methods
  175. /// <summary>
  176. /// Unpack all files from an index
  177. /// </summary>
  178. /// <param name="indexName">Pathname of the index</param>
  179. public static void unpackIsoFiles(string indexName)
  180. {
  181. BinaryReader br = null;
  182. byte bIndexNbSector = 0;
  183. int numFileIndex;
  184. int numFileOpen = -1;
  185. int iIndexSize = 0;
  186. string fileNameBase = Path.GetFileNameWithoutExtension(indexName);
  187. int.TryParse(Path.GetExtension(indexName).Substring(1), out numFileIndex);
  188. string directoryName = Variables.dirUnpack + string.Format("{0:D2}", numFileIndex);
  189. listPathElement index = new listPathElement();
  190. Trace.Write(string.Format("Reading index ({0}) : ", indexName));
  191. // Lecture de l'index
  192. try
  193. {
  194. bIndexNbSector = readIndex(indexName, index);
  195. index.SortBySector();
  196. }
  197. catch (Exception ex)
  198. {
  199. Trace.WriteLine(string.Format("ERROR : {0}", ex.Message));
  200. return;
  201. }
  202. Trace.WriteLine("OK");
  203. iIndexSize = bIndexNbSector * _sectorSize;
  204. try
  205. {
  206. Trace.WriteLine("Extracting files");
  207. Trace.Indent();
  208. foreach (pathElement entryPath in index.getEntries())
  209. {
  210. if (!entryPath.IsDirectory)
  211. {
  212. Trace.WriteLine(string.Format("Create file {0}", entryPath.FullPath));
  213. Directory.CreateDirectory(directoryName + Path.GetDirectoryName(entryPath.FullPath));
  214. int id = getIdFile(entryPath.Position, iIndexSize) + numFileIndex;
  215. double pos = (entryPath.Position - iIndexSize) % _maxSizeFile;
  216. if (numFileOpen == -1 || numFileOpen != id)
  217. {
  218. if (br != null)
  219. {
  220. br.Close();
  221. br.Dispose();
  222. }
  223. br = new BinaryReader(File.Open(string.Format("{0}.{1:D2}", fileNameBase, id), FileMode.Open));
  224. numFileOpen = id;
  225. }
  226. br.BaseStream.Seek((int)pos, SeekOrigin.Begin);
  227. int size_rest = (int)br.BaseStream.Length - (int)pos;
  228. using (BinaryWriter bw = new BinaryWriter(File.Open(directoryName + entryPath.FullPath, FileMode.Create)))
  229. {
  230. if (size_rest >= entryPath.SizeIn)
  231. bw.Write(br.ReadBytes((int)entryPath.SizeIn));
  232. else
  233. {
  234. bw.Write(br.ReadBytes(size_rest));
  235. int num = id + 1;
  236. if (br != null)
  237. {
  238. br.Close();
  239. br.Dispose();
  240. }
  241. br = new BinaryReader(File.Open(string.Format("{0}.{1:D2}", fileNameBase, num), FileMode.Open));
  242. numFileOpen = num;
  243. bw.Write(br.ReadBytes((int)entryPath.SizeIn - size_rest));
  244. }
  245. }
  246. }
  247. }
  248. }
  249. catch (Exception ex)
  250. {
  251. Trace.WriteLine(string.Format("==> ERROR : {0}", ex.Message));
  252. return;
  253. }
  254. finally
  255. {
  256. Trace.Unindent();
  257. }
  258. }
  259. /// <summary>
  260. /// Pack all files to index
  261. /// </summary>
  262. /// <param name="indexName">Pathname of the index</param>
  263. /// <param name="regroup">Used to know if the program pack files in 1 ou more files</param>
  264. public static void packIsoFiles(string indexName, bool regroup)
  265. {
  266. BinaryReader br = null;
  267. byte bIndexNbSector = 0;
  268. long l_sector = 0;
  269. long l_position = 0;
  270. int iIndexSize = 0;
  271. int numFileIndex;
  272. int numFileWrite = -1;
  273. int idSave = -1;
  274. int.TryParse(Path.GetExtension(indexName).Substring(1), out numFileIndex);
  275. string fileNameBase = Path.GetFileNameWithoutExtension(indexName);
  276. string filename = "";
  277. string directoryUnpackName = Variables.dirUnpack + string.Format("{0:D2}", numFileIndex);
  278. listPathElement index = new listPathElement();
  279. Trace.Write(string.Format("Reading index ({0}) : ", indexName));
  280. // Lecture de l'index
  281. try
  282. {
  283. bIndexNbSector = readIndex(indexName, index);
  284. index.SortBySector();
  285. }
  286. catch (Exception ex)
  287. {
  288. Trace.WriteLine(string.Format("ERROR : {0}", ex.Message));
  289. return;
  290. }
  291. l_sector += bIndexNbSector;
  292. iIndexSize = bIndexNbSector * _sectorSize;
  293. l_position += iIndexSize;
  294. numFileWrite = numFileIndex + 1;
  295. try
  296. {
  297. Trace.WriteLine("Inserting files");
  298. Trace.Indent();
  299. string s_pathname = string.Format("{0}{1}.", Variables.dirFinal, fileNameBase);
  300. Directory.CreateDirectory(Variables.dirFinal);
  301. foreach (pathElement entryPath in index.getEntries())
  302. {
  303. if (!entryPath.IsDirectory)
  304. {
  305. long size = 0;
  306. EnumSource idFile = findFile(entryPath, string.Format("{0:D2}", numFileIndex));
  307. if (idFile == EnumSource.PACK_DIR || idFile == EnumSource.UNPACK_DIR)
  308. {
  309. if (idFile == EnumSource.PACK_DIR)
  310. {
  311. Trace.WriteLine(string.Format("From {0} : file {1}", Variables.dirPack, entryPath.FullPath));
  312. filename = Variables.dirPack + entryPath.Name;
  313. }
  314. else
  315. {
  316. Trace.WriteLine(string.Format("From {0} : file {1}", Variables.dirUnpack, entryPath.FullPath));
  317. filename = directoryUnpackName + entryPath.FullPath;
  318. }
  319. using (BinaryReader brFile = new BinaryReader(File.Open(filename, FileMode.Open)))
  320. {
  321. size = brFile.BaseStream.Length;
  322. if (entryPath.IsCompressed)
  323. {
  324. // Compression du fichier
  325. entryPath.SizeIn = (UInt32)size;
  326. //entryPath.SizeOut = entryPath.SizeIn;
  327. }
  328. else
  329. {
  330. entryPath.SizeIn = (UInt32)size;
  331. //entryPath.SizeOut = entryPath.SizeIn;
  332. }
  333. int size_new = AddFileToStream(s_pathname, ref numFileWrite, brFile, (int)size, regroup);
  334. entryPath.Position = (UInt32)l_position;
  335. entryPath.Sector = entryPath.Position / _sectorSize;
  336. l_position += size_new;
  337. }
  338. }
  339. else
  340. {
  341. int id = getIdFile(entryPath.Position, iIndexSize) + numFileIndex;
  342. filename = string.Format("{0}.{1:D2}", fileNameBase, id);
  343. Trace.WriteLine(string.Format("From {0} : file {1}", filename, entryPath.FullPath));
  344. size = entryPath.SizeIn;
  345. double pos = (entryPath.Position - iIndexSize) % _maxSizeFile;
  346. int size_new = 0;
  347. if (br != null && id != idSave)
  348. {
  349. br.Close();
  350. br.Dispose();
  351. br = null;
  352. }
  353. if (br == null)
  354. {
  355. br = new BinaryReader(File.Open(filename, FileMode.Open));
  356. idSave = id;
  357. }
  358. br.BaseStream.Seek((int)pos, SeekOrigin.Begin);
  359. int size_rest = (int)br.BaseStream.Length - (int)pos;
  360. if (size_rest >= size)
  361. size_new = AddFileToStream(s_pathname, ref numFileWrite, br, (int)size, regroup);
  362. else
  363. {
  364. int sizeTemp = AddFileToStream(s_pathname, ref numFileWrite, br, size_rest, regroup);
  365. int num = id + 1;
  366. size_new = sizeTemp;
  367. if (br != null)
  368. {
  369. br.Close();
  370. br.Dispose();
  371. }
  372. filename = string.Format("{0}.{1:D2}", fileNameBase, num);
  373. br = new BinaryReader(File.Open(filename, FileMode.Open));
  374. idSave = num;
  375. sizeTemp = AddFileToStream(s_pathname, ref numFileWrite, br, (int)entryPath.SizeIn - size_rest, regroup);
  376. size_new += sizeTemp;
  377. }
  378. entryPath.Position = (UInt32)l_position;
  379. entryPath.Sector = entryPath.Position / _sectorSize;
  380. l_position += size_new;
  381. }
  382. }
  383. }
  384. _bw.Close();
  385. _bw.Dispose();
  386. writeIndex(indexName, bIndexNbSector, index);
  387. }
  388. catch (Exception ex)
  389. {
  390. Trace.WriteLine(string.Format("==> ERROR : {0}", ex.Message));
  391. return;
  392. }
  393. finally
  394. {
  395. Trace.Unindent();
  396. }
  397. }
  398. /// <summary>
  399. /// List files of an index
  400. /// </summary>
  401. /// <param name="indexName">Pathname of the index</param>
  402. public static void listFiles(string indexName)
  403. {
  404. string outputName;
  405. byte bIndexNbSector = 0;
  406. int iIndexSize;
  407. int numFile;
  408. int.TryParse(Path.GetExtension(indexName).Substring(1), out numFile);
  409. listPathElement index = new listPathElement();
  410. Trace.Write(string.Format("Reading index file ({0}) : ", indexName));
  411. try
  412. {
  413. Directory.CreateDirectory(Variables.dirUnpack);
  414. outputName = string.Format("{0}{1}{2}", Variables.dirUnpack, indexName, _listExtension);
  415. using (StreamWriter sw = new StreamWriter(outputName))
  416. {
  417. Functions.ManageListener(false, true, sw);
  418. bIndexNbSector = readIndex(indexName, index);
  419. index.SortBySector();
  420. iIndexSize = bIndexNbSector * _sectorSize;
  421. foreach (pathElement entryPath in index.getEntries())
  422. {
  423. if (!entryPath.IsDirectory)
  424. {
  425. double d = (entryPath.Position - iIndexSize) / _maxSizeFile;
  426. int id = (int)Math.Floor(d) + 1;
  427. Trace.WriteLine(string.Format("{0,-36}Sector={1,-15}SizeIn={2,-15}SizeOut={3,-15}File=xenosaga.{4:D2}", entryPath.FullPath, entryPath.Sector, entryPath.SizeIn, entryPath.SizeOut, id + numFile));
  428. }
  429. }
  430. Functions.ManageListener(true, false);
  431. }
  432. Trace.WriteLine("OK");
  433. }
  434. catch (Exception ex)
  435. {
  436. Trace.WriteLine(string.Format("ERROR : {0}", ex.Message));
  437. return;
  438. }
  439. }
  440. #endregion
  441. }
  442. }