예제 #1
0
        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");
        }
예제 #2
0
        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));
                    }
                }
        }