bool AddAdditionalAssets(AssetDescSet assets) { try { DirectoryInfo directoryInfo = new DirectoryInfo(_dataFolderName); FileInfo[] fileInfos = directoryInfo.GetFiles("*", SearchOption.AllDirectories); if (fileInfos.Length <= 0) { throw new Exception("No files in \"" + _dataFolderName + "\"."); } foreach (FileInfo fileInfo in fileInfos) { string relativePath = fileInfo.FullName.Substring(_projectFolderName.Length); string extension = Path.GetExtension(relativePath); int endOfName = relativePath.Length - extension.Length; uint hashID = getSR1HashName(relativePath); // There may be more than one file with the same hash ID in the source bigfile. // Update the sizes for all of them. List <AssetDesc> existingEntries = assets.FindAll(x => x.FileHash == hashID); if (existingEntries != null) { foreach (AssetDesc existingEntry in existingEntries) { existingEntry.FileLength = (uint)fileInfo.Length; } } else { AssetDesc newEntry = new AssetDesc(); newEntry.FilePath = relativePath; newEntry.FileHash = hashID; newEntry.FileLength = (uint)fileInfo.Length; newEntry.Code.code0 = char.ToUpperInvariant(relativePath[endOfName - 4]); newEntry.Code.code1 = char.ToUpperInvariant(relativePath[endOfName - 3]); newEntry.Code.code2 = char.ToUpperInvariant(relativePath[endOfName - 2]); newEntry.Code.code3 = char.ToUpperInvariant(relativePath[endOfName - 1]); newEntry.FileIndex = assets.Count; assets.Add(newEntry); } } } catch (Exception) { assets.Clear(); Console.WriteLine("Error: Couldn't generate bigfile entries."); return(false); } return(true); }
public bool PackRepository(bool forceAllFiles = false) { if (!Directory.Exists(_dataFolderName)) { Console.WriteLine("Error: Cannot find data folder \"" + _dataFolderName + "\"."); return(false); } if (!Directory.Exists(_textureFolderName)) { Console.WriteLine("Error: Cannot find texture folder \"" + _dataFolderName + "\"."); return(false); } if (!Directory.Exists(_sfxFolderName)) { Console.WriteLine("Error: Cannot find sfx folder \"" + _sfxFolderName + "\"."); return(false); } if (!Directory.Exists(_outputFolderName)) { Console.WriteLine("Error: Cannot find output folder \"" + _outputFolderName + "\"."); return(false); } if (!File.Exists(_sourceBigfileName)) { Console.WriteLine("Error: Cannot find source bigfile \"" + _sourceBigfileName + "\"."); return(false); } if (!File.Exists(_sourceTexturesFileName)) { Console.WriteLine("Error: Cannot find source texture file \"" + _sourceTexturesFileName + "\"."); return(false); } if (!File.Exists(_assetsFileName)) { Console.WriteLine("Error: Cannot find asset file \"" + _assetsFileName + "\"."); return(false); } if (!File.Exists(_texturesFileName)) { Console.WriteLine("Error: Cannot find texture file \"" + _texturesFileName + "\"."); return(false); } try { FileStream outputBigFile = new FileStream(_outputBigFileName, FileMode.Create); FileStream outputTexturesFile = new FileStream(_outputTexturesFileName, FileMode.Create); string assetsFileData = File.ReadAllText(_assetsFileName, Encoding.ASCII); AssetDescSet assetData = (AssetDescSet)JsonSerializer.Deserialize(assetsFileData, typeof(AssetDescSet)); if (forceAllFiles && !AddAdditionalAssets(assetData)) { return(false); } string texturesFileData = File.ReadAllText(_texturesFileName, Encoding.ASCII); TexDescSet textureData = (TexDescSet)JsonSerializer.Deserialize(texturesFileData, typeof(TexDescSet)); if (forceAllFiles && !AddAdditionalTextures(textureData)) { return(false); } // Sort by index. Offset isn't saved. SortedList <int, AssetDesc> sortedAssets = new SortedList <int, AssetDesc>(); foreach (AssetDesc asset in assetData.Assets) { sortedAssets.Add(asset.FileIndex, asset); } #region Bigfile BinaryWriter bigFileWriter = new BinaryWriter(outputBigFile); bigFileWriter.Write(assetData.Count); uint fileIndexSize = 4u + (16u * (uint)assetData.Count); fileIndexSize += 0x00000800; fileIndexSize &= 0xFFFFF800; uint offset = fileIndexSize; foreach (AssetDesc entry in assetData.Assets) { bigFileWriter.Write(entry.FileHash); bigFileWriter.Write(entry.FileLength); bigFileWriter.Write(offset); bigFileWriter.Write(entry.Code.code); offset += entry.FileLength; while ((offset & 0xFFFF800) != offset) { offset++; } } while (((uint)bigFileWriter.BaseStream.Position & 0xFFFF800) != (uint)bigFileWriter.BaseStream.Position) { bigFileWriter.Write('\0'); } bigFileWriter.Flush(); foreach (AssetDesc entry in sortedAssets.Values) { string inputFileName = Path.Combine(_projectFolderName, entry.FilePath); FileStream inputFile = new FileStream(inputFileName, FileMode.Open, FileAccess.Read); CopyTo(outputBigFile, inputFile, (int)inputFile.Length); inputFile.Close(); Console.WriteLine("Added file: \"" + inputFileName + "\""); while (((uint)bigFileWriter.BaseStream.Position & 0xFFFF800) != (uint)bigFileWriter.BaseStream.Position) { bigFileWriter.Write('\0'); } bigFileWriter.Flush(); } #endregion #region Textures BinaryWriter texturesWriter = new BinaryWriter(outputTexturesFile); texturesWriter.Write((ushort)512); texturesWriter.Write((ushort)textureData.Count); texturesWriter.Write(new byte[headerLength - 4]); uint totalTextures = (uint)textureData.Count - 1; for (int t = 0; t <= totalTextures; t++) { string inputFileName = Path.Combine(_projectFolderName, textureData.Textures[t].FilePath); Bitmap tempBitmap = new Bitmap(inputFileName); WriteTexture(texturesWriter, tempBitmap); Console.WriteLine("Added file: \"" + inputFileName + "\""); } #endregion bigFileWriter.Close(); outputBigFile.Close(); Console.WriteLine("Packed " + assetData.Count.ToString() + " files into \"" + _outputBigFileName + "\"."); Console.WriteLine("Packed " + textureData.Count.ToString() + " files into \"" + _outputTexturesFileName + "\"."); } catch (Exception) { Console.WriteLine("Error: Couldn't pack the repository."); return(false); } return(true); }
public bool UnpackRepository() { if (!File.Exists(_sourceBigfileName)) { Console.WriteLine("Error: Cannot find source bigfile \"" + _sourceBigfileName + "\"."); return(false); } if (!File.Exists(_sourceTexturesFileName)) { Console.WriteLine("Error: Cannot find source textures file \"" + _sourceTexturesFileName + "\"."); return(false); } try { AssetDescSet assetData = new AssetDescSet(); LevelSet levelData = new LevelSet(); IntroSet introData = new IntroSet(); SFXClipSet clipData = new SFXClipSet(); TexDescSet textureData = new TexDescSet(); if (!LoadBigfileEntries(assetData)) { return(false); } CreateDirectories(); FileStream sourceBigFile = new FileStream(_sourceBigfileName, FileMode.Open, FileAccess.Read); FileStream sourceTexturesFile = new FileStream(_sourceTexturesFileName, FileMode.Open, FileAccess.Read); List <int> sfxIDs = new List <int>(); FileStream allClipsFile = new FileStream(_allClipsFileName, FileMode.Create, FileAccess.ReadWrite); allClipsFile.Write(new byte[16], 0, 16); #region Bigfile foreach (AssetDesc entry in assetData.Assets) { sourceBigFile.Position = entry.FileOffset; string outputFileName = Path.Combine(_projectFolderName, entry.FilePath); string outputFileDirectory = Path.GetDirectoryName(outputFileName); Directory.CreateDirectory(outputFileDirectory); FileStream outputFile = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite); CopyTo(outputFile, sourceBigFile, (int)entry.FileLength); outputFile.Flush(); string ext = Path.GetExtension(outputFileName); if (ext == ".pcm") { BinaryReader reader = new BinaryReader(outputFile, System.Text.Encoding.ASCII); reader.BaseStream.Position = 0; UInt32 dataStart = ((reader.ReadUInt32() >> 9) << 11) + 0x00000800; bool isUnit = (reader.ReadUInt32() == 0x00000000); if (isUnit) { Level level = new Level(); reader.BaseStream.Position = dataStart + 0x98; reader.BaseStream.Position = dataStart + reader.ReadUInt32(); level.UnitName = CleanName(new string(reader.ReadChars(8))); #region Events /*reader.BaseStream.Position = dataStart + 0xDC; * uint eventPointersOffset = reader.ReadUInt16(); * reader.BaseStream.Position = dataStart + eventPointersOffset; * int numPuzzles = reader.ReadInt32(); * for (int p = 0; p < numPuzzles; p++) * { * uint eventOffset = reader.ReadUInt32(); * uint nextEventPointer = (uint)reader.BaseStream.Position; * * reader.BaseStream.Position = dataStart + eventOffset; * reader.BaseStream.Position += 0x02; * * short numInstances = reader.ReadInt16(); * reader.BaseStream.Position += 0x0C; * * for (int i = 0; i < 0; i++) * { * uint instanceOffset = reader.ReadUInt32(); * uint nextInstancePointer = (uint)reader.BaseStream.Position; * * reader.BaseStream.Position = dataStart + instanceOffset; * short eventType = reader.ReadInt16(); * // Do EventInstance stuff here. * * reader.BaseStream.Position = nextInstancePointer; * } * * * reader.BaseStream.Position = nextEventPointer; * }*/ #endregion reader.BaseStream.Position = dataStart + 0xF8; level.StreamUnitID = reader.ReadInt32(); if (level.StreamUnitID > levelData.MaxID) { levelData.MaxID = level.StreamUnitID; } levelData.Add(level); #region Instances reader.BaseStream.Position = dataStart + 0x78; uint instanceCount = reader.ReadUInt32(); uint instanceStart = dataStart + reader.ReadUInt32(); for (int i = 0; i < instanceCount; i++) { Intro intro = new Intro(); reader.BaseStream.Position = instanceStart + 0x4C * i; intro.ObjectName = CleanName(new String(reader.ReadChars(16))); intro.UnitName = level.UnitName; intro.StreamUnitID = level.StreamUnitID; reader.BaseStream.Position += 4; intro.IntroUniqueID = reader.ReadInt32(); intro.Rotation.X = reader.ReadInt16(); intro.Rotation.Y = reader.ReadInt16(); intro.Rotation.Z = reader.ReadInt16(); reader.BaseStream.Position += 4; intro.Position.X = reader.ReadInt16(); intro.Position.Y = reader.ReadInt16(); intro.Position.Z = reader.ReadInt16(); if (intro.IntroUniqueID > introData.MaxID) { introData.MaxID = intro.IntroUniqueID; } introData.Add(intro); } #endregion } } else if (ext == ".pmf") { BinaryReader reader = new BinaryReader(outputFile, System.Text.Encoding.ASCII); reader.BaseStream.Position = 0; uint header = reader.ReadUInt32(); if (header == 0x61504D46 /*&& entry.FileHash == 0xf2c83bb8*/) // 0xf2c83bb8 for just raziel.pnf { reader.BaseStream.Position += 4; int clipCount = (int)reader.ReadUInt32(); reader.BaseStream.Position = 16; for (int clipNum = 0; clipNum < clipCount; clipNum++) { long clipHeaderStart = reader.BaseStream.Position; ushort sfxID = reader.ReadUInt16(); ushort waveID = reader.ReadUInt16(); reader.BaseStream.Position += 16; long currentPosition = reader.BaseStream.Position; long currentClipLength = reader.ReadUInt32() - 4; long currentClipStart = reader.BaseStream.Position + 4; long nextClipStart = currentClipStart + currentClipLength; reader.BaseStream.Position = clipHeaderStart; byte[] clipBuffer = reader.ReadBytes((int)(nextClipStart - clipHeaderStart)); SHA256 s256 = SHA256.Create(); byte[] s256Hash = s256.ComputeHash(clipBuffer); string s256String = ByteArrayToHexString(s256Hash); //string outputClipFileName = Path.Combine(_sfxFolderName, "clip-" + sfxID + "-" + waveID + "-" + s256String + ".sfx"); string outputClipFileName = Path.Combine(_sfxFolderName, "clip-" + sfxID + ".sfx"); FileStream outputClipFile = new FileStream(outputClipFileName, FileMode.Create); outputClipFile.Write(clipBuffer, 0, clipBuffer.Length); outputClipFile.Close(); if (!sfxIDs.Contains(sfxID)) { SFXClip clip = new SFXClip(); clip.SFXID = sfxID; clip.SFXName = "clip-" + sfxID; clipData.Add(clip); sfxIDs.Add(sfxID); } allClipsFile.Write(clipBuffer, 0, clipBuffer.Length); Console.WriteLine("\tExtracted clip: \"" + outputClipFileName + "\""); reader.BaseStream.Position = nextClipStart; } } } outputFile.Close(); Console.WriteLine("Extracted file: \"" + outputFileName + "\""); } #endregion #region Textures BinaryReader texturesReader = new BinaryReader(sourceTexturesFile); texturesReader.BaseStream.Position = headerLength; uint fileLength = (uint)sourceTexturesFile.Length; uint totalTextures = (uint)((fileLength - (long)headerLength) / (long)(textureWidth * textureHeight * 2) - 1); for (int t = 0; t <= totalTextures; t++) { string textureName = "texture-" + zeroFill(t.ToString(), 5) + ".png"; string outputFileName = Path.Combine(_textureFolderName, textureName); string relativePath = outputFileName.Substring(_projectFolderName.Length); TexDesc texDesc = new TexDesc(); texDesc.TextureIndex = textureData.Count; texDesc.FilePath = relativePath; textureData.Add(texDesc); Bitmap tempBitmap = ReadTexture(texturesReader); tempBitmap.Save(outputFileName, System.Drawing.Imaging.ImageFormat.Png); Console.WriteLine("Extracted file: \"" + outputFileName + "\""); } texturesReader.Close(); #endregion sourceBigFile.Close(); sourceTexturesFile.Close(); allClipsFile.Position = 0; allClipsFile.Write(BitConverter.GetBytes(0x61504D46), 0, 4); allClipsFile.Write(BitConverter.GetBytes((short)256), 0, 2); allClipsFile.Write(BitConverter.GetBytes((short)0), 0, 2); allClipsFile.Write(BitConverter.GetBytes((short)sfxIDs.Count), 0, 2); allClipsFile.Write(BitConverter.GetBytes((short)0), 0, 2); allClipsFile.Write(BitConverter.GetBytes(0x00000000), 0, 4); allClipsFile.Close(); string assetsFileData = JsonSerializer.Serialize(assetData, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_assetsFileName, assetsFileData, Encoding.ASCII); string levelsFileData = JsonSerializer.Serialize(levelData, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_levelsFileName, levelsFileData, Encoding.ASCII); string introsFileData = JsonSerializer.Serialize(introData, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_introsFileName, introsFileData, Encoding.ASCII); string clipsFileData = JsonSerializer.Serialize(clipData, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_clipsFileName, clipsFileData, Encoding.ASCII); string texturesFileData = JsonSerializer.Serialize(textureData, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_texturesFileName, texturesFileData, Encoding.ASCII); Console.WriteLine("Extracted " + assetData.Count.ToString() + " files from \"" + _sourceBigfileName + "\"."); Console.WriteLine("Extracted " + textureData.Count.ToString() + " files from \"" + _sourceTexturesFileName + "\"."); Console.WriteLine("Discovered " + levelData.Count + " unique levels."); Console.WriteLine("Discovered " + introData.Count + " unique intros."); Console.WriteLine("Discovered " + clipData.Count + " unique clips."); } catch (Exception) { Console.WriteLine("Error: Couldn't unpack the repository."); return(false); } return(true); }
bool LoadBigfileEntries(AssetDescSet assets) { if (!LoadHashTable(out Hashtable hashTable)) { return(false); } assets.Clear(); try { FileStream sourceFile = new FileStream(_sourceBigfileName, FileMode.Open, FileAccess.Read); BinaryReader reader = new BinaryReader(sourceFile); uint entryCount = reader.ReadUInt32(); while (entryCount > 0) { AssetDesc entry = new AssetDesc(); entry.FileHash = reader.ReadUInt32(); entry.FileLength = reader.ReadUInt32(); entry.FileOffset = reader.ReadUInt32(); entry.Code.code = reader.ReadUInt32(); string hashString = string.Format("{0:X8}", entry.FileHash); if (hashTable.Contains(hashString)) { entry.FilePath = (string)hashTable[hashString]; // Hack for hash collision. if (entry.FileCode == 0x444E554F) { if (entry.FileHash == 0xCA5731E8) { entry.FilePath = "kain2\\object\\alsound\\alsound.pcm"; } else if (entry.FileHash == 0xCA5731E9) { entry.FilePath = "kain2\\object\\alsound\\alsound.crm"; } } } assets.Add(entry); entryCount--; } reader.Close(); sourceFile.Close(); // Sort by offsets. SortedList <uint, AssetDesc> sortedAssets = new SortedList <uint, AssetDesc>(); foreach (AssetDesc asset in assets.Assets) { sortedAssets.Add(asset.FileOffset, asset); } // Fill indices according to their offset. int assetIndex = 0; foreach (AssetDesc asset in sortedAssets.Values) { asset.FileIndex = assetIndex; assetIndex++; } } catch (Exception) { assets.Clear(); Console.WriteLine("Error: Couldn't load bigfile entries."); return(false); } return(true); }