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')