265 lines
7.1 KiB
C#
265 lines
7.1 KiB
C#
using Ionic.Zlib;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace RMDEC
|
|
{
|
|
public class VXAProject
|
|
{
|
|
public VXAProject() { }
|
|
|
|
//Private Variables
|
|
private static int[] supportedVersions = { 0x3 };
|
|
private int currentVersion = 0;
|
|
private byte[] encryptionKey = new byte[0x4];
|
|
|
|
private string gameTitle;
|
|
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 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)
|
|
{
|
|
byte[] output = new byte[input.Length];
|
|
|
|
uint keyInt = BitConverter.ToUInt32(keydata, 0x00);
|
|
|
|
for (int i = 0; i < input.Length; i++)
|
|
{
|
|
if (i != 0 && i % 4 == 0)
|
|
{
|
|
// Derive new key
|
|
keyInt *= 7;
|
|
keyInt += 3;
|
|
keydata = BitConverter.GetBytes(keyInt);
|
|
}
|
|
|
|
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);
|
|
string gameExeName = Path.GetFileNameWithoutExtension(file);
|
|
string iniFilePath = Path.Combine(workDir, gameExeName + ".ini");
|
|
|
|
vxp.rgss3aStream = rgss3a;
|
|
try
|
|
{
|
|
vxp.gameTitle = read_ini_value(iniFilePath, "Game", "Title", gameExeName);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
vxp.gameTitle = gameExeName;
|
|
}
|
|
|
|
vxp.filePath = workDir;
|
|
|
|
rgss3a.Seek(0x1, SeekOrigin.Current);
|
|
int version = rgss3a.ReadByte();
|
|
|
|
|
|
if(!supportedVersions.Contains(version))
|
|
{
|
|
throw new InvalidDataException("Unsupported version!");
|
|
}
|
|
|
|
vxp.currentVersion = version;
|
|
vxp.encryptionKey = BitConverter.GetBytes((((vxp.readUInt32()) * 9) + 3));
|
|
|
|
|
|
return vxp;
|
|
}
|
|
|
|
|
|
public void DecryptFile(int fileIndex, Stream outStream)
|
|
{
|
|
|
|
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 += 0x40000000)
|
|
{
|
|
|
|
if (size >= 0x40000000) //1GB
|
|
{
|
|
byte[] gameData = new byte[0x40000000];
|
|
rgss3aStream.Read(gameData, 0x00, 0x40000000);
|
|
gameData = decryptFileData(gameData, keyData);
|
|
//gameData = ZlibStream.UncompressBuffer(gameData);
|
|
outStream.Write(gameData, 0x00, (int)size);
|
|
}
|
|
else
|
|
{
|
|
byte[] gameData = new byte[size];
|
|
rgss3aStream.Read(gameData, 0x00, (int)size);
|
|
gameData = decryptFileData(gameData, keyData);
|
|
//gameData = ZlibStream.UncompressBuffer(gameData);
|
|
outStream.Write(gameData, 0x00, (int)size);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
}
|