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
@ -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.
@ -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.
{
"name": "Example", // The name of the game, for reference.
"name": "Example", // The name of the game, for reference.
"fonts": {
"Melissa 8": "melissa8.json" // Font name and its filename.
"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.
"test.txt": { // Script filename.
"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

View File

@ -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
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
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,7 +315,10 @@ class Game:
if output: print('Writing map index...', end='')
with open(output_map, mode='wt') as f:
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:
@ -262,7 +331,7 @@ class Game:
if __name__ == '__main__':
import sys
import os
if len(sys.argv) < 1:
print('Usage: smeargle.py game.json [output_directory]')
print('\nPlease see the included readme.txt for documentation on file formats.')