mirror of
https://gitlab.com/kholdfuzion/goldeneye_src
synced 2026-05-23 14:41:54 -04:00
578 lines
24 KiB
Python
578 lines
24 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
GoldenEye Background Geometry Binary to C Converter
|
|
Parses binary bg files and generates C source files with complete structure mapping
|
|
"""
|
|
|
|
import struct
|
|
import sys
|
|
import os
|
|
from typing import BinaryIO, List, Tuple, Optional
|
|
from dataclasses import dataclass
|
|
|
|
# N64 base address for bg files
|
|
N64_BASE_ADDRESS = 0x0F000000
|
|
|
|
@dataclass
|
|
class BGHeader:
|
|
reserved: int
|
|
room_data_table_offset: int
|
|
portal_data_table_offset: int
|
|
global_vis_commands_offset: int
|
|
padding: int
|
|
|
|
@dataclass
|
|
class RoomDataEntry:
|
|
point_table_offset: int
|
|
pri_mapping_offset: int
|
|
sec_mapping_offset: int
|
|
x: float
|
|
y: float
|
|
z: float
|
|
|
|
@dataclass
|
|
class PortalDataEntry:
|
|
portal_offset: int
|
|
connected_room1: int
|
|
connected_room2: int
|
|
control_bytes: int
|
|
|
|
@dataclass
|
|
class Portal4Point:
|
|
num_points: int
|
|
padding: bytes
|
|
points: List[Tuple[float, float, float]]
|
|
|
|
class BGBinaryParser:
|
|
def __init__(self, binary_path: str, output_path: str = None):
|
|
self.binary_path = binary_path
|
|
self.output_path = output_path or binary_path.replace('.bin', '.c')
|
|
self.header_path = output_path.replace('.c', '.h') if output_path else binary_path.replace('.bin', '.h')
|
|
self.binary_data = b''
|
|
self.header = None
|
|
self.room_entries = []
|
|
self.portal_entries = []
|
|
self.global_vis_commands = []
|
|
self.portals = []
|
|
self.unused_portals = []
|
|
self.point_tables = {}
|
|
self.pri_mappings = {}
|
|
self.sec_mappings = {}
|
|
# Extract base name without extension and without _all_p suffix if present
|
|
base = os.path.splitext(os.path.basename(binary_path))[0]
|
|
self.base_name = base.replace('_all_p', '') if base.endswith('_all_p') else base
|
|
# Track structure offsets for proper ordering
|
|
self.structure_offsets = [] # List of (offset, type, name, data)
|
|
|
|
def load_binary(self):
|
|
"""Load binary file into memory"""
|
|
with open(self.binary_path, 'rb') as f:
|
|
self.binary_data = f.read()
|
|
|
|
def to_file_offset(self, n64_addr: int) -> int:
|
|
"""Convert N64 address to file offset"""
|
|
if n64_addr == 0:
|
|
return 0
|
|
return n64_addr - N64_BASE_ADDRESS
|
|
|
|
def read_u32(self, offset: int) -> int:
|
|
"""Read big-endian u32 at offset"""
|
|
return struct.unpack('>I', self.binary_data[offset:offset+4])[0]
|
|
|
|
def read_float(self, offset: int) -> float:
|
|
"""Read big-endian float at offset"""
|
|
return struct.unpack('>f', self.binary_data[offset:offset+4])[0]
|
|
|
|
def read_u8(self, offset: int) -> int:
|
|
"""Read u8 at offset"""
|
|
return self.binary_data[offset]
|
|
|
|
def read_u16(self, offset: int) -> int:
|
|
"""Read big-endian u16 at offset"""
|
|
return struct.unpack('>H', self.binary_data[offset:offset+2])[0]
|
|
|
|
def parse_header(self):
|
|
"""Parse bg_header structure at offset 0"""
|
|
room_offset_raw = self.read_u32(4)
|
|
portal_offset_raw = self.read_u32(8)
|
|
global_offset_raw = self.read_u32(12)
|
|
|
|
self.header = BGHeader(
|
|
reserved=self.read_u32(0),
|
|
room_data_table_offset=self.to_file_offset(room_offset_raw),
|
|
portal_data_table_offset=self.to_file_offset(portal_offset_raw),
|
|
global_vis_commands_offset=self.to_file_offset(global_offset_raw),
|
|
padding=self.read_u32(16)
|
|
)
|
|
print(f"Header parsed:")
|
|
print(f" Room table: 0x{room_offset_raw:08X} -> file offset 0x{self.header.room_data_table_offset:08X}")
|
|
print(f" Portal table: 0x{portal_offset_raw:08X} -> file offset 0x{self.header.portal_data_table_offset:08X}")
|
|
print(f" Global vis: 0x{global_offset_raw:08X} -> file offset 0x{self.header.global_vis_commands_offset:08X}")
|
|
|
|
def parse_room_data_table(self):
|
|
"""Parse room_data_table_entry array"""
|
|
offset = self.header.room_data_table_offset
|
|
entry_size = 24 # 3 pointers (12 bytes) + 3 floats (12 bytes)
|
|
null_entry_count = 0
|
|
first_entry = True
|
|
|
|
while True:
|
|
point_offset_raw = self.read_u32(offset)
|
|
pri_offset_raw = self.read_u32(offset + 4)
|
|
sec_offset_raw = self.read_u32(offset + 8)
|
|
|
|
x = self.read_float(offset + 12)
|
|
y = self.read_float(offset + 16)
|
|
z = self.read_float(offset + 20)
|
|
|
|
# Check for null entry - ALL fields must be zero (including pointers)
|
|
is_null_entry = (point_offset_raw == 0 and pri_offset_raw == 0 and
|
|
sec_offset_raw == 0 and x == 0.0 and y == 0.0 and z == 0.0)
|
|
|
|
if is_null_entry:
|
|
null_entry_count += 1
|
|
self.room_entries.append(RoomDataEntry(0, 0, 0, 0.0, 0.0, 0.0))
|
|
|
|
# Stop only on the second null entry (first is index 0, second is terminator)
|
|
if null_entry_count >= 2:
|
|
break
|
|
|
|
offset += entry_size
|
|
continue
|
|
|
|
# Convert N64 addresses to file offsets
|
|
point_offset = self.to_file_offset(point_offset_raw)
|
|
pri_offset = self.to_file_offset(pri_offset_raw)
|
|
sec_offset = self.to_file_offset(sec_offset_raw)
|
|
|
|
entry = RoomDataEntry(point_offset, pri_offset, sec_offset, x, y, z)
|
|
self.room_entries.append(entry)
|
|
|
|
offset += entry_size
|
|
|
|
print(f"Parsed {len(self.room_entries)} room entries (including {null_entry_count} null entries)")
|
|
|
|
def parse_portal_data_table(self):
|
|
"""Parse portal_data_table_entry array"""
|
|
offset = self.header.portal_data_table_offset
|
|
entry_size = 8 # 1 pointer (4 bytes) + 2 u8 + 1 u16
|
|
|
|
while True:
|
|
portal_offset_raw = self.read_u32(offset)
|
|
room1 = self.read_u8(offset + 4)
|
|
room2 = self.read_u8(offset + 5)
|
|
control = self.read_u16(offset + 6)
|
|
|
|
# Check for terminator
|
|
if portal_offset_raw == 0 and room1 == 0 and room2 == 0:
|
|
self.portal_entries.append(PortalDataEntry(0, 0, 0, 0))
|
|
break
|
|
|
|
# Convert N64 address to file offset
|
|
portal_offset = self.to_file_offset(portal_offset_raw)
|
|
|
|
entry = PortalDataEntry(portal_offset, room1, room2, control)
|
|
self.portal_entries.append(entry)
|
|
|
|
offset += entry_size
|
|
|
|
print(f"Parsed {len(self.portal_entries)} portal entries")
|
|
|
|
def parse_global_visibility_commands(self):
|
|
"""Parse global visibility commands u32 array"""
|
|
offset = self.header.global_vis_commands_offset
|
|
|
|
while True:
|
|
cmd = self.read_u32(offset)
|
|
self.global_vis_commands.append(cmd)
|
|
|
|
# Check for termination patterns:
|
|
# Pattern 1: 0x00010000, 0x00000000
|
|
# Pattern 2: 0x00000000, 0x00000000
|
|
if cmd == 0x00000000 and len(self.global_vis_commands) >= 2:
|
|
prev_cmd = self.global_vis_commands[-2]
|
|
if prev_cmd == 0x00010000 or prev_cmd == 0x00000000:
|
|
break
|
|
|
|
offset += 4
|
|
|
|
print(f"Parsed {len(self.global_vis_commands)} visibility commands")
|
|
|
|
def parse_portal_4_point(self, offset: int) -> Portal4Point:
|
|
"""Parse portal structure with variable number of points"""
|
|
num_points = self.read_u8(offset)
|
|
padding = self.binary_data[offset+1:offset+4]
|
|
|
|
points = []
|
|
point_offset = offset + 4
|
|
# Read only the number of points specified, not always 4
|
|
for i in range(num_points):
|
|
x = self.read_float(point_offset)
|
|
y = self.read_float(point_offset + 4)
|
|
z = self.read_float(point_offset + 8)
|
|
points.append((x, y, z))
|
|
point_offset += 12
|
|
|
|
return Portal4Point(num_points, padding, points)
|
|
|
|
def parse_all_portals(self):
|
|
"""Parse all portal structures"""
|
|
# Track unique portal offsets to avoid duplicates
|
|
unique_portals = {} # offset -> (first_idx, portal_data)
|
|
|
|
for i, entry in enumerate(self.portal_entries):
|
|
if entry.portal_offset != 0:
|
|
if entry.portal_offset not in unique_portals:
|
|
# First time seeing this offset, parse it
|
|
portal = self.parse_portal_4_point(entry.portal_offset)
|
|
unique_portals[entry.portal_offset] = (i, portal)
|
|
self.portals.append((i, portal))
|
|
|
|
# Check for unused portals between end of portal_data_table and first point_table
|
|
portal_table_end = self.header.portal_data_table_offset + (len(self.portal_entries) * 8)
|
|
|
|
# Find the first point table offset
|
|
first_point_table_offset = min([e.point_table_offset for e in self.room_entries if e.point_table_offset > 0], default=len(self.binary_data))
|
|
|
|
# Scan for unused portals in this range
|
|
current_offset = portal_table_end
|
|
unused_portals = []
|
|
|
|
while current_offset < first_point_table_offset:
|
|
# Check if this offset is already accounted for
|
|
if current_offset not in unique_portals:
|
|
# Try to parse a portal here
|
|
try:
|
|
num_points = self.read_u8(current_offset)
|
|
# Valid portal has 3-8 points typically
|
|
if 3 <= num_points <= 8:
|
|
portal = self.parse_portal_4_point(current_offset)
|
|
# Add as unused portal with negative index
|
|
unused_idx = -(len(unused_portals) + 1)
|
|
unused_portals.append((unused_idx, current_offset, portal))
|
|
# Skip past this portal structure
|
|
current_offset += 4 + (num_points * 12)
|
|
continue
|
|
except:
|
|
pass
|
|
|
|
current_offset += 4
|
|
|
|
if unused_portals:
|
|
print(f"Found {len(unused_portals)} unused portal structures")
|
|
self.unused_portals = unused_portals
|
|
else:
|
|
self.unused_portals = []
|
|
|
|
print(f"Parsed {len(self.portals)} unique portal geometries from {len([e for e in self.portal_entries if e.portal_offset != 0])} portal entries")
|
|
|
|
def parse_binary_data_array(self, offset: int, name: str) -> List[int]:
|
|
"""Parse u32 array with magic header"""
|
|
if offset == 0:
|
|
return None
|
|
|
|
# Read magic header
|
|
magic = self.read_u32(offset)
|
|
|
|
# 0x00000000 is a valid empty array (no compression header needed)
|
|
if magic == 0x00000000:
|
|
return [0x00000000]
|
|
|
|
# Check for valid compression magic header
|
|
if magic & 0xFFFF0000 != 0x11720000:
|
|
print(f"Warning: Invalid magic for {name}: 0x{magic:08X}")
|
|
return None
|
|
|
|
# Read until we hit another structure or end
|
|
data = []
|
|
current = offset
|
|
max_read = 10000 # Safety limit
|
|
|
|
while current < len(self.binary_data) and len(data) < max_read:
|
|
value = self.read_u32(current)
|
|
data.append(value)
|
|
current += 4
|
|
|
|
# Check if we've hit another known structure
|
|
if current in [e.point_table_offset for e in self.room_entries if e.point_table_offset > 0]:
|
|
break
|
|
if current in [e.pri_mapping_offset for e in self.room_entries if e.pri_mapping_offset > 0]:
|
|
break
|
|
if current in [e.sec_mapping_offset for e in self.room_entries if e.sec_mapping_offset > 0]:
|
|
break
|
|
|
|
return data
|
|
|
|
def parse_all_data_arrays(self):
|
|
"""Parse all point tables and mapping arrays"""
|
|
# Collect all unique offsets
|
|
offsets_to_parse = set()
|
|
|
|
for i, entry in enumerate(self.room_entries):
|
|
if entry.point_table_offset > 0:
|
|
offsets_to_parse.add(('point', i, entry.point_table_offset))
|
|
if entry.pri_mapping_offset > 0:
|
|
offsets_to_parse.add(('pri', i, entry.pri_mapping_offset))
|
|
# Only add sec_mapping if it's different from pri_mapping
|
|
if entry.sec_mapping_offset > 0 and entry.sec_mapping_offset != entry.pri_mapping_offset:
|
|
offsets_to_parse.add(('sec', i, entry.sec_mapping_offset))
|
|
|
|
# Parse each unique offset
|
|
for type_name, index, offset in sorted(offsets_to_parse, key=lambda x: x[2]):
|
|
data = self.parse_binary_data_array(offset, f"{type_name}_{index}")
|
|
|
|
if data:
|
|
if type_name == 'point':
|
|
self.point_tables[index] = data
|
|
elif type_name == 'pri':
|
|
self.pri_mappings[index] = data
|
|
elif type_name == 'sec':
|
|
self.sec_mappings[index] = data
|
|
|
|
print(f"Parsed {len(self.point_tables)} point tables")
|
|
print(f"Parsed {len(self.pri_mappings)} pri mappings")
|
|
print(f"Parsed {len(self.sec_mappings)} sec mappings")
|
|
|
|
def format_u32_array(self, data: List[int], name: str, per_line: int = 8) -> str:
|
|
"""Format u32 array for C output"""
|
|
if not data:
|
|
return f"u32 {name}[] = {{\n 0x00000000,\n}};\n"
|
|
|
|
lines = [f"u32 {name}[] = {{"]
|
|
|
|
for i in range(0, len(data), per_line):
|
|
chunk = data[i:i+per_line]
|
|
hex_vals = ', '.join(f"0x{val:08X}" for val in chunk)
|
|
lines.append(f" {hex_vals},")
|
|
|
|
lines.append("};\n")
|
|
return '\n'.join(lines)
|
|
|
|
def generate_c_file(self):
|
|
"""Generate complete C source file with structures in binary order"""
|
|
output = []
|
|
|
|
# Headers
|
|
output.append(f'#include "bg_all_p.h"')
|
|
output.append(f'#include "{self.base_name}_all_p.h"')
|
|
output.append('')
|
|
|
|
# Collect all structures with their file offsets
|
|
structures = []
|
|
|
|
# Header at offset 0
|
|
structures.append((0, 'header', 'struct bg_header header = {0, &room_data_table, &portal_data_table, &global_visibility_commands, 0};'))
|
|
|
|
# Room data table
|
|
if self.header:
|
|
room_lines = ['struct room_data_table_entry room_data_table[] = {']
|
|
for i, entry in enumerate(self.room_entries):
|
|
if entry.point_table_offset == 0 and entry.pri_mapping_offset == 0 and entry.sec_mapping_offset == 0:
|
|
# Completely null entry
|
|
room_lines.append(' {0, 0, 0, 0.000000, 0.000000, 0.000000},')
|
|
else:
|
|
# Has at least one non-null pointer
|
|
pt = f"&point_table_binary_{i}" if entry.point_table_offset != 0 and i in self.point_tables else "0"
|
|
pri = f"&pri_mapping_binary_{i}" if entry.pri_mapping_offset != 0 and i in self.pri_mappings else "0"
|
|
|
|
# Check if sec_mapping points to the same data as pri_mapping
|
|
if entry.sec_mapping_offset != 0:
|
|
if entry.sec_mapping_offset == entry.pri_mapping_offset:
|
|
# Secondary points to same data as primary
|
|
sec = f"&pri_mapping_binary_{i}" if i in self.pri_mappings else "0"
|
|
elif i in self.sec_mappings:
|
|
# Has its own secondary data
|
|
sec = f"&sec_mapping_binary_{i}"
|
|
else:
|
|
sec = "0"
|
|
else:
|
|
sec = "0"
|
|
|
|
room_lines.append(f' {{{pt}, {pri}, {sec}, {entry.x:.6f}, {entry.y:.6f}, {entry.z:.6f}}},')
|
|
room_lines.append('};')
|
|
structures.append((self.header.room_data_table_offset, 'room_table', '\n'.join(room_lines)))
|
|
|
|
# Create mapping from offset to portal index for reuse
|
|
offset_to_portal_idx = {}
|
|
for idx, portal in self.portals:
|
|
entry = self.portal_entries[idx]
|
|
offset_to_portal_idx[entry.portal_offset] = idx
|
|
|
|
# Portal data table - reference shared portals
|
|
if self.header:
|
|
portal_lines = ['struct portal_data_table_entry portal_data_table[] = {']
|
|
for i, entry in enumerate(self.portal_entries):
|
|
if entry.portal_offset == 0:
|
|
portal_lines.append(' {0, 0, 0, 0}')
|
|
else:
|
|
# Find which portal index to reference (the first one at this offset)
|
|
portal_idx = offset_to_portal_idx.get(entry.portal_offset, i)
|
|
portal_lines.append(f' {{&portal_{portal_idx}, 0x{entry.connected_room1:02X}, 0x{entry.connected_room2:02X}, 0x{entry.control_bytes:04X}}},')
|
|
portal_lines.append('};')
|
|
structures.append((self.header.portal_data_table_offset, 'portal_table', '\n'.join(portal_lines)))
|
|
|
|
# Global visibility commands
|
|
if self.header:
|
|
vis_lines = ['u32 global_visibility_commands[] ={']
|
|
for i in range(0, len(self.global_vis_commands), 2):
|
|
if i + 1 < len(self.global_vis_commands):
|
|
vis_lines.append(f' 0x{self.global_vis_commands[i]:08X}, 0x{self.global_vis_commands[i+1]:08X},')
|
|
else:
|
|
vis_lines.append(f' 0x{self.global_vis_commands[i]:08X}')
|
|
vis_lines.append('};')
|
|
structures.append((self.header.global_vis_commands_offset, 'vis_commands', '\n'.join(vis_lines)))
|
|
|
|
# Portal structures - only unique ones
|
|
for idx, portal in self.portals:
|
|
entry = self.portal_entries[idx]
|
|
pts = portal.points
|
|
num_pts = portal.num_points
|
|
point_str = ', '.join([f'{pt[0]:.6f}, {pt[1]:.6f}, {pt[2]:.6f}' for pt in pts])
|
|
struct_name = f'portal_{num_pts}_point'
|
|
portal_str = f'struct {struct_name} portal_{idx} = {{{num_pts}, 0, 0, 0, {point_str}}};'
|
|
structures.append((entry.portal_offset, f'portal_{idx}', portal_str))
|
|
|
|
# Unused portal structures (not referenced in portal_data_table)
|
|
for unused_idx, offset, portal in self.unused_portals:
|
|
pts = portal.points
|
|
num_pts = portal.num_points
|
|
point_str = ', '.join([f'{pt[0]:.6f}, {pt[1]:.6f}, {pt[2]:.6f}' for pt in pts])
|
|
struct_name = f'portal_{num_pts}_point'
|
|
portal_str = f'// Unused portal\nstruct {struct_name} portal_unused_{abs(unused_idx)} = {{{num_pts}, 0, 0, 0, {point_str}}};'
|
|
structures.append((offset, f'portal_unused_{abs(unused_idx)}', portal_str))
|
|
|
|
# Point tables
|
|
for idx in sorted(self.point_tables.keys()):
|
|
entry = self.room_entries[idx]
|
|
structures.append((entry.point_table_offset, f'point_{idx}',
|
|
self.format_u32_array(self.point_tables[idx], f'point_table_binary_{idx}')))
|
|
|
|
# Pri mappings
|
|
for idx in sorted(self.pri_mappings.keys()):
|
|
entry = self.room_entries[idx]
|
|
structures.append((entry.pri_mapping_offset, f'pri_{idx}',
|
|
self.format_u32_array(self.pri_mappings[idx], f'pri_mapping_binary_{idx}')))
|
|
|
|
# Sec mappings
|
|
for idx in sorted(self.sec_mappings.keys()):
|
|
entry = self.room_entries[idx]
|
|
structures.append((entry.sec_mapping_offset, f'sec_{idx}',
|
|
self.format_u32_array(self.sec_mappings[idx], f'sec_mapping_binary_{idx}')))
|
|
|
|
# Sort by offset
|
|
structures.sort(key=lambda x: x[0])
|
|
|
|
# Generate output in binary order
|
|
for offset, name, content in structures:
|
|
output.append(f'// Offset: 0x{offset:X}')
|
|
output.append(content)
|
|
output.append('')
|
|
|
|
return '\n'.join(output)
|
|
|
|
def generate_h_file(self):
|
|
"""Generate header file with extern declarations"""
|
|
output = []
|
|
|
|
guard = f"_{self.base_name.upper()}_ALL_P_H_"
|
|
output.append(f"#ifndef {guard}")
|
|
output.append(f"#define {guard}")
|
|
output.append('')
|
|
output.append('#include "bg_all_p.h"')
|
|
output.append('')
|
|
|
|
# Extern declarations for point tables
|
|
for idx in sorted(self.point_tables.keys()):
|
|
output.append(f'extern u32 point_table_binary_{idx}[];')
|
|
output.append('')
|
|
|
|
# Extern declarations for pri mappings
|
|
for idx in sorted(self.pri_mappings.keys()):
|
|
output.append(f'extern u32 pri_mapping_binary_{idx}[];')
|
|
output.append('')
|
|
|
|
# Extern declarations for sec mappings
|
|
for idx in sorted(self.sec_mappings.keys()):
|
|
output.append(f'extern u32 sec_mapping_binary_{idx}[];')
|
|
output.append('')
|
|
|
|
# Extern declarations for portals
|
|
for idx, portal in self.portals:
|
|
num_pts = portal.num_points
|
|
struct_name = f'portal_{num_pts}_point'
|
|
output.append(f'extern struct {struct_name} portal_{idx};')
|
|
output.append('')
|
|
|
|
output.append(f"#endif // {guard}")
|
|
|
|
return '\n'.join(output)
|
|
|
|
def parse(self):
|
|
"""Main parsing function"""
|
|
print(f"Parsing {self.binary_path}...")
|
|
self.load_binary()
|
|
self.parse_header()
|
|
self.parse_room_data_table()
|
|
self.parse_portal_data_table()
|
|
self.parse_global_visibility_commands()
|
|
self.parse_all_portals()
|
|
self.parse_all_data_arrays()
|
|
print("Parsing complete!")
|
|
|
|
def generate(self, force: bool = False):
|
|
"""Generate output files"""
|
|
# Check if output files exist
|
|
files_exist = []
|
|
if os.path.exists(self.output_path):
|
|
files_exist.append(self.output_path)
|
|
if os.path.exists(self.header_path):
|
|
files_exist.append(self.header_path)
|
|
|
|
if files_exist and not force:
|
|
print(f"Warning: The following files already exist:")
|
|
for f in files_exist:
|
|
print(f" {f}")
|
|
response = input("Overwrite? (y/N): ").strip().lower()
|
|
if response != 'y':
|
|
print("Aborted.")
|
|
return
|
|
|
|
print(f"Generating {self.output_path}...")
|
|
c_content = self.generate_c_file()
|
|
with open(self.output_path, 'w') as f:
|
|
f.write(c_content)
|
|
|
|
print(f"Generating {self.header_path}...")
|
|
h_content = self.generate_h_file()
|
|
with open(self.header_path, 'w') as f:
|
|
f.write(h_content)
|
|
|
|
print("Generation complete!")
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description='Convert GoldenEye background geometry binary files to C source'
|
|
)
|
|
parser.add_argument('input', help='Input binary file (.bin)')
|
|
parser.add_argument('output', nargs='?', help='Output C file (optional, defaults to input with .c extension)')
|
|
parser.add_argument('--force', '-f', action='store_true', help='Force overwrite without prompting')
|
|
|
|
args = parser.parse_args()
|
|
|
|
input_file = args.input
|
|
output_file = args.output
|
|
|
|
# If no output specified, use input filename with .c extension
|
|
if not output_file:
|
|
output_file = input_file.replace('.bin', '.c')
|
|
|
|
bg_parser = BGBinaryParser(input_file, output_file)
|
|
bg_parser.parse()
|
|
bg_parser.generate(force=args.force)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |