256 lines
7.5 KiB
Python
256 lines
7.5 KiB
Python
from pathlib import Path
|
|
|
|
import numpy as np
|
|
import spectra
|
|
from PIL import Image
|
|
|
|
import grf
|
|
|
|
OGFX_PATH = Path('opengfx-0.5.2')
|
|
OGFX_BASE_PATH = OGFX_PATH / 'ogfx1_base.grf'
|
|
OGFX_EXTRA_BASE_PATH = OGFX_PATH / 'ogfxe_extra.grf'
|
|
THIS_FILE = grf.PythonFile(__file__)
|
|
|
|
# OGFX_BASE_PATH = Path('OpenGFX_BigGUI-2.0.0/ogfx-biggui.grf')
|
|
|
|
class GRFFile(grf.LoadedResourceFile):
|
|
def __init__(self, path):
|
|
self.path = path
|
|
self.real_sprites = None
|
|
self.load()
|
|
|
|
def load(self):
|
|
if self.real_sprites is not None:
|
|
return
|
|
print(f'Decompiling {self.path}...')
|
|
self.context = grf.decompile.ParsingContext()
|
|
self.f = open(self.path, 'rb')
|
|
self.g, self.container, self.real_sprites = grf.decompile.read(self.f, self.context)
|
|
|
|
def unload(self):
|
|
self.context = None
|
|
self.gen = None
|
|
self.container = None
|
|
self.real_sprites = None
|
|
self.f.close()
|
|
|
|
def get_sprite_data(self, sprite_id, num):
|
|
s = self.real_sprites[sprite_id + 1][num]
|
|
self.f.seek(s.offset)
|
|
data, _ = grf.decompile.decode_sprite(self.f, s, self.container)
|
|
return data
|
|
|
|
def get_sprites(self, sprite_id):
|
|
return [
|
|
GRFSprite(self, s.type, s.bpp, sprite_id, i, w=s.width, h=s.height, xofs=s.xofs, yofs=s.yofs, zoom=s.zoom, crop=False)
|
|
for i, s in enumerate(self.real_sprites[sprite_id + 1])
|
|
]
|
|
|
|
|
|
class GRFSprite(grf.Sprite):
|
|
|
|
class Mask(grf.ImageMask):
|
|
def get_fingerprint(self):
|
|
return True
|
|
|
|
def __init__(self, file, type, grf_bpp, sprite_id, num, *args, **kw):
|
|
super().__init__(*args, **kw)
|
|
self.file = file
|
|
self.type = type
|
|
self.grf_bpp = grf_bpp
|
|
self.sprite_id = sprite_id
|
|
self.num = num # index with the same sprite_id
|
|
|
|
bpp = grf_bpp
|
|
if self.type & 0x04:
|
|
bpp -= 1
|
|
|
|
if bpp == 3:
|
|
self.bpp = grf.BPP_24
|
|
elif bpp == 4:
|
|
self.bpp = grf.BPP_32
|
|
else:
|
|
self.bpp = grf.BPP_8
|
|
|
|
self._image = None
|
|
|
|
def get_resource_files(self):
|
|
return (self.file, THIS_FILE)
|
|
|
|
def get_fingerprint(self):
|
|
return {
|
|
'class': self.__class__.__name__,
|
|
'sprite_id': self.sprite_id,
|
|
}
|
|
|
|
def load(self):
|
|
if self._image is not None:
|
|
return
|
|
|
|
data = self.file.get_sprite_data(self.sprite_id, self.num)
|
|
|
|
a = np.frombuffer(data, dtype=np.uint8)
|
|
assert a.size == self.w * self.h * self.grf_bpp, (a.size, self.w, self.h, self.grf_bpp)
|
|
|
|
bpp = self.grf_bpp
|
|
a.shape = (self.h, self.w, bpp)
|
|
|
|
mask = None
|
|
if self.type & 0x04 > 0:
|
|
bpp -= 1
|
|
mask = Image.fromarray(a[:, :, -1], mode='P')
|
|
mask.putpalette(grf.PALETTE)
|
|
|
|
if bpp == 3:
|
|
self._image = Image.fromarray(a[:, :, :bpp], mode='RGB'), grf.BPP_24
|
|
elif bpp == 4:
|
|
self._image = Image.fromarray(a[:, :, :bpp], mode='RGBA'), grf.BPP_32
|
|
else:
|
|
self._image = mask, grf.BPP_8
|
|
mask = None
|
|
|
|
if mask is not None:
|
|
self.mask = self.Mask(mask)
|
|
|
|
# return self._image[0].show()
|
|
|
|
def get_image(self):
|
|
self.load()
|
|
return self._image
|
|
|
|
|
|
def gen_recolour(color_func):
|
|
out = np.arange(256, dtype=np.uint8)
|
|
for i, c in grf.SPECTRA_PALETTE.items():
|
|
if i not in grf.SAFE_COLOURS:
|
|
continue
|
|
out[i] = grf.find_best_color(color_func(c))
|
|
return out
|
|
|
|
|
|
def gen_tint(tint, ratio):
|
|
return lambda x: x.blend(tint, ratio=ratio)
|
|
|
|
GREEN = gen_recolour(gen_tint(spectra.rgb(0, 1, 0), 0.5))
|
|
|
|
|
|
class RecolourSprite(grf.Sprite):
|
|
def __init__(self, sprite, recolour):
|
|
super().__init__(sprite.w, sprite.h, xofs=sprite.xofs, yofs=sprite.yofs, bpp=sprite.bpp, zoom=sprite.zoom, crop=sprite.crop)
|
|
self.sprite = sprite
|
|
self.recolour = recolour
|
|
self._image = None
|
|
|
|
def get_resource_files(self):
|
|
return self.sprite.get_resource_files()
|
|
|
|
def get_fingerprint(self):
|
|
return grf.combine_fingerprint(
|
|
super().get_fingerprint_base(),
|
|
sprite=self.sprite.get_fingerprint,
|
|
recolour=True,
|
|
)
|
|
|
|
def get_image(self):
|
|
if self._image is not None:
|
|
return self._image
|
|
im, bpp = self.sprite.get_image()
|
|
assert bpp == 8
|
|
data = np.array(im)
|
|
data = self.recolour[data]
|
|
im2 = Image.fromarray(data)
|
|
im2.putpalette(im.getpalette())
|
|
self._image = im2, grf.BPP_8
|
|
return self._image
|
|
|
|
|
|
g = grf.NewGRF(
|
|
grfid=b'CMU1',
|
|
name='OpenGFX1 GUI sprites',
|
|
description='Only GUI sprites from OpenGFX1. For those like me who don''t want to wait for baseset parameters',
|
|
version=0,
|
|
min_compatible_version=0,
|
|
)
|
|
|
|
ogfx = GRFFile(OGFX_BASE_PATH)
|
|
ogfx_extra = GRFFile(OGFX_EXTRA_BASE_PATH)
|
|
|
|
# bui = GRFFile('OpenGFX_BigGUI-2.0.0/ogfx-biggui.grf')
|
|
# ogfx_extra.load()
|
|
# for s in ogfx_extra.g.generators:
|
|
# if isinstance(s, grf.ReplaceOldSprites):
|
|
# print('OLD', s.sets)
|
|
# if isinstance(s, grf.ReplaceNewSprites):
|
|
# print('NEW', s.set_type, s.count, s.offset)
|
|
|
|
def replace_old_sprites(first_id, amount):
|
|
g.add(grf.ReplaceOldSprites([(first_id, amount)]))
|
|
res = []
|
|
for i in range(amount):
|
|
sprites = ogfx.get_sprites(first_id + i)
|
|
# sprites = [RecolourSprite(s, GREEN) for s in sprites]
|
|
res.append(grf.AlternativeSprites(*sprites))
|
|
g.add(*res)
|
|
return res
|
|
|
|
|
|
def replace_new_sprites(set_type, count, offset, first_id):
|
|
g.add(grf.ReplaceNewSprites(set_type, count, offset=offset))
|
|
res = []
|
|
for i in range(count):
|
|
sprites = ogfx_extra.get_sprites(first_id + i)
|
|
# sprites = [RecolourSprite(s, GREEN) for s in sprites]
|
|
res.append(grf.AlternativeSprites(*sprites))
|
|
g.add(*res)
|
|
return res
|
|
|
|
|
|
replace_new_sprites(11, 4, None, 2488)
|
|
# replace_new_sprites(20, 36, None, 2488+112+15+55)
|
|
# replace_new_sprites(21, 137, 38, 2488+112+15+55+38+36+6)
|
|
# replace_new_sprites(21, 137, 38, 2750)
|
|
ofs, sprite = 38, 2750
|
|
# for x in [16] + [1] * 39 + [17, 4, 23, 1, 8, 1, 6, 9, 13]:
|
|
for x in [16] + [1] * 39 + [17, 4, 23, 1, 8, 1, 6, 9, 13]:
|
|
if sprite != 2831: # skip helipad
|
|
replace_new_sprites(21, x, ofs, sprite)
|
|
sprite += x + 1
|
|
ofs += x
|
|
|
|
# Mouse cursors
|
|
replace_old_sprites(0, 2)
|
|
|
|
# arrows and check marks
|
|
replace_old_sprites(130, 1)
|
|
replace_old_sprites(140, 1)
|
|
replace_old_sprites(142, 1)
|
|
replace_old_sprites(143, 1)
|
|
replace_old_sprites(145, 1)
|
|
|
|
|
|
replace_old_sprites(679, 73) # Base GUI
|
|
replace_old_sprites(1251, 12) # GUI rails
|
|
replace_old_sprites(1263, 12) # Rail cursors
|
|
replace_old_sprites(1291, 1) # Signal icon
|
|
replace_old_sprites(1292, 2) # Signal cursors
|
|
replace_old_sprites(1294, 2) # Depot icons
|
|
replace_old_sprites(1296, 2) # Depot cursors
|
|
replace_old_sprites(1298, 2) # Rail station icon
|
|
replace_old_sprites(1300, 1) # Rail station cursor
|
|
replace_old_sprites(1309, 2) # Road icons
|
|
replace_old_sprites(1311, 2) # Road cursors
|
|
replace_old_sprites(2429, 4) # Tunnel icons
|
|
replace_old_sprites(2433, 4) # Tunnel cursors
|
|
replace_old_sprites(2593, 1) # Bridge cursor
|
|
replace_old_sprites(2594, 7) # Bridge icons
|
|
replace_old_sprites(3090, 2) # Start-stop icons
|
|
replace_old_sprites(4077, 1) # Town icon
|
|
replace_old_sprites(4080, 2) # Town and industry cursors
|
|
replace_old_sprites(4082, 6) # Object toolbar icons
|
|
replace_old_sprites(4791, 1) # Land own icon
|
|
replace_old_sprites(4792, 1) # Land own cursor
|
|
|
|
grf.main(g, 'cm_ogfx1_gui.grf')
|
|
|
|
|