Created
March 22, 2024 00:04
-
-
Save aerosoul94/90b22cb94b629b557a7cd312e861d7e1 to your computer and use it in GitHub Desktop.
This file contains 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 characters
import sys | |
def read_uint32(stream): | |
return int.from_bytes(stream.read(4), 'big') | |
def read_uint64(stream): | |
return int.from_bytes(stream.read(8), 'big') | |
class BDEmuImageDetail: | |
def __init__(self, stream): | |
self._stream = stream | |
self._load(self._stream) | |
def _load(self, stream): | |
self._image_name = stream.read(0x80) | |
self._image_size = read_uint64(stream) | |
self._unknown1 = stream.read(0x78) | |
self._title_id = stream.read(0x20) | |
self._copyright_holder = stream.read(0x20) | |
self._producer_name = stream.read(0x20) | |
self._unknown_date = stream.read(8) | |
self._disk_type = stream.read(0x19) | |
self._unknown2 = stream.read(0x17) | |
@property | |
def image_name(self) -> str: | |
return (self._image_name.split(b'\0')[0]).decode('ascii') | |
@property | |
def image_size(self): | |
return self._image_size | |
class BDEmuEntry: | |
def __init__(self, stream): | |
self._stream = stream | |
self._flags = None | |
self._unknown1 = None | |
self._image_size = None | |
self._unknown2 = None | |
self._load(self._stream) | |
def _load(self, stream): | |
self._flags = read_uint64(stream) | |
self._unknown1 = read_uint64(stream) | |
self._image_size = read_uint64(stream) | |
self._unknown2 = read_uint64(stream) | |
@property | |
def exists(self): | |
return ((self._flags >> 8) & 0xff) == 0xff | |
class BDEmuHeader: | |
def __init__(self, stream): | |
self._stream = stream | |
self._signature = None | |
self._entries = [] | |
self._load(self._stream) | |
def _load(self, stream): | |
self._signature = stream.read(8) | |
if (self._signature != b'BD-EMU-1'): | |
raise Exception("Invalid signature at 0x20000") | |
stream.read(0x18) | |
for i in range(4): | |
entry = BDEmuEntry(stream) | |
self._entries.append(entry) | |
@property | |
def entries(self): | |
return self._entries | |
class BDEmu: | |
def __init__(self, path): | |
self._path = path | |
self._image_details = [] | |
self._stream = open(self._path, 'rb') | |
self._IMAGE_START = 0x100000 | |
self._IMAGE_SIZE = 0xBA7400000 | |
self._load(self._stream) | |
def __fini__(self): | |
self._stream.close() | |
def _load(self, stream): | |
stream.seek(0x20000) | |
self._header = BDEmuHeader(stream) | |
self._stream.seek(0x40000) | |
for entry in self._header.entries: | |
details = BDEmuImageDetail(stream) | |
if not entry.exists: | |
break | |
self._image_details.append(details) | |
@property | |
def header(self): | |
return self._header | |
@property | |
def entries(self): | |
return self._header.entries | |
@property | |
def image_details(self): | |
return self._image_details | |
def list_games(self): | |
detail: BDEmuImageDetail | |
for (i, detail) in enumerate(self.image_details): | |
print(f"Index: {i}") | |
print(f"Image Name: {detail.image_name}") | |
print(f"Image Size: {detail.image_size} bytes") | |
def extract_game(self, index, path): | |
if index > len(self._image_details): | |
raise Exception(f"Invalid index: {index}") | |
details: BDEmuImageDetail = self._image_details[index] | |
ostream = open(path + "/" + details.image_name, 'wb') | |
self._stream.seek(self._IMAGE_START + (self._IMAGE_SIZE * index)) | |
bytes_written = 0 | |
for _ in range(0, details.image_size, 0x80000): | |
bytes_to_write = min(0x80000, details.image_size - bytes_written) | |
buffer = self._stream.read(bytes_to_write) | |
ostream.write(buffer) | |
bytes_written += bytes_to_write | |
print(f"{bytes_written} / {details.image_size}") | |
ostream.close() | |
def print_usage(): | |
print(f"Usage: {sys.argv[0]} <command>\n" | |
f" Commands:\n" | |
f" list <bdemu image path>\n" | |
f" extract <bdemu image path> <image index> <output iso path>") | |
exit() | |
def main(args): | |
if len(args) < 2: | |
print_usage() | |
command = args[1] | |
path = args[2] | |
bdemu = BDEmu(path) | |
if (command == "list"): | |
if len(args) < 3: | |
print_usage() | |
bdemu.list_games() | |
elif (command == "extract"): | |
if len(args) < 5: | |
print_usage() | |
image_index = int(args[3]) | |
output_path = args[4] | |
bdemu.extract_game(image_index, output_path) | |
if __name__ == '__main__': | |
args = sys.argv | |
#args = [sys.argv[0], "list", "dev.img"] | |
#args = [sys.argv[0], "extract", "dev.img", "0", "./"] | |
main(args) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment