265 lines
6.9 KiB
C#
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();
|
|
}
|
|
}
|
|
|
|
}
|