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

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 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. }