rmdec/RMDEC/VXAProject.cs

265 lines
6.9 KiB
C#

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<ArchiveFile> archiveFileList = new List<ArchiveFile>();
//Public Variables
public List<String> FileList = new List<string>();
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();
}
}
}