Merge branch 'atlas_tools' of Aerdan/smeargle into master

current
Aerdan 2018-06-06 15:41:53 -05:00 committed by Gitea
commit 9252c663d4
2 changed files with 109 additions and 21 deletions

View File

@ -1,4 +1,4 @@
Smeargle 0.4.0 readme Smeargle 0.6.0 readme
--------------------- ---------------------
Usage: smeargle.py game.json 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 * <script>_index.txt provides a mapping of deduplicated tiles to the original
text. text.
These filenames can be configured on an individual script basis; see game.json
documentation below.
game.json format game.json format
---------------- ----------------
The following format MUST be observed, or you will not get the output you want. The following format MUST be observed, or you will not get the output you want.
@ -23,13 +26,19 @@ this example for your own use. Do not leave a trailing comma on the final entry
in each object or array. in each object or array.
{ {
"name": "Example", // The name of the game, for reference. "name": "Example", // The name of the game, for reference.
"fonts": { "fonts": {
"Melissa 8": "melissa8.json" // Font name and its filename. "Melissa 8": "melissa8.json" // Font name and its filename.
}, "scripts": { }, "scripts": {
"test.txt": { // Script filename. "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 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 0.5.0
* Introduce a master 'game.json' file in order to enable batch processing, for * Introduce a master 'game.json' file in order to enable batch processing, for
games which use multiple scripts that have different fonts or rendering games which use multiple scripts that have different fonts or rendering

View File

@ -1,13 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import json import json
from math import ceil from math import ceil, floor
from PyQt5.QtGui import QGuiApplication, QPixmap, QImage, QColor, QPainter from PyQt5.QtGui import QGuiApplication, QPixmap, QImage, QColor, QPainter
class Font: class Font:
"""A simple class for managing Smeargle's font data.""" """A simple class for managing Smeargle's font data."""
def __init__(self, filename): def __init__(self, filename):
"""Creates the font object. """Creates the font object.
@ -75,8 +76,15 @@ class Font:
class Script: 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.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: with open(filename, mode='r', encoding='UTF-8') as f:
self._text = f.read().split('\n') self._text = f.read().split('\n')
@ -117,8 +125,7 @@ class Script:
return lines return lines
@staticmethod def generate_tilemap(self, font, lines):
def generate_tilemap(font, lines):
tilemap = {} tilemap = {}
raw_tiles = [] raw_tiles = []
compressed_tiles = [] compressed_tiles = []
@ -152,7 +159,27 @@ class Script:
if data not in tilemap.keys(): if data not in tilemap.keys():
tilemap[data] = tile tilemap[data] = tile
compressed_tiles.append(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 unique += 1
raw_tiles.append(tile) raw_tiles.append(tile)
@ -161,7 +188,10 @@ class Script:
column += font.width column += font.width
count -= 1 count -= 1
indexes.append((text, ' '.join(tile_idx))) 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 return compressed_tiles, raw_tiles, map_idx, indexes, total, unique
def render_tiles(self, font, tiles): def render_tiles(self, font, tiles):
@ -201,11 +231,36 @@ class Game:
for name, file in self._data['fonts'].items(): for name, file in self._data['fonts'].items():
self._fonts[name] = Font(file) 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(): 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] = ( 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']] self._fonts[data['font']]
) )
@ -224,12 +279,23 @@ class Game:
filebase = os.path.split(script)[-1] filebase = os.path.split(script)[-1]
name, ext = os.path.splitext(filebase) 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] 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...') if output: print('Rendering text...')
lines = script.render_lines(font) lines = script.render_lines(font)
if output: print('Text rendered.') if output: print('Text rendered.')
@ -249,7 +315,10 @@ class Game:
if output: print('Writing map index...', end='') if output: print('Writing map index...', end='')
with open(output_map, mode='wt') as f: with open(output_map, mode='wt') as f:
for text, index in indexes: for text, index in indexes:
f.write('{} = {}\n'.format(text, index)) if script.output_format == 'thingy':
f.write('{}={}\n'.format(index, text))
else:
f.write('{} = {}\n'.format(text, index))
if output: print('done.') if output: print('done.')
if output: if output:
@ -262,7 +331,7 @@ class Game:
if __name__ == '__main__': if __name__ == '__main__':
import sys import sys
import os import os
if len(sys.argv) < 1: if len(sys.argv) < 1:
print('Usage: smeargle.py game.json [output_directory]') print('Usage: smeargle.py game.json [output_directory]')
print('\nPlease see the included readme.txt for documentation on file formats.') print('\nPlease see the included readme.txt for documentation on file formats.')