using System; using System.Collections.Generic; using System.IO; using System.Text; namespace RMDEC { public class VXAProject { public VXAProject() { } //Private Variables private byte[] encryptionKey = new byte[0x4]; private string gameTitle; private string exeName; private string filePath; private Stream rgss3aStream; private struct ArchiveFile { public UInt32 Offset; public UInt32 Size; public Byte[] Key; public String Name; } private List archiveFileList = new List(); //Public Variables public List FileList = new List(); public String GameTitle { get { return gameTitle; } } public String ExeName { get { return exeName; } } public String FilePath { get { return filePath; } } public byte[] EncryptionKey { get { return encryptionKey; } } //Private Functions private uint readUInt32() { byte[] intBytes = new byte[0x4]; rgss3aStream.Read(intBytes, 0x00, 0x4); return BitConverter.ToUInt32(intBytes, 0x00); } private byte[] decryptData(uint size) { byte[] data = new byte[size]; rgss3aStream.Read(data, 0x00, data.Length); return RMProject.Xor(data, encryptionKey); } private uint decryptUint32() { byte[] intBytes = decryptData(0x4); return BitConverter.ToUInt32(intBytes, 0x00); } private string decryptString(uint length) { byte[] strBytes = decryptData(length); return Encoding.UTF8.GetString(strBytes); } private static string read_ini_value(string iniFile, string column,string key,string defaultValue="") { string[] lines = File.ReadAllLines(iniFile); string value = defaultValue; bool inColumn = false; foreach(string line in lines) { if (line == ("[" + column + "]")) { inColumn = true; continue; } if(!inColumn) { continue; } string[] iniKeyValue = line.Split('='); if (iniKeyValue[0] == key) { value = iniKeyValue[1]; break; } if(line.StartsWith("[")) { break; } } return value; } private byte[] decryptFileData(byte[] input, byte[] keydata) { long size = input.LongLength; byte[] output = new byte[size]; uint keyInt = BitConverter.ToUInt32(keydata, 0x00); for (int i = 0; i < size; i++) { if (i != 0 && i % 4 == 0) { // Derive new key keyInt = ((keyInt * 7) + 3); byte[] derivedKeyBytes = BitConverter.GetBytes(keyInt); Array.Copy(derivedKeyBytes, keydata, 0x4); // have to do this in order to make the arguments update.. } output[i] = (byte)(input[i] ^ keydata[i % keydata.Length]); } return output; } //Public Functions public static VXAProject ParseRgss3a(string file) { VXAProject vxp = new VXAProject(); FileStream rgss3a = File.OpenRead(file); byte[] magic = new byte[0x06]; rgss3a.Read(magic, 0x00, 0x06); string magicStr = Encoding.UTF8.GetString(magic); if (magicStr != "RGSSAD") { throw new InvalidDataException("Not a valid rgss3a!"); } string workDir = Path.GetDirectoryName(file); vxp.exeName = Path.GetFileNameWithoutExtension(file); string iniFilePath = Path.Combine(workDir, vxp.exeName + ".ini"); vxp.rgss3aStream = rgss3a; try { vxp.gameTitle = read_ini_value(iniFilePath, "Game", "Title", vxp.exeName); } catch (Exception) { vxp.gameTitle = vxp.exeName; } vxp.filePath = workDir; rgss3a.Seek(0x2, SeekOrigin.Current); vxp.encryptionKey = BitConverter.GetBytes((((vxp.readUInt32()) * 9) + 3)); return vxp; } public void DecryptFile(int fileIndex, Stream outStream) { outStream.SetLength(0); ArchiveFile fileData = archiveFileList[fileIndex]; byte[] keyData = fileData.Key; rgss3aStream.Seek(fileData.Offset, SeekOrigin.Begin); uint size = fileData.Size; for(int i = 0; i < size; i += 0x20000000) { if (size > 0x20000000) //512MB { byte[] gameData = new byte[0x20000000]; rgss3aStream.Read(gameData, 0x00, 0x20000000); gameData = decryptFileData(gameData, keyData); outStream.Write(gameData, 0x00, gameData.Length); } else { byte[] gameData = new byte[size]; rgss3aStream.Read(gameData, 0x00, gameData.Length); gameData = decryptFileData(gameData, keyData); outStream.Write(gameData, 0x00, gameData.Length); } } } public void PopulateFileList() { archiveFileList.Clear(); FileList.Clear(); while (true) { uint offset = decryptUint32(); if (offset == 0x00) { break; } ArchiveFile file; file.Offset = offset; file.Size = decryptUint32(); file.Key = decryptData(0x4); uint nameLen = decryptUint32(); file.Name = decryptString(nameLen); archiveFileList.Add(file); FileList.Add(file.Name); } } public void Close() { rgss3aStream.Close(); rgss3aStream.Dispose(); FileList.Clear(); archiveFileList.Clear(); } } }