249 lines
7.0 KiB
C#
249 lines
7.0 KiB
C#
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace RMDEC
|
|
{
|
|
public class MVProject
|
|
{
|
|
public MVProject() { }
|
|
|
|
//Private internal variables
|
|
private byte[] encryptionKey = new byte[0x10];
|
|
|
|
private bool isKeySet = false;
|
|
private bool encryptedImages = false;
|
|
private bool encryptedAudio = false;
|
|
|
|
private string gameTitle;
|
|
private string filePath;
|
|
private string systemJsonFile;
|
|
private dynamic jsonData;
|
|
|
|
//Public readable variables
|
|
public Boolean IsEncrypted
|
|
{
|
|
get
|
|
{
|
|
if (EncryptedAudio == true || EncryptedImages == true)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public Byte[] EncryptionKey
|
|
{
|
|
get
|
|
{
|
|
if(isKeySet)
|
|
{
|
|
return encryptionKey;
|
|
}
|
|
else
|
|
{
|
|
genEncryptionKey();
|
|
return encryptionKey;
|
|
}
|
|
|
|
}
|
|
}
|
|
public String GameTitle
|
|
{
|
|
get
|
|
{
|
|
return gameTitle;
|
|
}
|
|
}
|
|
public Boolean EncryptedImages
|
|
{
|
|
get
|
|
{
|
|
return encryptedImages;
|
|
}
|
|
set
|
|
{
|
|
jsonData.hasEncryptedImages = value;
|
|
File.WriteAllText(systemJsonFile, jsonData.ToString(Formatting.None));
|
|
encryptedImages = value;
|
|
}
|
|
}
|
|
|
|
public Boolean EncryptedAudio
|
|
{
|
|
get
|
|
{
|
|
return encryptedAudio;
|
|
}
|
|
set
|
|
{
|
|
jsonData.hasEncryptedAudio = value;
|
|
File.WriteAllText(systemJsonFile, jsonData.ToString(Formatting.None));
|
|
encryptedAudio = value;
|
|
}
|
|
}
|
|
|
|
public String FilePath
|
|
{
|
|
get
|
|
{
|
|
return filePath;
|
|
}
|
|
}
|
|
|
|
//Private functions
|
|
private static byte[] hexStr2Bytes(String HexStr)
|
|
{
|
|
if (HexStr.Length % 2 != 0)
|
|
{
|
|
throw new InvalidDataException(HexStr + " Is not divisible by 2!");
|
|
}
|
|
|
|
List<byte> byteList = new List<byte>();
|
|
|
|
for (int i = 0; i < HexStr.Length; i += 2)
|
|
{
|
|
string curHex = HexStr.Substring(i, 2);
|
|
byte hexByte = Byte.Parse(curHex, NumberStyles.HexNumber);
|
|
|
|
byteList.Add(hexByte);
|
|
}
|
|
|
|
byte[] resultingByteArray = byteList.ToArray();
|
|
return resultingByteArray;
|
|
|
|
}
|
|
|
|
private static byte[] xor(byte[] input, byte[] key)
|
|
{
|
|
long inpLen = input.LongLength;
|
|
byte[] output = new byte[inpLen];
|
|
|
|
for(long i = 0; i < input.LongLength; i++)
|
|
{
|
|
output[i] = (byte)(input[i] ^ key[i % key.LongLength]);
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
private void genEncryptionKey()
|
|
{
|
|
byte[] keyData = new byte[0x10];
|
|
Random rng = new Random((int)DateTime.Now.Ticks);
|
|
rng.NextBytes(keyData);
|
|
encryptionKey = keyData;
|
|
jsonData.encryptionKey = BitConverter.ToString(keyData, 0x00, keyData.Length).Replace("-", "");
|
|
File.WriteAllText(systemJsonFile, jsonData.ToString(Formatting.None));
|
|
isKeySet = true;
|
|
}
|
|
|
|
//Public functions
|
|
public static MVProject ParseSystemJson(string path)
|
|
{
|
|
if (File.Exists(path))
|
|
{
|
|
string jsonStr = File.ReadAllText(path, Encoding.UTF8);
|
|
dynamic systemJson = JObject.Parse(jsonStr);
|
|
|
|
//Check if valid system.json
|
|
MVProject mvp = new MVProject();
|
|
|
|
if (systemJson.gameTitle != null)
|
|
{
|
|
mvp.gameTitle = systemJson.gameTitle;
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidDataException("Not a valid system.json!");
|
|
}
|
|
|
|
if (systemJson.hasEncryptedAudio != null)
|
|
{
|
|
mvp.encryptedAudio = systemJson.hasEncryptedAudio;
|
|
}
|
|
|
|
if (systemJson.hasEncryptedImages != null)
|
|
{
|
|
mvp.encryptedImages = systemJson.hasEncryptedImages;
|
|
}
|
|
|
|
if (systemJson.encryptionKey != null)
|
|
{
|
|
string encKey = systemJson.encryptionKey;
|
|
mvp.encryptionKey = hexStr2Bytes(encKey);
|
|
mvp.isKeySet = true;
|
|
}
|
|
|
|
mvp.filePath = Path.GetDirectoryName(Path.GetDirectoryName(path));
|
|
mvp.systemJsonFile = path;
|
|
mvp.jsonData = systemJson;
|
|
return mvp;
|
|
|
|
}
|
|
else
|
|
{
|
|
throw new FileNotFoundException(path + " was not found!");
|
|
}
|
|
|
|
}
|
|
|
|
public void EncryptFile(Stream inStream, Stream outStream)
|
|
{
|
|
outStream.Seek(0x00, SeekOrigin.Begin);
|
|
outStream.SetLength(0x00);
|
|
|
|
BinaryWriter boutStream = new BinaryWriter(outStream);
|
|
string magic = "RPGMV";
|
|
byte[] magicBytes = Encoding.UTF8.GetBytes(magic);
|
|
|
|
outStream.Write(magicBytes, 0x00, magicBytes.Length);
|
|
boutStream.Write(0x00);
|
|
boutStream.Write(0x103);
|
|
outStream.Write(new byte[0x3], 0x00, 0x3);
|
|
|
|
inStream.Seek(0x00, SeekOrigin.Begin);
|
|
byte[] plaintextHeader = new byte[0x10];
|
|
inStream.Read(plaintextHeader, 0x00, 0x10);
|
|
|
|
byte[] encryptedHeader = xor(plaintextHeader, EncryptionKey);
|
|
outStream.Write(encryptedHeader, 0x00, encryptedHeader.Length);
|
|
inStream.CopyTo(outStream);
|
|
}
|
|
public void DecryptFile(Stream inStream, Stream outStream)
|
|
{
|
|
inStream.Seek(0x00, SeekOrigin.Begin);
|
|
|
|
byte[] magic = new byte[0x05];
|
|
inStream.Read(magic, 0x00,0x05);
|
|
string magicStr = Encoding.UTF8.GetString(magic);
|
|
|
|
if(magicStr != "RPGMV")
|
|
{
|
|
throw new InvalidDataException("Not an encrypted file!");
|
|
}
|
|
|
|
inStream.Seek(0x10, SeekOrigin.Begin);
|
|
byte[] encryptedHeader = new byte[0x10];
|
|
inStream.Read(encryptedHeader, 0x00, 0x10);
|
|
|
|
byte[] plaintextHeader = xor(encryptedHeader, EncryptionKey);
|
|
|
|
outStream.Seek(0x00, SeekOrigin.Begin);
|
|
outStream.SetLength(0);
|
|
|
|
outStream.Write(plaintextHeader, 0x00, plaintextHeader.Length);
|
|
inStream.CopyTo(outStream);
|
|
}
|
|
|
|
}
|
|
}
|