alpine: Select ground sprite based on terrain (GRF logic generator)
This commit is contained in:
@@ -114,7 +114,16 @@ replace_coastal_sprites('gfx/water/seashore_grid_temperate.gimp.png', 1, 1)
|
||||
png = grf.ImageFile("gfx/meadow_grid_temperate.png")
|
||||
gen.add_sprite(grf.SpriteSet(grf.OBJECT, 19))
|
||||
tmpl_ground_sprites(lambda *args, **kw: gen.add_sprite(grf.FileSprite(png, *args, **kw)), 1, 1)
|
||||
gen.add_sprite(grf.BasicSpriteLayout(grf.OBJECT, 255))
|
||||
# gen.add_sprite(grf.BasicSpriteLayout(grf.OBJECT, 255))
|
||||
gen.add_sprite(grf.AdvancedSpriteLayout(
|
||||
grf.OBJECT, 255,
|
||||
ground={
|
||||
'sprite': 0,
|
||||
'pal': 32768,
|
||||
'flags': 2,
|
||||
'add': grf.Temp(0),
|
||||
}
|
||||
))
|
||||
|
||||
gen.add_sprite(grf.Object(0,
|
||||
label=b'FLMA',
|
||||
@@ -124,15 +133,29 @@ gen.add_sprite(grf.Object(0,
|
||||
flags=grf.Object.Flags.HAS_NO_FOUNDATION | grf.Object.Flags.ALLOW_UNDER_BRIDGE,
|
||||
))
|
||||
|
||||
gen.add_sprite(grf.VarAction2(
|
||||
feature=grf.OBJECT,
|
||||
use_related=False,
|
||||
set_id=0,
|
||||
ranges={0: 0, 1: 1, 2: 2, 4: 4, 8: 8, 9: 9, 3: 3, 6: 6, 12: 12, 5: 5, 10: 10, 11: 11, 7: 7, 14: 14, 13: 13, 27: 17, 23: 16, 30: 18, 29: 15},
|
||||
default=0,
|
||||
code='tile_slope'
|
||||
))
|
||||
|
||||
gen.add_sprite(grf.VarAction2(
|
||||
feature=grf.OBJECT,
|
||||
use_related=False,
|
||||
set_id=255,
|
||||
ranges={0: grf.Set(255)},
|
||||
default=grf.Set(255),
|
||||
code='TEMP[0] = call(0)'
|
||||
))
|
||||
|
||||
gen.add_sprite(grf.Action3(grf.OBJECT, [0], [[255, 255]], 255))
|
||||
|
||||
{0: 0, 1: 1, 2: 2, 4: 4, 8: 8, 9: 9, 3: 3, 6: 6, 12: 12, 5: 5, 10: 10, 11: 11, 7: 7, 14: 14, 13: 13, 27: 17, 23: 16, 30: 18, 29: 15}
|
||||
|
||||
# CREEKS
|
||||
|
||||
|
||||
|
||||
|
||||
# MEADOW
|
||||
|
||||
# spriteset (meadow_groundsprites, "gfx/meadow_grid_temperate.png") { tmpl_groundsprites(1, 1) }
|
||||
|
||||
@@ -6,6 +6,9 @@ import spectra
|
||||
import struct
|
||||
import numpy as np
|
||||
|
||||
from parser import Node, Expr, Value, Var, Temp, Perm, Call, parse_code, OP_INIT
|
||||
|
||||
|
||||
to_spectra = lambda r, g, b: spectra.rgb(float(r) / 255., float(g) / 255., float(b) / 255.)
|
||||
# working with DOS palette only
|
||||
PALETTE = (0, 0, 255, 16, 16, 16, 32, 32, 32, 48, 48, 48, 64, 64, 64, 80, 80, 80, 100, 100, 100, 116, 116, 116, 132, 132, 132, 148, 148, 148, 168, 168, 168, 184, 184, 184, 200, 200, 200, 216, 216, 216, 232, 232, 232, 252, 252, 252, 52, 60, 72, 68, 76, 92, 88, 96, 112, 108, 116, 132, 132, 140, 152, 156, 160, 172, 176, 184, 196, 204, 208, 220, 48, 44, 4, 64, 60, 12, 80, 76, 20, 96, 92, 28, 120, 120, 64, 148, 148, 100, 176, 176, 132, 204, 204, 168, 72, 44, 4, 88, 60, 20, 104, 80, 44, 124, 104, 72, 152, 132, 92, 184, 160, 120, 212, 188, 148, 244, 220, 176, 64, 0, 4, 88, 4, 16, 112, 16, 32, 136, 32, 52, 160, 56, 76, 188, 84, 108, 204, 104, 124, 220, 132, 144, 236, 156, 164, 252, 188, 192, 252, 208, 0, 252, 232, 60, 252, 252, 128, 76, 40, 0, 96, 60, 8, 116, 88, 28, 136, 116, 56, 156, 136, 80, 176, 156, 108, 196, 180, 136, 68, 24, 0, 96, 44, 4, 128, 68, 8, 156, 96, 16, 184, 120, 24, 212, 156, 32, 232, 184, 16, 252, 212, 0, 252, 248, 128, 252, 252, 192, 32, 4, 0, 64, 20, 8, 84, 28, 16, 108, 44, 28, 128, 56, 40, 148, 72, 56, 168, 92, 76, 184, 108, 88, 196, 128, 108, 212, 148, 128, 8, 52, 0, 16, 64, 0, 32, 80, 4, 48, 96, 4, 64, 112, 12, 84, 132, 20, 104, 148, 28, 128, 168, 44, 28, 52, 24, 44, 68, 32, 60, 88, 48, 80, 104, 60, 104, 124, 76, 128, 148, 92, 152, 176, 108, 180, 204, 124, 16, 52, 24, 32, 72, 44, 56, 96, 72, 76, 116, 88, 96, 136, 108, 120, 164, 136, 152, 192, 168, 184, 220, 200, 32, 24, 0, 56, 28, 0, 72, 40, 4, 88, 52, 12, 104, 64, 24, 124, 84, 44, 140, 108, 64, 160, 128, 88, 76, 40, 16, 96, 52, 24, 116, 68, 40, 136, 84, 56, 164, 96, 64, 184, 112, 80, 204, 128, 96, 212, 148, 112, 224, 168, 128, 236, 188, 148, 80, 28, 4, 100, 40, 20, 120, 56, 40, 140, 76, 64, 160, 100, 96, 184, 136, 136, 36, 40, 68, 48, 52, 84, 64, 64, 100, 80, 80, 116, 100, 100, 136, 132, 132, 164, 172, 172, 192, 212, 212, 224, 40, 20, 112, 64, 44, 144, 88, 64, 172, 104, 76, 196, 120, 88, 224, 140, 104, 252, 160, 136, 252, 188, 168, 252, 0, 24, 108, 0, 36, 132, 0, 52, 160, 0, 72, 184, 0, 96, 212, 24, 120, 220, 56, 144, 232, 88, 168, 240, 128, 196, 252, 188, 224, 252, 16, 64, 96, 24, 80, 108, 40, 96, 120, 52, 112, 132, 80, 140, 160, 116, 172, 192, 156, 204, 220, 204, 240, 252, 172, 52, 52, 212, 52, 52, 252, 52, 52, 252, 100, 88, 252, 144, 124, 252, 184, 160, 252, 216, 200, 252, 244, 236, 72, 20, 112, 92, 44, 140, 112, 68, 168, 140, 100, 196, 168, 136, 224, 200, 176, 248, 208, 184, 255, 232, 208, 252, 60, 0, 0, 92, 0, 0, 128, 0, 0, 160, 0, 0, 196, 0, 0, 224, 0, 0, 252, 0, 0, 252, 80, 0, 252, 108, 0, 252, 136, 0, 252, 164, 0, 252, 192, 0, 252, 220, 0, 252, 252, 0, 204, 136, 8, 228, 144, 4, 252, 156, 0, 252, 176, 48, 252, 196, 100, 252, 216, 152, 8, 24, 88, 12, 36, 104, 20, 52, 124, 28, 68, 140, 40, 92, 164, 56, 120, 188, 72, 152, 216, 100, 172, 224, 92, 156, 52, 108, 176, 64, 124, 200, 76, 144, 224, 92, 224, 244, 252, 200, 236, 248, 180, 220, 236, 132, 188, 216, 88, 152, 172, 244, 0, 244, 245, 0, 245, 246, 0, 246, 247, 0, 247, 248, 0, 248, 249, 0, 249, 250, 0, 250, 251, 0, 251, 252, 0, 252, 253, 0, 253, 254, 0, 254, 255, 0, 255, 76, 24, 8, 108, 44, 24, 144, 72, 52, 176, 108, 84, 210, 146, 126, 252, 60, 0, 252, 84, 0, 252, 104, 0, 252, 124, 0, 252, 148, 0, 252, 172, 0, 252, 196, 0, 64, 0, 0, 255, 0, 0, 48, 48, 0, 64, 64, 0, 80, 80, 0, 255, 255, 0, 32, 68, 112, 36, 72, 116, 40, 76, 120, 44, 80, 124, 48, 84, 128, 72, 100, 144, 100, 132, 168, 216, 244, 252, 96, 128, 164, 68, 96, 140, 255, 255, 255)
|
||||
@@ -21,6 +24,12 @@ BPP_8, BPP_32 = range(2)
|
||||
OBJECT = 0x0f
|
||||
|
||||
|
||||
def hex_str(s):
|
||||
if isinstance(s, (bytes, memoryview)):
|
||||
return ':'.join('{:02x}'.format(b) for b in s)
|
||||
return ':'.join('{:02x}'.format(ord(c)) for c in s)
|
||||
|
||||
|
||||
def color_distance(c1, c2):
|
||||
rmean = (c1.rgb[0] + c2.rgb[0]) / 2.
|
||||
r = c1.rgb[0] - c2.rgb[0]
|
||||
@@ -46,82 +55,6 @@ def find_best_color(x, in_range=SAFE_COLORS):
|
||||
# assert im.mode == 'RGB', im.mode
|
||||
# data = np.array(im)
|
||||
|
||||
OP_ADD = 0x00
|
||||
OP_SUB = 0x01
|
||||
OP_TSTO = 0x0E
|
||||
OP_PSTO = 0x10
|
||||
|
||||
OPERATORS = {
|
||||
OP_ADD: ('{a} + {b}', 2),
|
||||
OP_SUB: ('{a} - {b}', 2),
|
||||
OP_TSTO: ('TEMP[{b}] = {a}', 1),
|
||||
OP_PSTO: ('PERM[{b}] = {a}', 1),
|
||||
}
|
||||
|
||||
DEFAULT_INDENT_STR = ' '
|
||||
|
||||
|
||||
class Node:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def _make_node(self, value):
|
||||
if isinstance(value, int):
|
||||
return Value(value)
|
||||
assert isinstance(value, Node)
|
||||
return value
|
||||
|
||||
def __add__(self, other):
|
||||
return Expr(OP_ADD, self, self._make_node(other))
|
||||
|
||||
def __sub__(self, other):
|
||||
return Expr(OP_SUB, self, self._make_node(other))
|
||||
|
||||
def store_temp(self, register):
|
||||
return Expr(OP_TSTO, self, self._make_node(register))
|
||||
|
||||
def store_perm(self, register):
|
||||
return Expr(OP_PSTO, self, self._make_node(register))
|
||||
|
||||
def format(self, parent_priority=0, indent=0, indent_str=DEFAULT_INDENT_STR):
|
||||
raise NotImplementedError
|
||||
|
||||
def __str__(self):
|
||||
return self.format()
|
||||
|
||||
def __repr__(self):
|
||||
return self.format()
|
||||
|
||||
|
||||
class Expr(Node):
|
||||
def __init__(self, op, a, b):
|
||||
self.op = op
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
def format(self, parent_priority=0, indent=0, indent_str=DEFAULT_INDENT_STR):
|
||||
if self.op in OPERATORS:
|
||||
fmt, prio = OPERATORS[self.op]
|
||||
else:
|
||||
fmt, prio = '{a} <{self.op:02x}> {b}', 1
|
||||
astr = self.a.format(prio, indent=0)
|
||||
bstr = self.b.format(prio, indent=0)
|
||||
res = fmt.format(a=astr, b=bstr)
|
||||
suffix = indent_str * indent
|
||||
if prio <= parent_priority:
|
||||
return suffix + f'({res})'
|
||||
return suffix + res
|
||||
|
||||
|
||||
class Value(Node):
|
||||
def __init__(self, value):
|
||||
super().__init__()
|
||||
self.value = value
|
||||
|
||||
def format(self, parent_priority=0, indent=0, indent_str=DEFAULT_INDENT_STR):
|
||||
return indent_str * indent + str(self.value)
|
||||
|
||||
|
||||
class BaseSprite:
|
||||
def get_data(self):
|
||||
raise NotImplemented
|
||||
@@ -476,11 +409,11 @@ class SpriteSet(Action1):
|
||||
|
||||
|
||||
class BasicSpriteLayout(LazyBaseSprite):
|
||||
def __init__(self, feature, ref_id, xofs=0, yofs=0, extent=(0, 0, 0)):
|
||||
def __init__(self, feature, set_id, xofs=0, yofs=0, extent=(0, 0, 0)):
|
||||
super().__init__()
|
||||
assert feature in (0x07, 0x09, OBJECT, 0x11), feature
|
||||
self.feature = feature
|
||||
self.ref_id = ref_id
|
||||
self.set_id = set_id
|
||||
self.xofs = xofs
|
||||
self.yofs = yofs
|
||||
self.extent = extent
|
||||
@@ -489,13 +422,135 @@ class BasicSpriteLayout(LazyBaseSprite):
|
||||
return id | (mode << 14) | (recolor << 16) | (draw_in_transparent << 30) | (use_last << 31)
|
||||
|
||||
def _encode(self):
|
||||
return struct.pack('<BBBBIIbbBBB', 0x02, self.feature, self.ref_id, 0,
|
||||
return struct.pack('<BBBBIIbbBBB', 0x02, self.feature, self.set_id, 0,
|
||||
# self._encode_sprite(), self._encode_sprite(),
|
||||
self._encode_sprite(use_last=True), 0,
|
||||
self.xofs, self.yofs,
|
||||
|
||||
*self.extent)
|
||||
|
||||
|
||||
class AdvancedSpriteLayout(LazyBaseSprite):
|
||||
def __init__(self, feature, set_id, ground, sprites=()):
|
||||
super().__init__()
|
||||
assert feature in (0x07, 0x09, OBJECT, 0x11), feature
|
||||
assert len(sprites) < 64, len(sprites)
|
||||
self.feature = feature
|
||||
self.set_id = set_id
|
||||
self.ground = ground
|
||||
self.sprites = sprites
|
||||
|
||||
def _encode_sprite(self, sprite, aux=False):
|
||||
res = struct.pack('<HHH', sprite['sprite'], sprite['pal'], sprite['flags'])
|
||||
|
||||
if aux:
|
||||
delta = s.get('delta', (0, 0, 0))
|
||||
is_parent = bool(sprite.get('parent'))
|
||||
if not is_parent:
|
||||
delta = (delta[0], delta[1], 0x80)
|
||||
res += struct.pack('<BBB', *delta)
|
||||
if is_parent:
|
||||
res += struct.pack('<BBB', *sprite.get('size'))
|
||||
|
||||
for k in ('dodraw', 'add', 'palette', 'sprite_var10', 'palette_var10'):
|
||||
if k in sprite:
|
||||
val = sprite[k]
|
||||
if k == 'add':
|
||||
assert isinstance(val, Temp)
|
||||
val = val.register
|
||||
res += bytes((val, ))
|
||||
# TODO deltas
|
||||
return res
|
||||
|
||||
def _encode(self):
|
||||
res = struct.pack('<BBBB', 0x02, self.feature, self.set_id, len(self.sprites) + 0x40)
|
||||
res += self._encode_sprite(self.ground)
|
||||
for s in self.sprites:
|
||||
res += self._encode_sprite(s, True)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class Set:
|
||||
def __init__(self, set_id):
|
||||
self.value = set_id
|
||||
self.is_callback = bool(set_id & 0x8000)
|
||||
self.set_id = set_id & 0x7fff
|
||||
|
||||
def __str__(self):
|
||||
if self.is_callback:
|
||||
return f'CB({self.set_id})'
|
||||
return f'Set({self.set_id})'
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
class CB(Set):
|
||||
def __init__(self, value):
|
||||
super().__init__(value | 0x8000)
|
||||
|
||||
|
||||
class Range:
|
||||
def __init__(self, low, high, set):
|
||||
self.set = set
|
||||
self.low = low
|
||||
self.high = high
|
||||
|
||||
def __str__(self):
|
||||
if self.low == self.high:
|
||||
return f'{self.low} -> {self.set}'
|
||||
return f'{self.low}..{self.high} -> {self.set}'
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
class VarAction2(LazyBaseSprite):
|
||||
def __init__(self, feature, set_id, use_related, ranges, default, code):
|
||||
super().__init__()
|
||||
self.feature = feature
|
||||
assert feature == OBJECT, feature
|
||||
self.set_id = set_id
|
||||
self.use_related = use_related
|
||||
self.ranges = ranges
|
||||
self.default = default
|
||||
self.code = parse_code(code)
|
||||
|
||||
def _get_set_value(self, set_obj):
|
||||
if isinstance(set_obj, Set):
|
||||
return set_obj.value
|
||||
assert isinstance(set_obj, int)
|
||||
return set_obj | 0x8000
|
||||
|
||||
def _encode(self):
|
||||
res = bytes((0x02, self.feature, self.set_id, 0x8a if self.use_related else 0x89))
|
||||
code = self.code[0].compile(register=0x80)[1]
|
||||
for c in self.code[1:]:
|
||||
code += bytes((OP_INIT,))
|
||||
code += c.compile(register=0x80)[1]
|
||||
# print('CODE', hex_str(code))
|
||||
res += code[:-5]
|
||||
res += bytes((code[-5] & ~0x20,)) # mark the end of a chain
|
||||
res += code[-4:]
|
||||
res += bytes((len(self.ranges),))
|
||||
# print('RES', hex_str(res))
|
||||
ranges = self.ranges
|
||||
if isinstance(ranges, dict):
|
||||
ranges = self.ranges.items()
|
||||
for r in ranges:
|
||||
if isinstance(r, Range):
|
||||
set_obj = r.set
|
||||
low = r.low
|
||||
high = r.high
|
||||
else:
|
||||
set_obj = r[1]
|
||||
low = r[0]
|
||||
high = r[0]
|
||||
# TODO split (or validate) negative-positive ranges
|
||||
res += struct.pack('<HII', self._get_set_value(set_obj), low, high)
|
||||
res += struct.pack('<H', self._get_set_value(self.default))
|
||||
return res
|
||||
|
||||
|
||||
class Action3(LazyBaseSprite):
|
||||
def __init__(self, feature, ids, maps, default):
|
||||
super().__init__()
|
||||
|
||||
437
grf/alpine/parser.py
Normal file
437
grf/alpine/parser.py
Normal file
@@ -0,0 +1,437 @@
|
||||
import struct
|
||||
|
||||
import ply.lex
|
||||
import ply.yacc
|
||||
|
||||
OP_ADD = 0x00
|
||||
OP_SUB = 0x01
|
||||
OP_MIN = 0x02
|
||||
OP_MAX = 0x03
|
||||
OP_MINU = 0x04
|
||||
OP_MAXU = 0x05
|
||||
OP_MUL = 0x0A
|
||||
OP_BINAND = 0x0B
|
||||
OP_BINOR = 0x0C
|
||||
OP_BINXOR = 0x0D
|
||||
OP_TSTO = 0x0E
|
||||
OP_INIT = 0x0F
|
||||
OP_PSTO = 0x10
|
||||
OP_ROT = 0x11
|
||||
OP_CMP = 0x12
|
||||
OP_CMPU = 0x13
|
||||
OP_SHL = 0x14
|
||||
OP_SHRU = 0x15
|
||||
OP_SHR = 0x16
|
||||
|
||||
OPERATORS = {
|
||||
OP_ADD: ('{a} + {b}', 5, False),
|
||||
OP_SUB: ('{a} - {b}', 5, True),
|
||||
OP_MIN: ('min({a}, {b})', 7, False),
|
||||
OP_MAX: ('max({a}, {b})', 7, False),
|
||||
OP_MINU: ('minu({a}, {b})', 7, False),
|
||||
OP_MAXU: ('maxu({a}, {b})', 7, False),
|
||||
OP_MUL: ('{a} * {b}', 6, False),
|
||||
OP_BINAND: ('{a} & {b}', 4, True),
|
||||
OP_BINOR: ('{a} | {b}', 4, True),
|
||||
OP_BINXOR: ('{a} ^ {b}', 4, True),
|
||||
OP_TSTO: ('TEMP[{b}] = {a}', 2, True),
|
||||
OP_INIT: (None, 1, False),
|
||||
OP_PSTO: ('PERM[{b}] = {a}', 2, True),
|
||||
OP_ROT: ('rot({a}, {b})', 7, False),
|
||||
OP_CMP: ('cmp({a}, {b})', 7, False),
|
||||
OP_CMPU: ('cmpu({a}, {b})', 7, False),
|
||||
OP_SHL: ('{a} << {b}', 4, True),
|
||||
OP_SHRU: ('{a} u>> {b}', 4, True),
|
||||
OP_SHR: ('{a} >> {b}', 4, True),
|
||||
}
|
||||
|
||||
DEFAULT_INDENT_STR = ' '
|
||||
|
||||
|
||||
class Node:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def _make_node(self, value):
|
||||
if isinstance(value, int):
|
||||
return Value(value)
|
||||
assert isinstance(value, Node)
|
||||
return value
|
||||
|
||||
def __add__(self, other):
|
||||
return Expr(OP_ADD, self, self._make_node(other))
|
||||
|
||||
def __sub__(self, other):
|
||||
return Expr(OP_SUB, self, self._make_node(other))
|
||||
|
||||
def store_temp(self, register):
|
||||
return Expr(OP_TSTO, self, self._make_node(register))
|
||||
|
||||
def store_perm(self, register):
|
||||
return Expr(OP_PSTO, self, self._make_node(register))
|
||||
|
||||
def format(self, parent_priority=0):
|
||||
raise NotImplementedError
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join(self.format())
|
||||
|
||||
def __repr__(self):
|
||||
return '; '.join(self.format())
|
||||
|
||||
def compile(self, register, shift=0, and_mask=0xffffffff):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Expr(Node):
|
||||
def __init__(self, op, a, b):
|
||||
self.op = op
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
def format(self, parent_priority=0):
|
||||
if self.op in OPERATORS:
|
||||
fmt, prio, bracket = OPERATORS[self.op]
|
||||
else:
|
||||
fmt, prio, bracket = f'{{a}} <{self.op:02x}> {{b}}', 1, True
|
||||
|
||||
ares = self.a.format(prio - 1)
|
||||
bres = self.b.format(prio - int(not bracket))
|
||||
assert len(bres) == 1, bres
|
||||
|
||||
if self.op == OP_INIT:
|
||||
ares.append(bres[0])
|
||||
return ares
|
||||
|
||||
res = fmt.format(a=ares[-1], b=bres[-1])
|
||||
if prio <= parent_priority:
|
||||
res = f'({res})'
|
||||
ares[-1] = res
|
||||
return ares
|
||||
|
||||
def compile(self, register, shift=0, and_mask=0xffffffff):
|
||||
is_value, b_code = self.b.compile(register, shift, and_mask)
|
||||
if is_value:
|
||||
# Second arg is a simple value, do operation directly
|
||||
res = self.a.compile(register, shift, and_mask)[1]
|
||||
res += bytes((self.op,))
|
||||
res += b_code
|
||||
return False, res
|
||||
|
||||
# Calculate secord arg first and store in in a temp var
|
||||
res += b_code
|
||||
res += struct.pack('<BBBIB', OP_TSTO, 0x1a, 0x20, register, OP_INIT)
|
||||
res += self.a.compile(register + 1, shift, and_mask)[1]
|
||||
res += struct.pack('<BBBI', self.op, 0x7d, 0x20, 0xffffffff)
|
||||
return False, res
|
||||
|
||||
|
||||
class Value(Node):
|
||||
def __init__(self, value):
|
||||
super().__init__()
|
||||
self.value = value
|
||||
|
||||
def format(self, parent_priority=0):
|
||||
return [str(self.value)]
|
||||
|
||||
def compile(self, register, shift=0, and_mask=0xffffffff):
|
||||
assert shift < 0x20, shift
|
||||
assert and_mask <= 0xffffffff, and_mask
|
||||
valueadj = (self.value >> shift) & and_mask
|
||||
return True, struct.pack('<BBI', 0x1a, 0x20, valueadj)
|
||||
|
||||
|
||||
class Var(Node):
|
||||
def __init__(self, name):
|
||||
super().__init__()
|
||||
self.name = name
|
||||
|
||||
def format(self, parent_priority=0):
|
||||
return [self.name]
|
||||
|
||||
def compile(self, register, shift=0, and_mask=0xffffffff):
|
||||
var_data = VARS = {
|
||||
'tile_slope': (0x41, 8, 0x1f)
|
||||
}.get(self.name)
|
||||
if var_data is None:
|
||||
raise ValueError(f'Unknown variable `{self.name}`')
|
||||
and_mask &= var_data[2] >> shift
|
||||
shift += var_data[1]
|
||||
assert shift < 0x20, shift
|
||||
assert and_mask <= 0xffffffff, and_mask
|
||||
return True, struct.pack('<BBI', var_data[0], 0x20 | shift, and_mask)
|
||||
|
||||
|
||||
class Temp(Node):
|
||||
def __init__(self, register):
|
||||
super().__init__()
|
||||
self.register = register
|
||||
|
||||
def format(self, parent_priority=0):
|
||||
return [f'TEMP[{self.register}]']
|
||||
|
||||
def compile(self, register, shift=0, and_mask=0xffffffff):
|
||||
assert shift < 0x20, shift
|
||||
assert and_mask <= 0xffffffff, and_mask
|
||||
return True, struct.pack('<BBI', 0x7d, 0x20 | shift, and_mask)
|
||||
|
||||
|
||||
class Perm(Node):
|
||||
def __init__(self, register):
|
||||
super().__init__()
|
||||
self.register = register
|
||||
|
||||
def format(self, parent_priority=0):
|
||||
return [f'PERM[{self.register}]']
|
||||
|
||||
def compile(self, register, shift=0, and_mask=0xffffffff):
|
||||
assert shift < 0x20, shift
|
||||
assert and_mask <= 0xffffffff, and_mask
|
||||
return True, struct.pack('<BBI', 0x7c, 0x20 | shift, and_mask)
|
||||
|
||||
|
||||
class Call(Node):
|
||||
def __init__(self, subroutine):
|
||||
assert 0 <= subroutine < 256, subroutine
|
||||
self.subroutine = subroutine
|
||||
|
||||
def format(self, parent_priority=0):
|
||||
return [f'call({self.subroutine})']
|
||||
|
||||
def compile(self, register, shift=0, and_mask=0xffffffff):
|
||||
assert shift < 0x20, shift
|
||||
assert and_mask <= 0xffffffff, and_mask
|
||||
return True, struct.pack('<BBBI', 0x7e, self.subroutine, 0x20 | shift, and_mask)
|
||||
|
||||
|
||||
tokens = (
|
||||
'NAME', 'NUMBER', 'NEWLINE',
|
||||
'ADD', 'SUB', 'MUL',
|
||||
'BINAND', 'BINOR', 'BINXOR',
|
||||
'ASSIGN', 'COMMA',
|
||||
'LPAREN', 'RPAREN', 'LBRACKET', 'RBRACKET',
|
||||
)
|
||||
|
||||
# Tokens
|
||||
|
||||
t_ADD = r'\+'
|
||||
t_SUB = r'-'
|
||||
t_MUL = r'\*'
|
||||
# t_DIV = r'/'
|
||||
# t_MOD = r'%'
|
||||
t_BINAND = r'\&'
|
||||
t_BINOR = r'\|'
|
||||
t_BINXOR = r'\^'
|
||||
t_ASSIGN = r'='
|
||||
t_COMMA = r','
|
||||
t_LPAREN = r'\('
|
||||
t_RPAREN = r'\)'
|
||||
t_LBRACKET = r'\['
|
||||
t_RBRACKET = r'\]'
|
||||
t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
|
||||
|
||||
|
||||
def t_NUMBER(t):
|
||||
r'\d+'
|
||||
try:
|
||||
t.value = int(t.value)
|
||||
except ValueError:
|
||||
print("Integer value too large %d", t.value)
|
||||
t.value = 0
|
||||
return t
|
||||
|
||||
|
||||
# Ignored characters
|
||||
t_ignore = ' \t'
|
||||
|
||||
|
||||
def t_NEWLINE(t):
|
||||
r'\n+'
|
||||
t.lexer.lineno += t.value.count("\n")
|
||||
return t
|
||||
|
||||
|
||||
def t_error(t):
|
||||
print("Illegal character '%s'" % t.value[0])
|
||||
t.lexer.skip(1)
|
||||
|
||||
|
||||
# Parsing rules
|
||||
|
||||
precedence = (
|
||||
('left', 'BINOR'),
|
||||
('left', 'BINXOR'),
|
||||
('left', 'BINAND'),
|
||||
('left', 'ADD', 'SUB'),
|
||||
('left', 'MUL'),
|
||||
# ('right', 'UMINUS'),
|
||||
)
|
||||
|
||||
|
||||
def p_lines(t):
|
||||
'''lines : line
|
||||
| expression
|
||||
| lines line
|
||||
'''
|
||||
# print('LINES', t[1])
|
||||
if len(t) == 2:
|
||||
lines, line = [], t[1]
|
||||
else:
|
||||
lines, line = t[1], t[2]
|
||||
|
||||
if line is not None:
|
||||
lines.append(line)
|
||||
t[0] = lines
|
||||
|
||||
|
||||
def p_line(t):
|
||||
'''line : expression NEWLINE
|
||||
| NEWLINE
|
||||
'''
|
||||
# print('LINE', t[1], len(t))
|
||||
if len(t) == 2:
|
||||
t[0] = None
|
||||
else:
|
||||
t[0] = t[1]
|
||||
|
||||
|
||||
# def p_statement_expr(t):
|
||||
# 'statement : expression'
|
||||
# t[0] = t[1]
|
||||
|
||||
|
||||
# def p_storagge(t):
|
||||
# 'storage : NAME LBRACKET NUMBER RBRACKET'
|
||||
# storage = {
|
||||
# 'TEMP': grf.Temp,
|
||||
# 'PERM': grf.Perm,
|
||||
# }.get(t[1])
|
||||
# assert storage is not None, t[1]
|
||||
# register = int(t[3])
|
||||
|
||||
# t[0] = storage(grf.Value(register))
|
||||
|
||||
|
||||
def p_expression_binop(t):
|
||||
'''expression : expression ADD expression
|
||||
| expression SUB expression
|
||||
| expression MUL expression
|
||||
| expression BINAND expression
|
||||
| expression BINOR expression
|
||||
| expression BINXOR expression
|
||||
'''
|
||||
op = {
|
||||
'+': OP_ADD,
|
||||
'-': OP_SUB,
|
||||
'*': OP_MUL,
|
||||
'&': OP_BINAND,
|
||||
'|': OP_BINOR,
|
||||
'^': OP_BINXOR,
|
||||
}.get(t[2])
|
||||
|
||||
assert op is not None, t[2]
|
||||
# print(op, t[1], t[3])
|
||||
t[0] = Expr(op, t[1], t[3])
|
||||
|
||||
|
||||
# def p_storagge(t):
|
||||
# 'storage : NAME LBRACKET NUMBER RBRACKET'
|
||||
# storage = {
|
||||
# 'TEMP': grf.Temp,
|
||||
# 'PERM': grf.Perm,
|
||||
# }.get(t[1])
|
||||
# assert storage is not None, t[1]
|
||||
# register = int(t[3])
|
||||
|
||||
# t[0] = storage(grf.Value(register))
|
||||
|
||||
|
||||
def p_expression_assign(t):
|
||||
'expression : NAME LBRACKET NUMBER RBRACKET ASSIGN expression'
|
||||
assert t[1] in ('TEMP', 'PERM'), t[1]
|
||||
op = OP_TSTO if t[1] == 'TEMP' else OP_PSTO
|
||||
register = int(t[3])
|
||||
t[0] = Expr(op, t[6], Value(register))
|
||||
|
||||
|
||||
# def p_expression_uminus(t):
|
||||
# 'expression : SUB expression %prec UMINUS'
|
||||
# t[0] = -t[2]
|
||||
|
||||
def p_expression_call1(t):
|
||||
'expression : NAME LPAREN NUMBER RPAREN'
|
||||
assert t[1] == 'call', t[1]
|
||||
t[0] = Call(int(t[3]))
|
||||
|
||||
|
||||
def p_expression_call2(t):
|
||||
'expression : NAME LPAREN expression COMMA expression RPAREN'
|
||||
op = {
|
||||
'min': OP_MIN,
|
||||
'max': OP_MAX,
|
||||
'minu': OP_MINU,
|
||||
'maxu': OP_MAXU,
|
||||
'rot': OP_ROT,
|
||||
'cmp': OP_CMP,
|
||||
'cmpu': OP_CMPU,
|
||||
}.get(t[1])
|
||||
assert op is not None, t[1]
|
||||
t[0] = Expr(op, t[3], t[5])
|
||||
|
||||
|
||||
def p_expression_group(t):
|
||||
'expression : LPAREN expression RPAREN'
|
||||
t[0] = t[2]
|
||||
|
||||
|
||||
def p_expression_storage(t):
|
||||
'expression : NAME LBRACKET NUMBER RBRACKET'
|
||||
assert t[1] in ('TEMP', 'PERM'), t[1]
|
||||
cls = Temp if t[1] == 'TEMP' else Perm
|
||||
register = int(t[3])
|
||||
t[0] = cls(Value(register))
|
||||
|
||||
|
||||
def p_expression_number(t):
|
||||
'expression : NUMBER'
|
||||
t[0] = Value(int(t[1]))
|
||||
|
||||
|
||||
def p_expression_name(t):
|
||||
'expression : NAME'
|
||||
t[0] = Var(t[1])
|
||||
|
||||
|
||||
def p_error(t):
|
||||
# stack_state_str = ' '.join([symbol.type for symbol in parser.symstack][1:])
|
||||
|
||||
# print('Syntax error in input! Parser State:{} {} . {}'
|
||||
# .format(parser.state,
|
||||
# stack_state_str,
|
||||
# t))
|
||||
|
||||
if t is None:
|
||||
print("Unexpected syntax error")
|
||||
return
|
||||
|
||||
print(f'Syntax error at `{t.value}` line {t.lineno}')
|
||||
|
||||
|
||||
def parse_code(code):
|
||||
lexer = ply.lex.lex()
|
||||
parser = ply.yacc.yacc()
|
||||
return parser.parse(code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
res = parse_code('''
|
||||
TEMP[128] = (cmp(tile_slope, 30) & 1) * 18
|
||||
TEMP[129] = (cmp(tile_slope, 29) & 1) * 15
|
||||
TEMP[130] = (cmp(tile_slope, 27) & 1) * 17
|
||||
TEMP[131] = (cmp(tile_slope, 23) & 1) * 16
|
||||
TEMP[132] = min(cmp(tile_slope, 0), 1)
|
||||
TEMP[134] = (min((cmp(tile_slope, 14) ^ 2), 1) & TEMP[132]) * tile_slope + TEMP[131] + TEMP[130] + TEMP[129] + TEMP[128]
|
||||
''')
|
||||
|
||||
for line in res:
|
||||
print(line.format())
|
||||
48
grf/alpine/parsetab.py
Normal file
48
grf/alpine/parsetab.py
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
# parsetab.py
|
||||
# This file is automatically generated. Do not edit.
|
||||
# pylint: disable=W,C,R
|
||||
_tabversion = '3.10'
|
||||
|
||||
_lr_method = 'LALR'
|
||||
|
||||
_lr_signature = 'leftBINORleftBINXORleftBINANDleftADDSUBleftMULADD ASSIGN BINAND BINOR BINXOR COMMA LBRACKET LPAREN MUL NAME NEWLINE NUMBER RBRACKET RPAREN SUBlines : line\n | expression\n | lines line\n line : expression NEWLINE\n | NEWLINE\n expression : expression ADD expression\n | expression SUB expression\n | expression MUL expression\n | expression BINAND expression\n | expression BINOR expression\n | expression BINXOR expression\n expression : NAME LBRACKET NUMBER RBRACKET ASSIGN expressionexpression : NAME LPAREN NUMBER RPARENexpression : NAME LPAREN expression COMMA expression RPARENexpression : LPAREN expression RPARENexpression : NAME LBRACKET NUMBER RBRACKETexpression : NUMBERexpression : NAME'
|
||||
|
||||
_lr_action_items = {'NEWLINE':([0,1,2,3,4,5,6,8,9,10,20,21,22,23,24,25,29,30,31,35,36,],[4,4,-1,10,-5,-18,-17,-3,10,-4,-6,-7,-8,-9,-10,-11,-15,-16,-13,-12,-14,]),'NAME':([0,1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,18,20,21,22,23,24,25,29,30,31,32,33,35,36,],[5,5,-1,-2,-5,-18,-17,5,-3,-4,5,5,5,5,5,5,5,-6,-7,-8,-9,-10,-11,-15,-16,-13,5,5,-12,-14,]),'LPAREN':([0,1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,18,20,21,22,23,24,25,29,30,31,32,33,35,36,],[7,7,-1,-2,-5,18,-17,7,-3,-4,7,7,7,7,7,7,7,-6,-7,-8,-9,-10,-11,-15,-16,-13,7,7,-12,-14,]),'NUMBER':([0,1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17,18,20,21,22,23,24,25,29,30,31,32,33,35,36,],[6,6,-1,-2,-5,-18,-17,6,-3,-4,6,6,6,6,6,6,26,27,-6,-7,-8,-9,-10,-11,-15,-16,-13,6,6,-12,-14,]),'$end':([1,2,3,4,5,6,8,10,20,21,22,23,24,25,29,30,31,35,36,],[0,-1,-2,-5,-18,-17,-3,-4,-6,-7,-8,-9,-10,-11,-15,-16,-13,-12,-14,]),'ADD':([3,5,6,9,19,20,21,22,23,24,25,27,28,29,30,31,34,35,36,],[11,-18,-17,11,11,-6,-7,-8,11,11,11,-17,11,-15,-16,-13,11,11,-14,]),'SUB':([3,5,6,9,19,20,21,22,23,24,25,27,28,29,30,31,34,35,36,],[12,-18,-17,12,12,-6,-7,-8,12,12,12,-17,12,-15,-16,-13,12,12,-14,]),'MUL':([3,5,6,9,19,20,21,22,23,24,25,27,28,29,30,31,34,35,36,],[13,-18,-17,13,13,13,13,-8,13,13,13,-17,13,-15,-16,-13,13,13,-14,]),'BINAND':([3,5,6,9,19,20,21,22,23,24,25,27,28,29,30,31,34,35,36,],[14,-18,-17,14,14,-6,-7,-8,-9,14,14,-17,14,-15,-16,-13,14,14,-14,]),'BINOR':([3,5,6,9,19,20,21,22,23,24,25,27,28,29,30,31,34,35,36,],[15,-18,-17,15,15,-6,-7,-8,-9,-10,-11,-17,15,-15,-16,-13,15,15,-14,]),'BINXOR':([3,5,6,9,19,20,21,22,23,24,25,27,28,29,30,31,34,35,36,],[16,-18,-17,16,16,-6,-7,-8,-9,16,-11,-17,16,-15,-16,-13,16,16,-14,]),'LBRACKET':([5,],[17,]),'RPAREN':([5,6,19,20,21,22,23,24,25,27,29,30,31,34,35,36,],[-18,-17,29,-6,-7,-8,-9,-10,-11,31,-15,-16,-13,36,-12,-14,]),'COMMA':([5,6,20,21,22,23,24,25,27,28,29,30,31,35,36,],[-18,-17,-6,-7,-8,-9,-10,-11,-17,32,-15,-16,-13,-12,-14,]),'RBRACKET':([26,],[30,]),'ASSIGN':([30,],[33,]),}
|
||||
|
||||
_lr_action = {}
|
||||
for _k, _v in _lr_action_items.items():
|
||||
for _x,_y in zip(_v[0],_v[1]):
|
||||
if not _x in _lr_action: _lr_action[_x] = {}
|
||||
_lr_action[_x][_k] = _y
|
||||
del _lr_action_items
|
||||
|
||||
_lr_goto_items = {'lines':([0,],[1,]),'line':([0,1,],[2,8,]),'expression':([0,1,7,11,12,13,14,15,16,18,32,33,],[3,9,19,20,21,22,23,24,25,28,34,35,]),}
|
||||
|
||||
_lr_goto = {}
|
||||
for _k, _v in _lr_goto_items.items():
|
||||
for _x, _y in zip(_v[0], _v[1]):
|
||||
if not _x in _lr_goto: _lr_goto[_x] = {}
|
||||
_lr_goto[_x][_k] = _y
|
||||
del _lr_goto_items
|
||||
_lr_productions = [
|
||||
("S' -> lines","S'",1,None,None,None),
|
||||
('lines -> line','lines',1,'p_lines','parser.py',272),
|
||||
('lines -> expression','lines',1,'p_lines','parser.py',273),
|
||||
('lines -> lines line','lines',2,'p_lines','parser.py',274),
|
||||
('line -> expression NEWLINE','line',2,'p_line','parser.py',288),
|
||||
('line -> NEWLINE','line',1,'p_line','parser.py',289),
|
||||
('expression -> expression ADD expression','expression',3,'p_expression_binop','parser.py',316),
|
||||
('expression -> expression SUB expression','expression',3,'p_expression_binop','parser.py',317),
|
||||
('expression -> expression MUL expression','expression',3,'p_expression_binop','parser.py',318),
|
||||
('expression -> expression BINAND expression','expression',3,'p_expression_binop','parser.py',319),
|
||||
('expression -> expression BINOR expression','expression',3,'p_expression_binop','parser.py',320),
|
||||
('expression -> expression BINXOR expression','expression',3,'p_expression_binop','parser.py',321),
|
||||
('expression -> NAME LBRACKET NUMBER RBRACKET ASSIGN expression','expression',6,'p_expression_assign','parser.py',350),
|
||||
('expression -> NAME LPAREN NUMBER RPAREN','expression',4,'p_expression_call1','parser.py',362),
|
||||
('expression -> NAME LPAREN expression COMMA expression RPAREN','expression',6,'p_expression_call2','parser.py',368),
|
||||
('expression -> LPAREN expression RPAREN','expression',3,'p_expression_group','parser.py',383),
|
||||
('expression -> NAME LBRACKET NUMBER RBRACKET','expression',4,'p_expression_storage','parser.py',388),
|
||||
('expression -> NUMBER','expression',1,'p_expression_number','parser.py',396),
|
||||
('expression -> NAME','expression',1,'p_expression_name','parser.py',401),
|
||||
]
|
||||
@@ -2,7 +2,7 @@ import sys
|
||||
import struct
|
||||
from nml import lz77
|
||||
|
||||
from grf import Node, Expr, Value
|
||||
from grf import Node, Expr, Value, Var, Temp, Perm, Range, Set, Call
|
||||
|
||||
|
||||
def hex_str(s):
|
||||
@@ -270,33 +270,6 @@ def read_sprite_layout_registers(d, flags, is_parent):
|
||||
return regs
|
||||
|
||||
|
||||
class Set:
|
||||
def __init__(self, set_id):
|
||||
self.is_callback = bool(set_id & 0x8000)
|
||||
self.set_id = set_id & 0x7fff
|
||||
|
||||
def __str__(self):
|
||||
if self.is_callback:
|
||||
return f'CB({self.set_id})'
|
||||
return f'Set({self.set_id})'
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
class Range:
|
||||
def __init__(self, low, high, set):
|
||||
self.set = set
|
||||
self.low = low
|
||||
self.high = high
|
||||
|
||||
def __str__(self):
|
||||
if self.low == self.high:
|
||||
return f'{self.low} -> {self.set}'
|
||||
return f'{self.low}..{self.high} -> {self.set}'
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
def read_sprite_layout(d, num, no_z_position):
|
||||
has_z_position = not no_z_position
|
||||
has_flags = bool((num >> 6) & 1)
|
||||
@@ -427,11 +400,6 @@ def get_va2_var(var):
|
||||
return V2_OBJECT_VARS[var]
|
||||
|
||||
|
||||
class Call(Node):
|
||||
def __init__(self, subroutine):
|
||||
self.suroutine = subroutine
|
||||
|
||||
|
||||
class Generic(Node):
|
||||
def __init__(self, var, shift, and_mask, type, add_val, divmod_val):
|
||||
self.var = var
|
||||
@@ -441,13 +409,13 @@ class Generic(Node):
|
||||
self.add_val = add_val
|
||||
self.divmod_val = divmod_val
|
||||
|
||||
def format(self, parent_priority=0, indent=0, indent_str=' '):
|
||||
def format(self, parent_priority=0):
|
||||
addstr = ''
|
||||
if self.type == 1:
|
||||
addstr = ' +{self.add_val} /{self.divmod_val}'
|
||||
addstr = f' +{self.add_val} /{self.divmod_val}'
|
||||
elif self.type == 2:
|
||||
addstr = ' +{self.add_val} %{self.divmod_val}'
|
||||
return (indent_str * indent) + f'(var: {self.var} >>{self.shift} &{self.and_mask}{addstr})'
|
||||
addstr = f' +{self.add_val} %{self.divmod_val}'
|
||||
return [f'(var{self.var:02x} >>{self.shift} &{self.and_mask:x}{addstr})']
|
||||
|
||||
|
||||
def decode_action2(data):
|
||||
@@ -466,7 +434,7 @@ def decode_action2(data):
|
||||
print(f'BASIC ground_sprite:{ground_sprite} building_sprite:{building_sprite} '
|
||||
f'xofs:{xofs} yofs:{yofs} extent:({xext}, {yext}, {zext})')
|
||||
return
|
||||
if num_ent1 < 0x3f:
|
||||
if num_ent1 <= 0x3f:
|
||||
raise NotImplemented
|
||||
|
||||
if num_ent1 in (0x81, 0x82, 0x85, 0x86, 0x89, 0x8a):
|
||||
@@ -486,6 +454,7 @@ def decode_action2(data):
|
||||
has_more = bool(varadj & 0x20)
|
||||
node_type = varadj >> 6
|
||||
and_mask = d.get_var(group_size)
|
||||
# print(f'CODE op:{op:x} var{var:02x} >>{shift} &{and_mask:x} has_more:{has_more} node_type:{node_type}')
|
||||
if node_type != 0:
|
||||
# old magic, use advaction2 instead
|
||||
add_val = d.get_var(group_size)
|
||||
@@ -493,6 +462,14 @@ def decode_action2(data):
|
||||
node = Generic(var, shift, and_mask, node_type, add_val, divmod_val)
|
||||
elif var == 0x1a and shift == 0:
|
||||
node = Value(and_mask)
|
||||
elif (var, shift, and_mask) == (0x7c, 0, 0xffffffff):
|
||||
node = Perm(param)
|
||||
elif (var, shift, and_mask) == (0x7d, 0, 0xffffffff):
|
||||
node = Temp(param)
|
||||
elif (var, shift, and_mask) == (0x7e, 0, 0xffffffff):
|
||||
node = Call(param)
|
||||
elif (var, shift, and_mask) == (0x41, 8, 0x1f):
|
||||
node = Var('tile_slope')
|
||||
else:
|
||||
node = Generic(var, shift, and_mask, 0, None, None)
|
||||
|
||||
@@ -533,8 +510,8 @@ def decode_action2(data):
|
||||
# if a['type'] != 0:
|
||||
# type_str = '+{add_val} /%{divmod_val}'.format(**a)
|
||||
# print(f' op<{a["op"]}>:{op} var<{var:02x}>:{name}({fmt}){param_str} type:{a["type"]} >>{a["shift_num"]} &{a["and_mask"]:x}{type_str}')
|
||||
print()
|
||||
print(root.format(indent=1))
|
||||
for line in root.format():
|
||||
print(' ', line)
|
||||
return
|
||||
|
||||
layout = read_sprite_layout(d, max(num_ent1, 1), num_ent1 == 0)
|
||||
@@ -684,6 +661,7 @@ ACTIONS = {
|
||||
0x14: decode_action14,
|
||||
}
|
||||
|
||||
|
||||
def read_pseudo_sprite(f, nfo_line):
|
||||
l = struct.unpack('<I', f.read(4))[0]
|
||||
if l == 0:
|
||||
@@ -699,6 +677,7 @@ def read_pseudo_sprite(f, nfo_line):
|
||||
decoder(data[1:])
|
||||
return True
|
||||
|
||||
|
||||
def decode_sprite(f, num):
|
||||
data = b''
|
||||
while num > 0:
|
||||
@@ -720,6 +699,7 @@ def decode_sprite(f, num):
|
||||
if num != 0: raise RuntimeError('Corrupt sprite')
|
||||
return data
|
||||
|
||||
|
||||
def read_real_sprite(f):
|
||||
sprite_id = struct.unpack('<I', f.read(4))[0]
|
||||
if sprite_id == 0:
|
||||
@@ -742,6 +722,7 @@ def read_real_sprite(f):
|
||||
f.seek(start_pos + num - 1, 0)
|
||||
return True
|
||||
|
||||
|
||||
with open(sys.argv[1], 'rb') as f:
|
||||
print('Header:', hex_str(f.read(10)))
|
||||
data_offest, compression = struct.unpack('<IB', f.read(5))
|
||||
|
||||
Reference in New Issue
Block a user