Merge branch 'atlas_tools' of Aerdan/smeargle into master
commit
9252c663d4
25
readme.txt
25
readme.txt
|
@ -1,4 +1,4 @@
|
|||
Smeargle 0.4.0 readme
|
||||
Smeargle 0.6.0 readme
|
||||
---------------------
|
||||
Usage: smeargle.py game.json
|
||||
|
||||
|
@ -15,6 +15,9 @@ Smeargle outputs three files per script, at present:
|
|||
* <script>_index.txt provides a mapping of deduplicated tiles to the original
|
||||
text.
|
||||
|
||||
These filenames can be configured on an individual script basis; see game.json
|
||||
documentation below.
|
||||
|
||||
game.json format
|
||||
----------------
|
||||
The following format MUST be observed, or you will not get the output you want.
|
||||
|
@ -28,8 +31,14 @@ in each object or array.
|
|||
"Melissa 8": "melissa8.json" // Font name and its filename.
|
||||
}, "scripts": {
|
||||
"test.txt": { // Script filename.
|
||||
"max_tiles_per_line": 8, // Omit or set to 0 for unlimited tiles.
|
||||
"font": "Melissa 8" // Reference to the font table, above.
|
||||
"font": "Melissa 8", // Reference to the font table, above.
|
||||
"max_tiles_per_line": 8, // Optional: set to 0 for unlimited tiles.
|
||||
"output_format": "thingy", // Optional: Output format for tilemap. "thingy", "atlas"
|
||||
"leading_zeroes": true, // Optional: Forces 16-bit tilemap output (i.e. 0x0012 instead of 0x12)
|
||||
"tile_offset": 256, // Optional: Constant to add to tile index (first tile: 0x0000 + 256 = 0x0100)
|
||||
"raw_fn": "ex_raw.png", // Optional: Output filename for raw graphic tile data.
|
||||
"deduped_fn": "ex_comp.png", // Optional: Output filename for deduped tile data.
|
||||
"tilemap_fn": "example.tbl" // Optional: Output filename for tilemap text.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +76,16 @@ arguments to see what formats are available.
|
|||
|
||||
Changelog
|
||||
---------
|
||||
0.6.0
|
||||
* Adds several optional arguments to script json elements:
|
||||
** output_format: determines how the tilemap text file gets rendered. Possible values are "atlas", "thingy", null
|
||||
** leading_zeroes: Forces tilemap output to always be 16-bit.
|
||||
** tile_offset: Adds a constant value to the tile index.
|
||||
Useful if you want the tilemap to start counting somewhere other than zero.
|
||||
** raw_fn: Filename for raw tile graphic png output.
|
||||
** deduped_fn: Filename for compressed tile graphic png output.
|
||||
** tilemap_fn: Filename for text index tilemap file.
|
||||
|
||||
0.5.0
|
||||
* Introduce a master 'game.json' file in order to enable batch processing, for
|
||||
games which use multiple scripts that have different fonts or rendering
|
||||
|
|
93
smeargle.py
93
smeargle.py
|
@ -1,13 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
from math import ceil
|
||||
from math import ceil, floor
|
||||
|
||||
from PyQt5.QtGui import QGuiApplication, QPixmap, QImage, QColor, QPainter
|
||||
|
||||
|
||||
class Font:
|
||||
"""A simple class for managing Smeargle's font data."""
|
||||
|
||||
def __init__(self, filename):
|
||||
"""Creates the font object.
|
||||
|
||||
|
@ -75,8 +76,15 @@ class Font:
|
|||
|
||||
|
||||
class Script:
|
||||
def __init__(self, filename, max_tiles=0):
|
||||
def __init__(self, filename, raw_fn=None, deduped_fn=None, tilemap_fn=None,
|
||||
max_tiles=0, output_format=None, tile_offset=0, leading_zeroes=False):
|
||||
self.max_tiles = max_tiles
|
||||
self.output_format = output_format
|
||||
self.tile_offset = tile_offset
|
||||
self.leading_zeroes = leading_zeroes
|
||||
self.raw_fn = raw_fn
|
||||
self.deduped_fn = deduped_fn
|
||||
self.tilemap_fn = tilemap_fn
|
||||
with open(filename, mode='r', encoding='UTF-8') as f:
|
||||
self._text = f.read().split('\n')
|
||||
|
||||
|
@ -117,8 +125,7 @@ class Script:
|
|||
|
||||
return lines
|
||||
|
||||
@staticmethod
|
||||
def generate_tilemap(font, lines):
|
||||
def generate_tilemap(self, font, lines):
|
||||
tilemap = {}
|
||||
raw_tiles = []
|
||||
compressed_tiles = []
|
||||
|
@ -152,7 +159,27 @@ class Script:
|
|||
if data not in tilemap.keys():
|
||||
tilemap[data] = tile
|
||||
compressed_tiles.append(tile)
|
||||
map_idx[data] = '0x{:02x}'.format(unique)
|
||||
if self.output_format == 'atlas':
|
||||
index = unique + self.tile_offset
|
||||
upper_val = int(floor(index / 256))
|
||||
lower_val = int(index % 256)
|
||||
if upper_val > 0 or self.leading_zeroes is True:
|
||||
map_idx[data] = "<${:02x}><${:02x}>".format(upper_val, lower_val)
|
||||
else:
|
||||
map_idx[data] = "<${:02x}>".format(lower_val)
|
||||
elif self.output_format == 'thingy':
|
||||
index = unique + self.tile_offset
|
||||
upper_val = int(floor(index / 256))
|
||||
lower_val = int(index % 256)
|
||||
if upper_val > 0 or self.leading_zeroes is True:
|
||||
map_idx[data] = "{:02x}{:02x}".format(upper_val, lower_val)
|
||||
else:
|
||||
map_idx[data] = "{:02x}".format(lower_val)
|
||||
else:
|
||||
if self.leading_zeroes:
|
||||
map_idx[data] = '0x{:04x}'.format(unique + self.tile_offset)
|
||||
else:
|
||||
map_idx[data] = '0x{:02x}'.format(unique + self.tile_offset)
|
||||
unique += 1
|
||||
|
||||
raw_tiles.append(tile)
|
||||
|
@ -161,7 +188,10 @@ class Script:
|
|||
column += font.width
|
||||
count -= 1
|
||||
|
||||
if self.output_format is None:
|
||||
indexes.append((text, ' '.join(tile_idx)))
|
||||
else:
|
||||
indexes.append((text, ''.join(tile_idx)))
|
||||
return compressed_tiles, raw_tiles, map_idx, indexes, total, unique
|
||||
|
||||
def render_tiles(self, font, tiles):
|
||||
|
@ -201,11 +231,36 @@ class Game:
|
|||
for name, file in self._data['fonts'].items():
|
||||
self._fonts[name] = Font(file)
|
||||
|
||||
valid_formats = ['thingy', 'atlas', None]
|
||||
defaults = {
|
||||
'max_tiles_per_line': 0,
|
||||
'output_format': None,
|
||||
'tile_offset': 0,
|
||||
'leading_zeroes': False,
|
||||
'raw_fn': None,
|
||||
'deduped_fn': None,
|
||||
'tilemap_fn': None
|
||||
}
|
||||
|
||||
for script, data in self._data['scripts'].items():
|
||||
if 'max_tiles_per_line' not in data:
|
||||
data['max_tiles_per_line'] = 0
|
||||
|
||||
# Add defaults to script data if not present
|
||||
for k, v in defaults.items():
|
||||
if k not in data:
|
||||
data[k] = v
|
||||
|
||||
if data['output_format'] not in valid_formats:
|
||||
raise ValueError("output_format must be one of {} or omitted entirely".format(valid_formats[:-1]))
|
||||
|
||||
self._scripts[script] = (
|
||||
Script(script, data['max_tiles_per_line']),
|
||||
Script(filename=script,
|
||||
raw_fn=data['raw_fn'],
|
||||
deduped_fn=data['deduped_fn'],
|
||||
tilemap_fn=data['tilemap_fn'],
|
||||
max_tiles=data['max_tiles_per_line'],
|
||||
output_format=data['output_format'],
|
||||
tile_offset=data['tile_offset'],
|
||||
leading_zeroes=data['leading_zeroes']),
|
||||
self._fonts[data['font']]
|
||||
)
|
||||
|
||||
|
@ -224,12 +279,23 @@ class Game:
|
|||
filebase = os.path.split(script)[-1]
|
||||
name, ext = os.path.splitext(filebase)
|
||||
|
||||
output_raw = os.path.join(render_path, name + '_raw.png')
|
||||
output_comp = os.path.join(render_path, name + '_compressed.png')
|
||||
output_map = os.path.join(render_path, name + '_index.txt')
|
||||
|
||||
script, font = self._scripts[script]
|
||||
|
||||
if script.raw_fn is None:
|
||||
output_raw = os.path.join(render_path, name + '_raw.png')
|
||||
else:
|
||||
output_raw = os.path.join(render_path, script.raw_fn)
|
||||
|
||||
if script.deduped_fn is None:
|
||||
output_comp = os.path.join(render_path, name + '_compressed.png')
|
||||
else:
|
||||
output_comp = os.path.join(render_path, script.deduped_fn)
|
||||
|
||||
if script.tilemap_fn is None:
|
||||
output_map = os.path.join(render_path, name + '_index.txt')
|
||||
else:
|
||||
output_map = os.path.join(render_path, script.tilemap_fn)
|
||||
|
||||
if output: print('Rendering text...')
|
||||
lines = script.render_lines(font)
|
||||
if output: print('Text rendered.')
|
||||
|
@ -249,6 +315,9 @@ class Game:
|
|||
if output: print('Writing map index...', end='')
|
||||
with open(output_map, mode='wt') as f:
|
||||
for text, index in indexes:
|
||||
if script.output_format == 'thingy':
|
||||
f.write('{}={}\n'.format(index, text))
|
||||
else:
|
||||
f.write('{} = {}\n'.format(text, index))
|
||||
if output: print('done.')
|
||||
|
||||
|
|
Loading…
Reference in New Issue