├── PRead.cs ├── README.md ├── combine.py ├── convert.py └── extract.py /PRead.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | // Token: 0x0200000A RID: 10 7 | public class PRead 8 | { 9 | // Token: 0x06000036 RID: 54 RVA: 0x0000346C File Offset: 0x0000186C 10 | public PRead(string fn) 11 | { 12 | this.fs = new FileStream(fn, FileMode.Open, FileAccess.Read); 13 | this.Init(); 14 | if (fn.ToLower().EndsWith("adult.dat")) 15 | { 16 | this.ti.Remove("def/version.txt"); 17 | } 18 | } 19 | 20 | // Token: 0x06000037 RID: 55 RVA: 0x000034BC File Offset: 0x000018BC 21 | ~PRead() 22 | { 23 | this.fs.Close(); 24 | } 25 | 26 | // Token: 0x06000038 RID: 56 RVA: 0x000034F0 File Offset: 0x000018F0 27 | private void Init() 28 | { 29 | this.ti = new Dictionary(); 30 | this.fs.Position = 0L; 31 | byte[] array = new byte[1024]; 32 | this.fs.Read(array, 0, 1024); 33 | int num = 0; 34 | for (int i = 4; i < 255; i++) 35 | { 36 | num += BitConverter.ToInt32(array, i * 4); 37 | } 38 | byte[] array2 = new byte[16 * num]; 39 | this.fs.Read(array2, 0, array2.Length); 40 | this.dd(array2, 16 * num, BitConverter.ToUInt32(array, 212)); 41 | int num2 = BitConverter.ToInt32(array2, 12); 42 | int num3 = num2 - (1024 + 16 * num); 43 | byte[] array3 = new byte[num3]; 44 | this.fs.Read(array3, 0, array3.Length); 45 | this.dd(array3, num3, BitConverter.ToUInt32(array, 92)); 46 | int num4 = 0; 47 | for (int j = 0; j < num; j++) 48 | { 49 | int num5 = 16 * j; 50 | uint l = BitConverter.ToUInt32(array2, num5); 51 | int num6 = BitConverter.ToInt32(array2, num5 + 4); 52 | uint k = BitConverter.ToUInt32(array2, num5 + 8); 53 | uint p = BitConverter.ToUInt32(array2, num5 + 12); 54 | int m; 55 | for (m = num6; m < array3.Length; m++) 56 | { 57 | if (array3[m] == 0) 58 | { 59 | break; 60 | } 61 | } 62 | string key = Encoding.ASCII.GetString(array3, num4, m - num4).ToLower(); 63 | PRead.fe value = default(PRead.fe); 64 | value.p = p; 65 | value.L = l; 66 | value.k = k; 67 | this.ti.Add(key, value); 68 | num4 = m + 1; 69 | } 70 | } 71 | 72 | // Token: 0x06000039 RID: 57 RVA: 0x00003698 File Offset: 0x00001A98 73 | private void gk(byte[] b, uint k0) 74 | { 75 | uint num = k0 * 7391U + 42828U; 76 | uint num2 = num << 17 ^ num; 77 | for (int i = 0; i < 256; i++) 78 | { 79 | num -= k0; 80 | num += num2; 81 | num2 = num + 56U; 82 | num *= (num2 & 239U); 83 | b[i] = (byte)num; 84 | num >>= 1; 85 | } 86 | } 87 | 88 | // Token: 0x0600003A RID: 58 RVA: 0x000036F0 File Offset: 0x00001AF0 89 | private void dd(byte[] b, int L, uint k) 90 | { 91 | byte[] array = new byte[256]; 92 | this.gk(array, k); 93 | for (int i = 0; i < L; i++) 94 | { 95 | byte b2 = b[i]; 96 | b2 ^= array[i % 253]; 97 | b2 += 3; 98 | b2 += array[i % 89]; 99 | b2 ^= 153; 100 | b[i] = b2; 101 | } 102 | } 103 | 104 | // Token: 0x0600003B RID: 59 RVA: 0x00003750 File Offset: 0x00001B50 105 | public byte[] Data(string fn) 106 | { 107 | PRead.fe fe; 108 | if (!this.ti.TryGetValue(fn, out fe)) 109 | { 110 | return null; 111 | } 112 | this.fs.Position = (long)((ulong)fe.p); 113 | byte[] array = new byte[fe.L]; 114 | this.fs.Read(array, 0, array.Length); 115 | this.dd(array, array.Length, fe.k); 116 | return array; 117 | } 118 | 119 | // Token: 0x04000032 RID: 50 120 | private FileStream fs; 121 | 122 | // Token: 0x04000033 RID: 51 123 | private Dictionary ti; 124 | 125 | // Token: 0x0200000B RID: 11 126 | public struct fe 127 | { 128 | // Token: 0x04000034 RID: 52 129 | public uint p; 130 | 131 | // Token: 0x04000035 RID: 53 132 | public uint L; 133 | 134 | // Token: 0x04000036 RID: 54 135 | public uint k; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aokana-Extractor 2 | Extract data from Aokana(蒼の彼方のフォーリズム) Steam Version 3 | 4 | * PRead.cs - 5 | * Reversed C# code from AssemblyCsharp.dll, related to decryption 6 | 7 | * extract.py - 8 | * python3 extract.py 9 | * extract data from input_file.dat to output_path 10 | * python3 extract.py 11 | * display file(s) contained in nput_file.dat 12 | 13 | * convert.py - 14 | * convert group of files from webp format to png(ffmpeg required) 15 | 16 | * conbine.py - 17 | * combine all cg based on vcglist.csv(extracted from system.dat), should put all sprite&cg(include SD, evcg) under same directory 18 | 19 | 20 | For further detail(Such as CG combination or Download, in Chinese), see [https://zhuanlan.zhihu.com/p/108191499] 21 | -------------------------------------------------------------------------------- /combine.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | with open("vcglist.csv", "r", encoding='utf8') as cglist: 4 | content = cglist.read().split('\n')[:-1] 5 | for line in content: 6 | line = line.split(' ') 7 | cgname = line[0] 8 | combo = line[1:] 9 | for i in range(0, len(combo)): 10 | combo[i] = combo[i] + ".png" 11 | print("Generating %s......" % cgname) 12 | ret = os.system("convert -page +0+0 " + " -page +0+0 ".join(combo) + " -mosaic " + "./combined/" + cgname + ".png") 13 | if ret !=0: 14 | with open("failed.log", "a+") as log: 15 | log.write(cgname + "\n") 16 | -------------------------------------------------------------------------------- /convert.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | files = os.listdir("./") 4 | for item in files: 5 | ext = item.split('.')[-1] 6 | if ext == "webp": 7 | filename = item.split('.')[0] 8 | os.system("ffmpeg -i %s ./convert/%s" % (item, filename + ".png")) 9 | print("Processing %s" % item) 10 | -------------------------------------------------------------------------------- /extract.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import struct 4 | import os 5 | import sys 6 | 7 | # 32 Bits module 8 | global mod, mod1 9 | mod = 2 ** 32 10 | mod1 = 2 ** 31 11 | 12 | # Generate Key array 13 | def gk(k): 14 | a = (k * 7391 + 42828) % mod 15 | b = (a << 17 ^ a) % mod 16 | out = [] 17 | for _ in range(256): 18 | a = (a - k + b) % mod 19 | b = (a + 56) % mod 20 | a = (a * (b & 239)) % mod 21 | out.append(a & 255) 22 | a = a >> 1 23 | return out 24 | 25 | # decrypt data with key(Generated by k) 26 | def dd(data, k): 27 | data = bytearray(data) 28 | key = gk(k) 29 | for i in range(len(data)): 30 | k = data[i] 31 | k = k ^ key[i % 253] 32 | k = k + 3 + key[i % 89] 33 | k = k ^ 153 34 | data[i] = k & 255 35 | return data 36 | 37 | # Parse file header and get file list & location for decrypt 38 | def getInfo(f): 39 | f.seek(0) 40 | header = f.read(1024) 41 | num = 0 42 | num = (sum(struct.unpack(251 * "i", header[16:-4])) + mod1) % mod - mod1 43 | raw = dd(f.read(16 * num), struct.unpack("I", header[212:216])[0]) 44 | start = struct.unpack("I", raw[12:16])[0] 45 | array = dd(f.read(start - 1024 - 16 * num), struct.unpack("I", header[92:96])[0]) 46 | out = [] 47 | for i in range(num): 48 | l, offset, k, p = struct.unpack("IIII", raw[16 * i:16 * (i + 1)]) 49 | name = array[offset:array.find(0, offset)].decode("ascii") 50 | out.append((name, p, l, k)) 51 | return out 52 | 53 | # Locate position, create file, then write decrypted binaries 54 | def extract(f, files, out): 55 | for name, p, l, k in files: 56 | print("Extracting file %s ..." % name, end="") 57 | name = os.path.join(out, name) 58 | os.makedirs(os.path.dirname(name), exist_ok=True) 59 | with open(name, "wb") as o: 60 | f.seek(p) 61 | data = dd(f.read(l), k) 62 | o.write(data) 63 | print("Done.", end="\n") 64 | 65 | # Entry: argv1-> .dat file path; argv2-> output path 66 | if __name__ == "__main__": 67 | path = sys.argv[1] 68 | f = open(path, "rb") 69 | files = getInfo(f) 70 | if len(sys.argv) == 3: 71 | extract(f, files, sys.argv[2]) 72 | elif len(sys.argv) == 2: 73 | for item in files: 74 | print(item[0], item[2], "\tKByte(s)") 75 | --------------------------------------------------------------------------------