Files
mcpe/tools/grabsounds.py
iProgramInCpp db4543fa67 * Make the end-user extract assets from v0.1.3 instead of mcpe01_canada.apk.
This allows for less breakage from ignorance or other reasons, because the following things will now work by default:
- Touch controls
- Clouds
- update_game blocks
- The iOS port can use default8.png directly
2023-12-10 12:55:11 +02:00

153 lines
4.8 KiB
Python
Executable File

#!/usr/bin/python3
# Minecraft PE Reverse Engineering Project
# Copyright (C) 2023 iProgramInCpp
# -----------------------------------------------
# This tool grabs the sound data from the .so file from the mcpe01_canada.apk. Offsets are hardcoded to that version, nothing else will work.
import sys
import os
import hashlib
# use memory efficient range always
merange = range
try:
merange = xrange # python 2
except NameError:
pass
# defines
pcm_addresses = [
('PCM_cloth1', 0x00112B08),
('PCM_cloth2', 0x00117134),
('PCM_cloth3', 0x0011B760),
('PCM_cloth4', 0x0011FD8C),
('PCM_grass1', 0x001243B8),
('PCM_grass2', 0x0012F00C),
('PCM_grass3', 0x00139C60),
('PCM_grass4', 0x001448B4),
('PCM_gravel1', 0x0014F508),
('PCM_gravel2', 0x0015A15C),
('PCM_gravel3', 0x00164DB0),
('PCM_gravel4', 0x0016FA04),
('PCM_sand1', 0x0017A658),
('PCM_sand2', 0x00181214),
('PCM_sand3', 0x00187DD0),
('PCM_sand4', 0x0018E98C),
('PCM_stone1', 0x00195548),
('PCM_stone2', 0x001A019C),
('PCM_stone3', 0x001AADF0),
('PCM_stone4', 0x001B5A44),
('PCM_wood1', 0x001C0698),
('PCM_wood2', 0x001CB2EC),
('PCM_wood3', 0x001D5F40),
('PCM_wood4', 0x001E0B94),
('PCM_click', 0x001EB7E8),
('PCM_explode', 0x001F78A8),
('PCM_splash', 0x00209A38)
]
so_file_sha256 = '157af341d13a54cc935bbe24c5e1cf3d02d7e40ec20f9859b9853c2e996ebd81'
header_1 = '// Minecraft Pocket Edition Reverse Engineering Project\n// Copyright (C) 2011 Mojang Specifications.\n'
header_2 = '// Data extracted from libminecraftpe.so (Minecraft PE v0.1.3.apk).\n\n'
def read_int_from_bytes(arr_bytes, offset):
return arr_bytes[offset] | arr_bytes[offset + 1] << 8 | arr_bytes[offset + 2] << 16 | arr_bytes[offset + 3] << 24
def read_short_from_bytes(arr_bytes, offset):
return arr_bytes[offset] | arr_bytes[offset + 1] << 8
def format_hex_short(short):
return "{:04x}".format(short)
def main():
if len(sys.argv) < 2:
print('Usage:', sys.argv[0], '[libminecraftpe.so] (output dir)')
return
output_dir = ''
if len(sys.argv) > 2:
output_dir = sys.argv[2]
if len(output_dir) == 0:
output_dir = './sound_data/'
# append a slash to the file name, if it doesn't have one already
if output_dir[len(output_dir) - 1] != '/':
output_dir += '/'
if not os.path.exists(output_dir):
os.makedirs(output_dir)
bytes = []
# open the file
try:
with open(sys.argv[1],mode='rb') as sofile:
# read all bytes
bytes = sofile.read()
except FileNotFoundError:
print('ERROR: The file', sys.argv[1], 'was not found.')
return
# first off, hash the file to ensure it matches
src_sha256 = hashlib.sha256(bytes).hexdigest()
print('* Source file hash: ', src_sha256)
if src_sha256 != so_file_sha256:
print('ERROR: Source file does not match mcpe01_canada.apk\'s SO file. Please extract one from the aforementioned APK.')
return
print('* Extracting PCM data...')
include_all_str = '#pragma once\n\n' + header_1
include_all_str+= '\n// @NOTE: This is meant to be included in exactly 1 compile object/source file.\n\n' + header_2
for item in pcm_addresses:
include_all_str += '#include "' + item[0] + '.h"\n'
ostr = header_1 + header_2
channels = read_int_from_bytes(bytes, item[1] + 0)
data_length = read_int_from_bytes(bytes, item[1] + 12)
# @NOTE: All the PCM sound data is in `.data`. So there's no consts to be found.
ostr += 'PCMSoundHeader '+item[0]+' =\n{\n'
ostr += '\t' + str(channels) + ',\n'
ostr += '\t' + str(read_int_from_bytes(bytes, item[1] + 4)) + ',\n'
ostr += '\t' + str(read_int_from_bytes(bytes, item[1] + 8)) + ',\n'
ostr += '\t' + str(data_length)
ostr += '\n};\n\n'
data_length *= channels
ostr += 'uint16_t '+item[0]+'_data['+str(data_length)+'] =\n{'
for i in merange(data_length):
if i % 16 == 0:
ostr += '\n\t'
ostr += '0x'+format_hex_short(read_short_from_bytes(bytes, item[1] + 16 + i * 2)) + ','
ostr += '\n};\n'
filename = output_dir + item[0] + '.h'
with open(filename, 'w') as of:
of.write(ostr)
include_all_str += '\n\n'
for item in pcm_addresses:
include_all_str += 'SoundDesc SA_' + item[0][4:] + '(' + item[0] + ', ' + item[0] + '_data);\n'
with open(output_dir + 'sounds.h', 'w') as of:
of.write(include_all_str)
pass
if __name__ == '__main__':
main()