public override byte[] ToRaw() { MemoryStream rebuildingFile = new MemoryStream(); BinaryWriter rebuildingWriter = new BinaryWriter(rebuildingFile); rebuildingWriter.Write(0x32425354); //TSB2 int currentStart = 0xC; rebuildingFile.Seek(0xC, SeekOrigin.Begin); for (int i = 0; i < subroutines.Count; i++) { rebuildingFile.Seek(4, SeekOrigin.Current); subroutine temp = (subroutine)subroutines[i]; rebuildingWriter.Write(ASCIIEncoding.UTF8.GetBytes(temp.name.PadRight(0x20, '\0'))); int headerLoc = (int)rebuildingFile.Position; rebuildingFile.Seek(0xC, SeekOrigin.Current); //rebuildingFile.Write(temp.subType); Fill this in with the rest! ArrayList localVariables = new ArrayList(); int[] opcodeLocs = new int[temp.opcodes.Count]; int writeStart = (int)rebuildingFile.Position; for (int j = 0; j < temp.opcodes.Count; j++) { opcodeLocs[j] = (int)rebuildingFile.Position - writeStart; operation tempOpcode = (operation)temp.opcodes[j]; rebuildingWriter.Write(tempOpcode.opcode); if (tempOpcode.opcode == 0x46) //Only one with an actual string argument { byte[] convertedString = ASCIIEncoding.GetEncoding("shift-jis").GetBytes(tempOpcode.strArg + '\0'); if (convertedString.Length % 4 != 0) { Array.Resize(ref convertedString, (convertedString.Length + 3) & 0xFFFFFFC); } tempOpcode.intArg = convertedString.Length / 4; rebuildingWriter.Write(tempOpcode.intArg); rebuildingWriter.Write(convertedString); } else if (opcodeTypes[tempOpcode.opcode] == 4) //These are the local variable ones! { if (!stringNames.Contains(tempOpcode.strArg)) { Array.Resize(ref stringNames, stringNames.Length + 1); stringNames[stringNames.Length - 1] = tempOpcode.strArg; } localVariables.Add(Array.IndexOf(stringNames, tempOpcode.strArg)); localVariables.Add((int)((rebuildingFile.Position - 0xC - headerLoc) / 4)); rebuildingWriter.Write((int)(-1));//Array.IndexOf(stringNames, tempOpcode.strArg)); } else if (opcodeTypes[tempOpcode.opcode] == 3) { rebuildingWriter.Write(tempOpcode.floatArg); } else if (opcodeTypes[tempOpcode.opcode] == 2) { rebuildingWriter.Write(tempOpcode.intArg); } } int localVarLoc = (int)rebuildingFile.Position; for (int j = 0; j < opcodeLocs.Length; j++) { if (((operation)temp.opcodes[j]).opcode > 0x2B && ((operation)temp.opcodes[j]).opcode < 0x2F) { int branchSource = opcodeLocs[j] + 0x8; int realDest = opcodeLocs[((operation)temp.opcodes[j]).intArg]; rebuildingFile.Seek(writeStart + opcodeLocs[j] + 0x4, SeekOrigin.Begin); rebuildingWriter.Write((int)(realDest - branchSource) >> 2); } } rebuildingFile.Seek(localVarLoc, SeekOrigin.Begin); for (int j = 0; j < localVariables.Count; j += 2) { rebuildingWriter.Write((int)localVariables[localVariables.Count - 2 - j]); rebuildingWriter.Write((int)localVariables[localVariables.Count - 1 - j]); } int nextSubLoc = (int)rebuildingFile.Position; rebuildingFile.Seek(headerLoc, SeekOrigin.Begin); rebuildingWriter.Write((int)localVarLoc - headerLoc - 0xC); rebuildingWriter.Write(temp.subType); rebuildingWriter.Write(localVariables.Count / 2); rebuildingFile.Seek(currentStart, SeekOrigin.Begin); if (i + 1 < subroutines.Count) { rebuildingWriter.Write(nextSubLoc - 0xC); } else { rebuildingWriter.Write((int)0); } rebuildingFile.Seek(nextSubLoc, SeekOrigin.Begin); currentStart = nextSubLoc; } int stringListLoc = (int)rebuildingFile.Position; for (int i = 0; i < stringNames.Length; i++) { rebuildingWriter.Write(ASCIIEncoding.UTF8.GetBytes(stringNames[i].PadRight(0x20, '\0'))); } rebuildingFile.Seek(4, SeekOrigin.Begin); rebuildingWriter.Write(stringListLoc - 0xC); rebuildingWriter.Write(stringNames.Length * 0x20); byte[] compressedFile = PrsCompDecomp.compress(rebuildingFile.ToArray()); byte[] toReturn = new byte[compressedFile.Length + 0x1C]; Array.Copy(BitConverter.GetBytes(rebuildingFile.Length), 0, toReturn, 0, 4); Array.Copy(BitConverter.GetBytes(compressedFile.Length), 0, toReturn, 4, 4); Array.Copy(compressedFile, 0, toReturn, 0x1C, compressedFile.Length); return(toReturn); }
public ScriptFile(string inFilename, byte[] rawData, bool bigEndian = false) { filename = inFilename; int inFilesize, outFilesize; if (!bigEndian) { outFilesize = BitConverter.ToInt32(rawData, 0); inFilesize = BitConverter.ToInt32(rawData, 4); } else { byte[] outFilesizeBytes = new byte[] { rawData[3], rawData[2], rawData[1], rawData[0] }; byte[] inFilesizeBytes = new byte[] { rawData[7], rawData[6], rawData[5], rawData[4] }; if (BitConverter.ToUInt32(inFilesizeBytes, 0) > rawData.Length) { //Stupid hack to try to fix tobitama outFilesize = BitConverter.ToInt32(outFilesizeBytes.Reverse().ToArray(), 0); inFilesize = BitConverter.ToInt32(inFilesizeBytes.Reverse().ToArray(), 0); } else { outFilesize = BitConverter.ToInt32(outFilesizeBytes, 0); inFilesize = BitConverter.ToInt32(inFilesizeBytes, 0); } } byte[] temp = new byte[inFilesize]; Array.Copy(rawData, 0x1C, temp, 0, inFilesize); byte[] decompressed = PrsCompDecomp.Decompress(temp, (uint)outFilesize); MemoryStream scriptStream = new MemoryStream(decompressed); BinaryReader scriptReader; if (bigEndian) { scriptReader = new BigEndianBinaryReader(scriptStream); } else { scriptReader = new BinaryReader(scriptStream); } scriptStream.Seek(4, SeekOrigin.Begin); int stringListPtr = scriptReader.ReadInt32(); int stringListLength = scriptReader.ReadInt32(); scriptStream.Seek(stringListPtr + 0xC, SeekOrigin.Begin); stringNames = new string[stringListLength / 0x20]; for (int i = 0; i < stringNames.Length; i++) { stringNames[i] = ASCIIEncoding.UTF8.GetString(scriptReader.ReadBytes(0x20)); stringNames[i] = stringNames[i].TrimEnd('\0'); } scriptStream.Seek(0xC, SeekOrigin.Begin); //ArrayList subroutines = new ArrayList(); while (scriptStream.Position < stringListPtr) { int nextSubLoc = scriptReader.ReadInt32() + 0xC; string subroutineName = ASCIIEncoding.UTF8.GetString(scriptReader.ReadBytes(0x20)).TrimEnd('\0'); int subLength = scriptReader.ReadInt32(); int subType = scriptReader.ReadLittleEndianInt32(); int locals = scriptReader.ReadInt32(); int currLoc = (int)scriptStream.Position; int subEnd = currLoc + subLength; scriptStream.Seek(subLength, SeekOrigin.Current); int[] localNums = new int[locals]; int[] localLocs = new int[locals]; if (subType == 0x4C) { for (int i = 0; i < locals; i++) { localNums[i] = scriptReader.ReadInt32(); localLocs[i] = scriptReader.ReadInt32() * 4; } } List <operation> operations = new List <operation>(); List <int> opcodeLocs = new List <int>(); scriptStream.Seek(currLoc, SeekOrigin.Begin); while (scriptStream.Position < subEnd) { opcodeLocs.Add((int)(scriptStream.Position - currLoc)); int currOpcode = scriptReader.ReadInt32(); operation tempOp = new operation(); if (opcodeTypes[currOpcode] == 3) { tempOp.floatArg = scriptReader.ReadSingle(); } else if (opcodeTypes[currOpcode] > 1 && opcodeTypes[currOpcode] != 4 && !localLocs.Contains((int)(scriptStream.Position - currLoc))) { tempOp.intArg = scriptReader.ReadInt32(); } else if (localLocs.Contains((int)(scriptStream.Position - currLoc))) { tempOp.strArg = stringNames[localNums[Array.IndexOf(localLocs, (int)(scriptStream.Position - currLoc))]]; tempOp.intArg = scriptReader.ReadInt32(); } if (opcodeTypes[currOpcode] == 99) { tempOp.strArg = ASCIIEncoding.GetEncoding("shift-jis").GetString(scriptReader.ReadBytes(tempOp.intArg * 4)).TrimEnd('\0'); } tempOp.opcode = currOpcode; operations.Add(tempOp); } for (int i = 0; i < operations.Count; i++) { if (((operation)operations[i]).opcode > 0x2B && ((operation)operations[i]).opcode < 0x2F) { int branchSource = opcodeLocs[i] + 0x8; int branchDestRaw = branchSource + 4 * ((operation)operations[i]).intArg; for (int j = 0; j < operations.Count; j++) { if (branchDestRaw == opcodeLocs[j]) { ((operation)operations[i]).intArg = j; break; } } } } subroutine tempSub = new subroutine(); tempSub.name = subroutineName; tempSub.subType = subType; if (subType != 0x4C) { tempSub.miscData = locals; } else { tempSub.miscData = -1; } tempSub.opcodes = operations; subroutines.Add(tempSub); if (nextSubLoc == 0xC) { break; } scriptStream.Seek(nextSubLoc, SeekOrigin.Begin); } scriptReader.Close(); }
private NblChunk loadGroup(Stream fileToLoad, BinaryReader fileLoader) { NblChunk toRet = new NblChunk(); toRet.bigEndian = isBigEndian; long offset = fileToLoad.Position; string formatName = new String(fileLoader.ReadChars(4)); ushort fileVersion = fileLoader.ReadUInt16(); int paddingAmount = fileVersion == 0x1002 ? 0x3F : 0x7FF; uint mask = fileVersion == 0x1002 ? 0xFFFFFFC0 : 0xFFFFF800; ushort chunkFilenameLength = fileLoader.ReadUInt16(); int headerSize = fileLoader.ReadInt32(); int numFiles = fileLoader.ReadInt32(); uint uncompressedSize = fileLoader.ReadUInt32(); uint compressedSize = fileLoader.ReadUInt32(); uint pointerLength = fileLoader.ReadUInt32() / 4; if (formatName.StartsWith("NML")) { decryptKey = fileLoader.ReadUInt32(); } else { fileLoader.ReadUInt32(); } uint size = compressedSize == 0 ? uncompressedSize : compressedSize; uint nmllDataLoc = (uint)((headerSize + paddingAmount) & mask); uint pointerLoc = (uint)(nmllDataLoc + size + paddingAmount) & mask; uint mainHeaderSize = 0x20; if (formatName.StartsWith("NML")) { mainHeaderSize = 0x30; uint tmllHeaderSize = fileLoader.ReadUInt32(); uint tmllDataSizeUncomp = fileLoader.ReadUInt32(); uint tmllDataSizeComp = fileLoader.ReadUInt32(); uint tmllCount = fileLoader.ReadUInt32(); if (tmllCount > 0) { tmllHeaderLoc = (uint)(pointerLoc + pointerLength * 4 + paddingAmount) & mask; } } decryptor = new BlewFish(decryptKey, isBigEndian); FileHeader[] groupHeaders = new FileHeader[numFiles]; for (int i = 0; i < numFiles; i++) { fileToLoad.Seek(mainHeaderSize + 0x60 * i + offset, SeekOrigin.Begin); FileHeader currentHeader = readHeader(fileLoader.ReadBytes(0x60)); groupHeaders[i] = currentHeader; } fileToLoad.Seek(nmllDataLoc + offset, SeekOrigin.Begin); int encryptedSectionSize; if (fileVersion == 0x1002) { //Obfuscation stuff from sega. int rawEncryptedSectionSize = (int)((((compressedSize >> 0xB) ^ compressedSize) & 0xE0) + 0x20); encryptedSectionSize = Math.Min(rawEncryptedSectionSize, (int)compressedSize); } else { encryptedSectionSize = (int)size; } if (encryptedSectionSize % 8 != 0) { encryptedSectionSize -= (encryptedSectionSize % 8); } byte[] decryptedFiles; if (decryptKey != 0 && formatName.StartsWith("NML")) { byte[] encryptedFiles = fileLoader.ReadBytes((int)size); decryptedFiles = decryptor.decryptBlock(encryptedFiles, encryptedSectionSize); } else { decryptedFiles = fileLoader.ReadBytes((int)size + 7); } if (compressedSize != 0) { if (fileVersion == 0x1002) { DeflateStream ds = new DeflateStream(new MemoryStream(decryptedFiles), CompressionMode.Decompress); MemoryStream decompressedStream = new MemoryStream((int)uncompressedSize); ds.CopyTo(decompressedStream); decompressedFiles = decompressedStream.ToArray(); } else { decompressedFiles = PrsCompDecomp.Decompress(decryptedFiles, uncompressedSize); } } else { decompressedFiles = decryptedFiles; } List <int> pointers = new List <int>((int)pointerLength); if (pointerLength > 0) { fileToLoad.Seek(pointerLoc + offset, SeekOrigin.Begin); for (int i = 0; i < pointerLength; i++) { pointers.Add(fileLoader.ReadInt32()); } } List <RawFile> files = new List <RawFile>(numFiles); for (int i = 0; i < numFiles; i++) { RawFile tempFile = new RawFile(); tempFile.filename = groupHeaders[i].fileName; tempFile.subHeader = groupHeaders[i].subHeader; tempFile.chunkSize = groupHeaders[i].chunkSize; tempFile.fileOffset = groupHeaders[i].filePosition; tempFile.fileContents = new byte[groupHeaders[i].fileSize]; Array.Copy(decompressedFiles, groupHeaders[i].filePosition, tempFile.fileContents, 0, groupHeaders[i].fileSize); if (tempFile.fileContents.Length > 0) { tempFile.fileheader = new String(ASCIIEncoding.ASCII.GetChars(tempFile.fileContents, 0, 4)); } if (groupHeaders[i].pointerSize > 0) { tempFile.pointers = pointers.GetRange((int)groupHeaders[i].pointerPosition / 4, (int)groupHeaders[i].pointerSize / 4); } files.Add(tempFile); } toRet.encryptionKey = decryptKey; toRet.encrypted = decryptKey != 0; toRet.compressed = compressedSize != 0; toRet.chunkID = formatName; toRet.versionNumber = (short)fileVersion; toRet.fileContents = files; return(toRet); }
public byte[] SaveFile(bool discardChanges) { //First, combine all the files. MemoryStream groupFileStream = new MemoryStream(); MemoryStream groupHeaderStream = new MemoryStream(); BinaryWriter groupHeaderWriter; if (bigEndian) { groupHeaderWriter = new BigEndianBinaryWriter(groupHeaderStream); } else { groupHeaderWriter = new BinaryWriter(groupHeaderStream); } int paddingAmount = versionNumber == 0x1002 ? 0x3F : 0x7FF; uint mask = versionNumber == 0x1002 ? 0xFFFFFFC0 : 0xFFFFF800; if (this.chunkID.StartsWith("NML")) { groupHeaderStream.Seek(0x30, SeekOrigin.Begin); } else if (this.chunkID.StartsWith("TML")) { groupHeaderStream.Seek(0x20, SeekOrigin.Begin); } NblLoader.FileHeader[] headers = new NblLoader.FileHeader[this.fileContents.Count]; List <RawFile> savedFiles = new List <RawFile>(this.fileContents); List <int> pointers = new List <int>(); //Annoying, this one has to be a running tally, can't do it any other way. ushort filenamelength = 0; for (int i = 0; i < fileContents.Count; i++) { //Figure out whether to take the cached copy or the original if (this.loadedFileCache.ContainsKey(fileContents[i].filename) && !discardChanges) { savedFiles[i] = loadedFileCache[fileContents[i].filename].ToRawFile((uint)groupFileStream.Position); savedFiles[i].chunkSize = fileContents[i].chunkSize; } else if (savedFiles[i].fileOffset != (uint)groupFileStream.Position) { savedFiles[i].RebaseFile((uint)groupFileStream.Position); } //Let's not use a FileHeader any more. Just put all the data straight into the file. //Guessing on identifier--this SHOULD be true, generally, but...? //This needs to be stored in the file class. string identifier = "STD\0"; if (this.chunkID.StartsWith("TML")) { identifier = "NNVR"; } else if (savedFiles[i].subHeader != null && savedFiles[i].subHeader[0] == 0x4E) //'N'--so hopefully just NXIF or NUIF { identifier = Path.GetExtension(savedFiles[i].filename).Substring(1).ToUpper().PadRight(4, '\0'); } groupHeaderWriter.Write(ASCIIEncoding.ASCII.GetBytes(identifier)); groupHeaderWriter.Write(savedFiles[i].chunkSize); groupHeaderWriter.Write((int)0); //"unknown1" groupHeaderWriter.Write((int)0); //"unknown2" groupHeaderWriter.Write(ASCIIEncoding.ASCII.GetBytes(savedFiles[i].filename.PadRight(0x20, '\0'))); groupHeaderWriter.Write(savedFiles[i].fileOffset); groupHeaderWriter.Write(savedFiles[i].fileContents.Length); groupHeaderWriter.Write(pointers.Count * 4); groupHeaderWriter.Write(savedFiles[i].pointers.Count * 4); if (savedFiles[i].subHeader == null) { groupHeaderWriter.Write(new byte[0x20]); } else { groupHeaderWriter.Write(savedFiles[i].subHeader); } //Update filename length (include \0 terminator). if (this.chunkID.StartsWith("NML")) { filenamelength += (ushort)(savedFiles[i].filename.Length + 1); } //Now put the data into the file pieces. groupFileStream.Write(savedFiles[i].fileContents, 0, savedFiles[i].fileContents.Length); //Padding out to nearest 0x10 groupFileStream.Seek((int)(groupFileStream.Position + 0x1F) & 0xFFFFFFE0, SeekOrigin.Begin); pointers.AddRange(savedFiles[i].pointers); } int headerLength = (int)groupHeaderStream.Position; groupHeaderStream.Seek((groupHeaderStream.Position + paddingAmount) & mask, SeekOrigin.Begin); int uncompressedSize = (int)groupFileStream.Position; byte[] rawData; if (compressed) { if (versionNumber == 0x1002) // PSP2 files use Deflate. { groupFileStream.Seek(0, SeekOrigin.Begin); MemoryStream compressedStream = new MemoryStream(); using (DeflateStream ds = new DeflateStream(compressedStream, CompressionMode.Compress)) { groupFileStream.CopyTo(ds); } rawData = compressedStream.ToArray(); } else //PSU uses PRS. { rawData = PrsCompDecomp.compress(groupFileStream.ToArray()); } } else { rawData = groupFileStream.ToArray(); } int fileLength = rawData.Length; groupHeaderWriter.Write(rawData); //Write out pointers (if applicable) groupHeaderStream.Seek((groupHeaderStream.Position + paddingAmount) & mask, SeekOrigin.Begin); for (int i = 0; i < pointers.Count; i++) { groupHeaderWriter.Write(pointers[i]); } groupHeaderWriter.Write(new byte[((groupHeaderStream.Position + paddingAmount) & mask) - groupHeaderStream.Position]); //Now fill in the header (leaving the space if necessary). groupHeaderStream.Seek(0, SeekOrigin.Begin); groupHeaderWriter.Write(ASCIIEncoding.ASCII.GetBytes(this.chunkID)); groupHeaderWriter.Write(this.versionNumber); groupHeaderWriter.Write(filenamelength); groupHeaderWriter.Write(headerLength); groupHeaderWriter.Write(this.fileContents.Count); groupHeaderWriter.Write(uncompressedSize); if (compressed) { groupHeaderWriter.Write(rawData.Length); } else { groupHeaderWriter.Write((int)0); } groupHeaderWriter.Write(pointers.Count * 4); groupHeaderWriter.Write((int)0); //Still enforcing no encryption. return(groupHeaderStream.ToArray()); }
private void decryptNMLBToolStripMenuItem_Click(object sender, EventArgs e) { if (openFileDialog1.ShowDialog() == DialogResult.OK) { byte[] fileContents = File.ReadAllBytes(openFileDialog1.FileName); MemoryStream fileStream = new MemoryStream(fileContents); fileStream.Seek(3, SeekOrigin.Begin); byte endian = (byte)fileStream.ReadByte(); fileStream.Seek(0, SeekOrigin.Begin); BinaryReader fileLoader; bool bigEndian = false; if (endian == 0x42) { fileLoader = new BigEndianBinaryReader(fileStream); bigEndian = true; } else { fileLoader = new BinaryReader(fileStream); } string formatName = new String(fileLoader.ReadChars(4)); ushort fileVersion = fileLoader.ReadUInt16(); ushort chunkFilenameLength = fileLoader.ReadUInt16(); uint headerSize = fileLoader.ReadUInt32(); uint nmllCount = fileLoader.ReadUInt32(); uint uncompressedSize = fileLoader.ReadUInt32(); uint compressedSize = fileLoader.ReadUInt32(); uint pointerLength = fileLoader.ReadUInt32() / 4; uint blowfishKey = fileLoader.ReadUInt32(); uint tmllHeaderSize = fileLoader.ReadUInt32(); uint tmllDataSizeUncomp = fileLoader.ReadUInt32(); uint tmllDataSizeComp = fileLoader.ReadUInt32(); uint tmllCount = fileLoader.ReadUInt32(); uint tmllHeaderLoc = 0; uint pointerLoc = 0; uint size = compressedSize == 0 ? uncompressedSize : compressedSize; uint nmllDataLoc = (uint)((headerSize + 0x7FF) & 0xFFFFF800); pointerLoc = (uint)(nmllDataLoc + size + 0x7FF) & 0xFFFFF800; if (tmllCount > 0) { tmllHeaderLoc = (pointerLoc + pointerLength * 4 + 0x7FF) & 0xFFFFF800; } BlewFish fish = new BlewFish(blowfishKey, bigEndian); for (int i = 0; i < nmllCount; i++) { int headerLoc = 0x40 + i * 0x60; byte[] toDecrypt = new byte[0x30]; Array.Copy(fileContents, headerLoc, toDecrypt, 0, 0x30); toDecrypt = fish.decryptBlock(toDecrypt); Array.Copy(toDecrypt, 0, fileContents, headerLoc, 0x30); } StringBuilder sb = new StringBuilder(); for (int i = 0; i < tmllCount; i++) { uint headerLoc = (uint)(tmllHeaderLoc + 0x30 + i * 0x60); byte[] toDecrypt = new byte[0x30]; Array.Copy(fileContents, headerLoc, toDecrypt, 0, 0x30); toDecrypt = fish.decryptBlock(toDecrypt); Array.Copy(toDecrypt, 0, fileContents, headerLoc, 0x30); sb.Append(ASCIIEncoding.ASCII.GetString(toDecrypt, 0, 0x20).Split('\0')[0] + "\t"); //sb.Append(BitConverter.ToUInt16(fileContents, (int)(headerLoc + 0x4C)) + "\t"); //sb.Append(BitConverter.ToUInt16(fileContents, (int)(headerLoc + 0x4E)) + "\n"); } fileStream.Seek(nmllDataLoc, SeekOrigin.Begin); byte[] encryptedNmll = fileLoader.ReadBytes((int)size); byte[] decryptedNmll = fish.decryptBlock(encryptedNmll); byte[] decompressedNmll = compressedSize != 0 ? PrsCompDecomp.Decompress(decryptedNmll, uncompressedSize) : decryptedNmll; File.WriteAllText(openFileDialog1.FileName + ".tml.list", sb.ToString()); File.WriteAllBytes(openFileDialog1.FileName + ".decrypt", fileContents); File.WriteAllBytes(openFileDialog1.FileName + ".encryptNmll", encryptedNmll); File.WriteAllBytes(openFileDialog1.FileName + ".decryptNmll", decryptedNmll); File.WriteAllBytes(openFileDialog1.FileName + ".decompressNmll", decompressedNmll); } }