Last active
          May 23, 2025 20:36 
        
      - 
      
- 
        Save SciresM/402dab55eec8fc1a6d9d5260ce2f9dac to your computer and use it in GitHub Desktop. 
Revisions
- 
        SciresM revised this gist Jun 25, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,7 +2,7 @@ from Crypto.Cipher import AES from struct import unpack as up XORPAD = '3F99BB49B43CBBD339FE5FEA463316A8'.decode('hex') KEY = 'CAECB4CA65678965CBE67D7A3AFD228C'.decode('hex') IV = 'A65D5EA2D54AD0436DD46158C191361D'.decode('hex') 
- 
        SciresM created this gist Jun 25, 2020 .There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,116 @@ import os, sys, zlib, traceback from Crypto.Cipher import AES from struct import unpack as up XORPAD = '3F99BB49B43CBBD339FE5FEA463316A8'.decode('hex').upper() KEY = 'CAECB4CA65678965CBE67D7A3AFD228C'.decode('hex') IV = 'A65D5EA2D54AD0436DD46158C191361D'.decode('hex') def safe_open(path, mode): import os dn = os.path.split(path)[0] try: os.makedirs(dn) except OSError: if not os.path.isdir(dn): raise except WindowsError: if not os.path.isdir(dn): raise return open(path, mode) def read_data(fp, ofs, size): fp.seek(ofs) dat = fp.read(size) assert len(dat) == size return dat def process_encryption(data, enc_size, dec_size, enc_type, external_key_id): # Ensure valid input. assert len(data) >= enc_size assert dec_size <= enc_size data = data[:enc_size] # External keys not yet supported, in memory it's basically a lookup table assert external_key_id == 0 # Ensure valid encryption type assert enc_type in [0, 1, 2] if enc_type == 0: # No encryption return data[:dec_size] elif enc_type == 1: # AES encryption assert (enc_size & 0xF) == 0 return AES.new(KEY, AES.MODE_CBC, IV).decrypt(data)[:dec_size] else: #if enc_type == 2 # xorpad encryption return ''.join(chr(ord(c) ^ ord(XORPAD[i % len(XORPAD)])) for i,c in enumerate(data)) def process_compression(data, dec_size, out_size, cmp_type): # Ensure valid input assert len(data) == dec_size # Ensure valid compression type assert cmp_type in [0, 1] if cmp_type == 0: # No compression assert dec_size == out_size return data else: # if cmp_type == 1 # zlib compression without header return zlib.decompress(data, -zlib.MAX_WBITS) def get_ext(data): if data.startswith('BNTX'): return 'bntx' elif data.startswith(b'\x89PNG\x0D\x0A\x1A\x0A'): return 'png' elif data.startswith(b'\x8D\x2E\x54\xF6'): return 'msg' return 'bin' def extract_cafe_mix_archive(archive, out_dir): fp = open(archive, 'rb') _00, archive_id, _08, version, num_files, body_crc, _18, _1C = up('<8I', fp.read(0x20)) assert _00 == 0x10 print 'Archive: %08X' % archive_id print 'Version: %d.%d.%d' % (((version >> 16) & 0xFF), ((version >> 8) & 0xFF), ((version >> 0) & 0xFF)) print 'Num Files: %d' % num_files entry_headers = [fp.read(0x30) for i in xrange(num_files)] for i,header in enumerate(entry_headers): _00, _type_maybe, store_size, store_offset, enc_size, dec_size, out_size, crc, _20, cmp_type, enc_type, _26, _27, external_key_id, _2C = up('<IIIIIIIIIBBBBII', header) if external_key_id != 0: print 'Skipping file %d (%08x) due to external key usage (%08x).' % (i, _00, external_key_id) continue print 'Processing file %d (%08x)...' % (i, _00) #print '%X %X %X %X %X' % (store_offset, store_size, enc_size, dec_size, out_size) store_data = read_data(fp, store_offset, store_size) dec_data = process_encryption(store_data, enc_size, dec_size, enc_type, external_key_id) out_data = process_compression(dec_data, dec_size, out_size, cmp_type) assert len(out_data) == out_size assert (zlib.crc32(out_data) & 0xFFFFFFFF) == crc with safe_open('%s/%08X.%s' % (out_dir, _00, get_ext(out_data)), 'wb') as f: f.write(out_data) def main(argc, argv): if argc < 2 or argc > 3: print 'Usage: %s archive [outdir]' % argv[0] return 1 archive = argv[1] if argc >= 3: out_dir = argv[2] elif archive.endswith('.arc'): out_dir = archive[:-4] else: out_dir = archive + '_out' try: extract_cafe_mix_archive(archive, out_dir) except Exception as e: print 'An error occured: %s' % str(e) traceback.print_exc(file=sys.stdout) return 1 return 0 if __name__ == '__main__': sys.exit(main(len(sys.argv), sys.argv))