using System.Runtime.CompilerServices; using System.Xml.Linq; //File table parsing algorithm: https://github.com/mbystryantsev/consolgames-tools/blob/master/game-specific/Silent%20Hill/FilesExtractor/Silent.cpp namespace LibCD { class App { static string CalledFileDir([CallerFilePath] string path = null) { return System.IO.Path.GetDirectoryName(path); } static string TestDataDir { get { return Path.Combine(CalledFileDir(), "..", "TestData"); } } static string ImageFilePath { get { return Path.Combine(TestDataDir, "Image.bin"); } } static void PrintFileSystem(ISO9660.FileSystem.Directory dir, string fullPath) { fullPath += dir.Name; Console.WriteLine(fullPath); foreach (var file in dir.Files) { Console.WriteLine($"{fullPath}/{file.Name} | LBA: {file.LBA} SIZE: {file.Size}"); } foreach (var childDir in dir.Directories) { PrintFileSystem(childDir, fullPath + "/"); } } public static ISO9660.FileSystem.File? FindFile(ISO9660.FileSystem fs, string name) { var dir = fs.RootDirectory; foreach (var file in dir.Files) { if (file.Name.StartsWith(name)) { return file; } } return null; } struct ShFileRecord { public uint b1; public uint b2; public uint b3; public uint StartSector { get { return b1 & 0x7ffff; } } public uint ChunkCount { get { return b1 >> 19; } } public uint DirectoryIndex { get { return b2 & 0xf; } } public uint Name0 { get { return (b2 >> 4) & 0xffffff; } } public uint Dummy { get { return (b2 >> 28); } } public uint Name1 { get { return b3 & 0xffffff; } } public uint ExtensionIndex { get { return b3 >> 24; } } public string Name { get { string result = ""; uint[] values = new uint[] { Name0, Name1 }; for (int j = 0; j < 2; j++) { uint v = values[j]; for (int i = 0; i < 4 && v != 0; i++) { byte c = (byte)((v & 0x3F) + 0x20); result += (char)c; v >>= 6; } } return result; } } public string Directory { get { return DefaultDirectoryStruct[DirectoryIndex]; } } public string Extension { get { return DefaultExtensions[DirectoryIndex]; } } public string Path { get { return System.IO.Path.Combine(Directory, $"{Name}{Extension}"); } } public uint Size { get { return ChunkCount * 0x100; } } } static string[] DefaultExtensions = new string[16] { ".TIM", ".VAB", ".BIN", ".DMS", ".ANM", ".PLM", ".IPD", ".ILM", ".TMD", ".DAT", ".KDT", ".CMP", ".TXT", "", "", "" }; static string[] DefaultDirectoryStruct = new string[16] { "1ST", "ANIM", "BG", "CHARA", "ITEM", "MISC", "SND", "TEST", "TIM", "VIN", "XA", "", "", "", "", "" }; public static void Main() { Console.WriteLine(ImageFilePath); var fs = new ISO9660.FileSystem(File.OpenRead(ImageFilePath)); var xaDiscLabel = fs.PrimaryVolumeDescriptor.GetXADiscLabel(); var exeFile = FindFile(fs, "SLUS_007.07"); var dataFile = FindFile(fs, "SILENT"); var exeFileStream = exeFile.OpenRead(fs.SectorStream); exeFileStream.Seek(0xB91C, SeekOrigin.Current); List fileRecords = new List(); for(int i = 0; i < 2074; ++i) { fileRecords.Add(LibCD.Serialization.StreamReader.ReadStruct(exeFileStream)); } string exportDirectory = @"E:\CdContent"; foreach (var file in fileRecords) { fs.SectorStream.SeekToSector(file.StartSector); var srcFileStream = new CdByteStream(fs.SectorStream); byte[] data = srcFileStream.Read((int)file.Size); string outPath = Path.Combine(exportDirectory, file.Path); string outDir = Path.GetDirectoryName(outPath); Directory.CreateDirectory(outDir); File.WriteAllBytes(outPath, data); Console.WriteLine($"{file.Path}"); } // Console.WriteLine("Done"); } } }