private static (string vCode1, string vCode2, uint indexRngSeed) LoadKeys(string startFolder) { // I had a stroke and decided to make everything as "functional" as possible, sorry. var pairs = new[] { new ResourceId("KEY_CODE"), new ResourceId("V_CODE"), new ResourceId("V_CODE2"), }; // assuming all *.int files are in game exe folder foreach (string file in Directory.GetFiles(startFolder, "*.exe")) { using (var resInfo = new ResourceInfo(file, true)) { // only attempt files where all resources are present if (!pairs.All(pair => resInfo.ResourceTypes.Any(resType => resType == pair))) { continue; } byte[][] resData = pairs.Select(pair => resInfo[pair].First().ToBytes()).ToArray(); Debug.Assert(resData.Skip(1).All(x => x.Length == 16), "VCodes were not 16 bytes long"); var bf = new BlowfishDecryptor(new Blowfish(resData[0].Select(x => (byte)(x ^ 0xCD)).ToArray())); var vCode1 = bf.TransformFinalBlock(resData[1], 0, 16).ToCString(); var vCode2 = bf.TransformFinalBlock(resData[2], 0, 16).ToCString(); return(vCode1, vCode2, GenerateIndexRngSeed(vCode2)); } } throw new Exception("Couldn't find game executable"); }
public IEnumerable <IExtractableFile> LoadFilesFromArchive(string inputArchive) { // find keys from exe, if not present yet if (key1 is null || key2 is null) { (key1, key2, indexRngSeed) = LoadKeys(Path.GetDirectoryName(inputArchive)); } using (var fs = File.OpenRead(inputArchive)) using (var br = new BinaryReader(fs)) { string magic = Encoding.ASCII.GetString(br.ReadBytes(4)); if (magic != FileMagic) { throw new InvalidMagicException(); } int entries = br.ReadInt32(); BlowfishDecryptor dec = null; for (int i = 0; i < entries + 1; i++) { string fileName = br.ReadBytes(0x40).ToCString(); ulong read = br.ReadUInt64(); if (fileName == "__key__.dat") { // don't make key multiple times if (!(dec is null)) { continue; } uint key = MersenneTwister.GenRand((uint)(read >> 32)); dec = new BlowfishDecryptor(new Blowfish(BitConverter.GetBytes(key))); continue; } if (dec is null) { throw new Exception("First file entry wasn't encryption key"); } fileName = DeobfuscateFileName(fileName, (uint)unchecked (indexRngSeed + i)); read += (ulong)i; var newBytes = dec.TransformFinalBlock(BitConverter.GetBytes(read), 0, 8); uint pos = BitConverter.ToUInt32(newBytes, 0); uint len = BitConverter.ToUInt32(newBytes, 4); yield return(new EncryptedFileSlice(fileName, pos, len, inputArchive, dec)); } } }