mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-31 16:03:24 +00:00
DEVTOOLS: Refactor director-generate-xobj-stub to allow multiple formats
This commit is contained in:
parent
94bd08a4dd
commit
ed97e05a1d
@ -6,7 +6,18 @@ import argparse
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
from typing import Any, BinaryIO
|
||||
from typing import Any, BinaryIO, Literal
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
XCodeType = Literal["XFCN", "XCMD", "XObject", "Xtra"]
|
||||
|
||||
|
||||
class XCode(TypedDict):
|
||||
type: XCodeType
|
||||
name: str
|
||||
slug: str
|
||||
method_table: list[str]
|
||||
|
||||
|
||||
DIRECTOR_SRC_PATH = os.path.abspath(
|
||||
os.path.join(__file__, "..", "..", "engines", "director")
|
||||
@ -15,7 +26,7 @@ MAKEFILE_PATH = os.path.join(DIRECTOR_SRC_PATH, "module.mk")
|
||||
LINGO_XLIBS_PATH = os.path.join(DIRECTOR_SRC_PATH, "lingo", "xlibs")
|
||||
LINGO_OBJECT_PATH = os.path.join(DIRECTOR_SRC_PATH, "lingo", "lingo-object.cpp")
|
||||
|
||||
TEMPLATE_H = """/* ScummVM - Graphic Adventure Engine
|
||||
LEGAL = """/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
@ -35,7 +46,11 @@ TEMPLATE_H = """/* ScummVM - Graphic Adventure Engine
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
"""
|
||||
|
||||
TEMPLATE_H = (
|
||||
LEGAL
|
||||
+ """
|
||||
#ifndef DIRECTOR_LINGO_XLIBS_{slug_upper}_H
|
||||
#define DIRECTOR_LINGO_XLIBS_{slug_upper}_H
|
||||
|
||||
@ -62,30 +77,13 @@ void close(int type);
|
||||
|
||||
#endif
|
||||
"""
|
||||
)
|
||||
|
||||
TEMPLATE_HEADER_METH = """void m_{methname}(int nargs);"""
|
||||
|
||||
TEMPLATE = """/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
TEMPLATE = (
|
||||
LEGAL
|
||||
+ """
|
||||
#include "common/system.h"
|
||||
|
||||
#include "director/director.h"
|
||||
@ -153,6 +151,7 @@ void {xobj_class}::m_new(int nargs) {{
|
||||
|
||||
}}
|
||||
"""
|
||||
)
|
||||
XLIB_METHOD_TEMPLATE = """ {{ "{methname}", {xobj_class}::m_{methname}, {arg_count}, {arg_count}, {director_version} }},"""
|
||||
|
||||
XOBJ_STUB_TEMPLATE = """XOBJSTUB({xobj_class}::m_{methname}, {default})"""
|
||||
@ -180,9 +179,9 @@ def read_uint32_be(data: bytes) -> int:
|
||||
return struct.unpack(">L", data)[0]
|
||||
|
||||
|
||||
def extract_xmethtable_macbinary(
|
||||
file: BinaryIO, resource_offset: int, xobj_id: int | None = None
|
||||
) -> tuple[str, str, list[str]]:
|
||||
def extract_xcode_macbinary(
|
||||
file: BinaryIO, resource_offset: int, xobj_id: str | None = None
|
||||
) -> XCode:
|
||||
file.seek(resource_offset)
|
||||
resource_data_offset = read_uint32_be(file.read(4))
|
||||
resource_map_offset = read_uint32_be(file.read(4))
|
||||
@ -197,64 +196,70 @@ def extract_xmethtable_macbinary(
|
||||
for _ in range(type_count + 1):
|
||||
key = file.read(4)
|
||||
types[key] = (read_uint16_be(file.read(2)) + 1, read_uint16_be(file.read(2)))
|
||||
if b"XCOD" in types:
|
||||
print("Found XCOD resources!")
|
||||
file.seek(
|
||||
resource_offset + resource_map_offset + type_list_offset + types[b"XCOD"][1]
|
||||
)
|
||||
resources = []
|
||||
for _ in range(types[b"XCOD"][0]):
|
||||
id = read_uint16_be(file.read(2))
|
||||
name_offset = read_uint16_be(file.read(2))
|
||||
file.read(1)
|
||||
data_offset = (read_uint8(file.read(1)) << 16) + read_uint16_be(
|
||||
file.read(2)
|
||||
xobj: dict[str, dict[str, Any]] = {}
|
||||
for chunk_type in [b"XCOD", b"XFCN", b"XCMD"]:
|
||||
if chunk_type in types:
|
||||
print(f"Found {chunk_type.decode('utf8')} resources!")
|
||||
file.seek(
|
||||
resource_offset
|
||||
+ resource_map_offset
|
||||
+ type_list_offset
|
||||
+ types[chunk_type][1]
|
||||
)
|
||||
file.read(4)
|
||||
resources.append((id, data_offset, name_offset))
|
||||
xobj: dict[int, dict[str, Any]] = {}
|
||||
for id, data_offset, name_offset in resources:
|
||||
xobj[id] = {}
|
||||
if name_offset != 0xFFFF:
|
||||
file.seek(
|
||||
resource_offset
|
||||
+ resource_map_offset
|
||||
+ name_list_offset
|
||||
+ name_offset
|
||||
resources: list[tuple[str, int, int]] = []
|
||||
for _ in range(types[chunk_type][0]):
|
||||
id = f"{chunk_type.decode('utf8')}_{read_uint16_be(file.read(2))}"
|
||||
name_offset = read_uint16_be(file.read(2))
|
||||
file.read(1)
|
||||
data_offset = (read_uint8(file.read(1)) << 16) + read_uint16_be(
|
||||
file.read(2)
|
||||
)
|
||||
name_size = read_uint8(file.read(1))
|
||||
xobj[id]["name"] = file.read(name_size).decode("macroman")
|
||||
else:
|
||||
xobj[id]["name"] = "<unknown>"
|
||||
file.seek(resource_offset + resource_data_offset + data_offset)
|
||||
size = read_uint32_be(file.read(4)) - 12
|
||||
file.read(12)
|
||||
xobj[id]["xmethtable"] = []
|
||||
while size > 0:
|
||||
count = read_uint8(file.read(1))
|
||||
if count == 0:
|
||||
break
|
||||
xobj[id]["xmethtable"].append(file.read(count).decode("macroman"))
|
||||
size -= 1 + count
|
||||
if xobj_id is None or xobj_id not in xobj:
|
||||
print("Please re-run with one of the following XOBJ resource IDs:")
|
||||
for id, data in xobj.items():
|
||||
print(f"{id} - {data['name']}")
|
||||
raise ValueError("Need to specify XOBJ resource ID")
|
||||
for entry in xobj[xobj_id]["xmethtable"]:
|
||||
print(entry)
|
||||
return (
|
||||
xobj[xobj_id]["name"],
|
||||
xobj[xobj_id]["name"].lower(),
|
||||
xobj[xobj_id]["xmethtable"],
|
||||
)
|
||||
file.read(4)
|
||||
resources.append((id, data_offset, name_offset))
|
||||
for id, data_offset, name_offset in resources:
|
||||
xobj[id] = {}
|
||||
if name_offset != 0xFFFF:
|
||||
file.seek(
|
||||
resource_offset
|
||||
+ resource_map_offset
|
||||
+ name_list_offset
|
||||
+ name_offset
|
||||
)
|
||||
name_size = read_uint8(file.read(1))
|
||||
xobj[id]["name"] = file.read(name_size).decode("macroman")
|
||||
else:
|
||||
xobj[id]["name"] = "<unknown>"
|
||||
file.seek(resource_offset + resource_data_offset + data_offset)
|
||||
xobj[id]["dump"] = file.read(read_uint32_be(file.read(4)) - 4)
|
||||
file.seek(resource_offset + resource_data_offset + data_offset)
|
||||
size = read_uint32_be(file.read(4)) - 12
|
||||
file.read(12)
|
||||
xobj[id]["xmethtable"] = []
|
||||
while size > 0:
|
||||
count = read_uint8(file.read(1))
|
||||
if count == 0:
|
||||
break
|
||||
xobj[id]["xmethtable"].append(file.read(count).decode("macroman"))
|
||||
size -= 1 + count
|
||||
if not xobj:
|
||||
raise ValueError("No extension resources found!")
|
||||
|
||||
raise ValueError("No XCOD resources found!")
|
||||
if xobj_id is None or xobj_id not in xobj:
|
||||
print("Please re-run with one of the following resource IDs:")
|
||||
for id, data in xobj.items():
|
||||
print(f"{id} - {data['name']}")
|
||||
raise ValueError("Need to specify resource ID")
|
||||
for entry in xobj[xobj_id]["xmethtable"]:
|
||||
print(entry)
|
||||
return {
|
||||
"type": "XObject",
|
||||
"name": xobj[xobj_id]["name"],
|
||||
"slug": xobj[xobj_id]["name"].lower(),
|
||||
"method_table": xobj[xobj_id]["xmethtable"],
|
||||
}
|
||||
|
||||
|
||||
def extract_xmethtable_win16(
|
||||
file: BinaryIO, ne_offset: int
|
||||
) -> tuple[str, str, list[str]]:
|
||||
def extract_xcode_win16(file: BinaryIO, ne_offset: int) -> XCode:
|
||||
# get resource table
|
||||
file.seek(ne_offset + 0x24, os.SEEK_SET)
|
||||
restable_offset = read_uint16_le(file.read(0x2))
|
||||
@ -325,10 +330,15 @@ def extract_xmethtable_win16(
|
||||
print(entry)
|
||||
library_name = xmethtable[1]
|
||||
xmethtable[1] = "--" + library_name
|
||||
return library_name, file_name, xmethtable
|
||||
return {
|
||||
"type": "XObject",
|
||||
"name": library_name,
|
||||
"slug": file_name,
|
||||
"method_table": xmethtable,
|
||||
}
|
||||
|
||||
|
||||
def extract_xmethtable_win32(file: BinaryIO, pe_offset: int) -> tuple[str, list[str]]:
|
||||
def extract_xcode_win32(file: BinaryIO, pe_offset: int) -> XCode:
|
||||
# get the .data section
|
||||
# find a string b"msgTable\x00", get the offset
|
||||
# get the .text section
|
||||
@ -337,10 +347,10 @@ def extract_xmethtable_win32(file: BinaryIO, pe_offset: int) -> tuple[str, list[
|
||||
# lookup addr2 in .data
|
||||
# get c string, split by \x0a
|
||||
|
||||
return ("", [])
|
||||
return {"type": "Xtra", "name": "", "slug": "", "method_table": []}
|
||||
|
||||
|
||||
def extract_xmethtable(path: str, resid: int) -> tuple[str, str, list[str]]:
|
||||
def extract_xcode(path: str, resid: str) -> XCode:
|
||||
with open(path, "rb") as file:
|
||||
magic = file.read(0x2)
|
||||
if magic == b"MZ":
|
||||
@ -350,7 +360,7 @@ def extract_xmethtable(path: str, resid: int) -> tuple[str, str, list[str]]:
|
||||
magic = file.read(0x2)
|
||||
if magic == b"NE":
|
||||
print("Found Win16 NE DLL!")
|
||||
return extract_xmethtable_win16(file, header_offset)
|
||||
return extract_xcode_win16(file, header_offset)
|
||||
elif magic == b"PE":
|
||||
raise ValueError("No support yet for extracting from Win32 DLLs")
|
||||
file.seek(0)
|
||||
@ -373,17 +383,17 @@ def extract_xmethtable(path: str, resid: int) -> tuple[str, str, list[str]]:
|
||||
+ ((128 - (data_size % 128)) if (data_size % 128) else 0)
|
||||
)
|
||||
print(f"resource offset: {resource_offset}")
|
||||
return extract_xmethtable_macbinary(file, resource_offset, resid)
|
||||
return extract_xcode_macbinary(file, resource_offset, resid)
|
||||
|
||||
raise ValueError("Unknown filetype")
|
||||
|
||||
|
||||
def generate_stubs(
|
||||
def generate_xobject_stubs(
|
||||
xmethtable: list[str],
|
||||
slug: str,
|
||||
name: str,
|
||||
director_version: int = 400,
|
||||
dry_run=False,
|
||||
dry_run: bool = False,
|
||||
) -> None:
|
||||
meths = []
|
||||
for e in xmethtable:
|
||||
@ -464,7 +474,7 @@ def main() -> None:
|
||||
)
|
||||
parser.add_argument("XOBJ_FILE", help="XObject/XLib file to test")
|
||||
parser.add_argument(
|
||||
"--resid", help="XOBJ resource ID (for MacBinary)", type=int, default=None
|
||||
"--resid", help="Resource ID (for MacBinary)", type=str, default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"--slug", help="Slug to use for files (e.g. {slug}.cpp, {slug}.h)"
|
||||
@ -483,10 +493,13 @@ def main() -> None:
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
library_name, file_name, xmethtable = extract_xmethtable(args.XOBJ_FILE, args.resid)
|
||||
slug = args.slug or file_name
|
||||
name = args.name or library_name
|
||||
generate_stubs(xmethtable, slug, name, args.version, args.dry_run)
|
||||
xcode = extract_xcode(args.XOBJ_FILE, args.resid)
|
||||
slug = args.slug or xcode["slug"]
|
||||
name = args.name or xcode["name"]
|
||||
if xcode["type"] == "XObject":
|
||||
generate_xobject_stubs(
|
||||
xcode["method_table"], slug, name, args.version, args.dry_run
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Loading…
x
Reference in New Issue
Block a user