Skip to content

Commit

Permalink
Merge pull request #94 from Daft-Freak/patch-1
Browse files Browse the repository at this point in the history
Validate metadata image sizes
  • Loading branch information
Gadgetoid committed Mar 10, 2022
2 parents e62c711 + c68b9ab commit 7ec4b88
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 43 deletions.
Binary file added src/tests/resources/doom-fire.blit
Binary file not shown.
8 changes: 8 additions & 0 deletions src/tests/resources/metadata-invalid-splash.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
title: Rocks & Diamonds
description: A pulse pounding, rock rollin', diamond hunting adventure
author: gadgetoid
icon:
file: no-icon.png
splash:
file: image.png #this is the wrong size
version: v1.0.0
17 changes: 17 additions & 0 deletions src/tests/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ def test_metadata_icns(test_resources, test_binary_file, test_icns_file):
test_icns_file.flush()
assert test_icns_file.read()[:4] == b'icns'

def test_metadata_dump(test_resources):
from ttblit import main

with pytest.raises(SystemExit):
main([
'metadata',
'--file', str(test_resources / 'doom-fire.blit')
])

def test_metadata_invalid_bin(test_resources, test_invalid_binary_file):
from ttblit import main
Expand All @@ -79,3 +87,12 @@ def test_metadata_invalid_bin(test_resources, test_invalid_binary_file):
'--config', str(test_resources / 'metadata-basic.yml'),
'--file', test_invalid_binary_file.name
])

def test_metadata_invalid_splash(test_resources, test_invalid_binary_file):
from ttblit import main

with pytest.raises(ValueError):
main([
'metadata',
'--config', str(test_resources / 'metadata-invalid-splash.yml')
])
97 changes: 54 additions & 43 deletions src/ttblit/tool/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,49 @@ def build_icns(self, config, working_path):

return blit_icns.build({'data': image_bytes.read()})

def dump_game_metadata(self, file, game, dump_images):
print(f'\nParsed: {file.name} ({game.bin.length:,} bytes)')
if game.relo is not None:
print(f'Relocations: Yes ({len(game.relo.relocs)})')
else:
print('Relocations: No')
if game.meta is not None:
print('Metadata: Yes')
for field in ['title', 'description', 'version', 'author', 'category', 'url']:
print(f'{field.title()+":":13s}{getattr(game.meta.data, field)}')
if len(game.meta.data.filetypes) > 0:
print(' Filetypes: ')
for filetype in game.meta.data.filetypes:
print(' ', filetype)
if game.meta.data.icon is not None:
game_icon = game.meta.data.icon
print(f' Icon: {game_icon.data.width}x{game_icon.data.height} ({len(game_icon.data.palette)} colours) ({game_icon.type})')
if dump_images:
image_icon = self.blit_image_to_pil(game_icon)
image_icon_filename = file.with_suffix(".icon.png")
image_icon.save(image_icon_filename)
print(f' Dumped to: {image_icon_filename}')
if game.meta.data.splash is not None:
game_splash = game.meta.data.splash
print(f' Splash: {game_splash.data.width}x{game_splash.data.height} ({len(game_splash.data.palette)} colours) ({game_splash.type})')
if dump_images:
image_splash = self.blit_image_to_pil(game_splash)
image_splash_filename = file.with_suffix('.splash.png')
image_splash.save(image_splash_filename)
print(f' Dumped to: {image_splash_filename}')
else:
print('Metadata: No')
print('')

def run(self, config, icns, file, force, dump_images):
if file is None and config is None:
raise click.UsageError('the following arguments are required: --config and/or --file')

if file and not file.is_file():
raise ValueError(f'Unable to find bin file at {file}')

icon = b''
splash = b''
icon = None
splash = None

game = None

Expand All @@ -75,58 +109,32 @@ def run(self, config, icns, file, force, dump_images):

# No config supplied, so dump the game info
if config is None:
print(f'\nParsed: {file.name} ({game.bin.length:,} bytes)')
if game.relo is not None:
print(f'Relocations: Yes ({len(game.relo.relocs)})')
else:
print('Relocations: No')
if game.meta is not None:
print('Metadata: Yes')
for field in ['title', 'description', 'version', 'author', 'category', 'url']:
print(f'{field.title()+":":13s}{getattr(game.meta.data, field)}')
if len(game.meta.data.filetypes) > 0:
print(' Filetypes: ')
for filetype in game.meta.data.filetypes:
print(' ', filetype)
if game.meta.data.icon is not None:
game_icon = game.meta.data.icon
print(f' Icon: {game_icon.data.width}x{game_icon.data.height} ({len(game_icon.data.palette)} colours) ({game_icon.type})')
if dump_images:
image_icon = self.blit_image_to_pil(game_icon)
image_icon_filename = file.with_suffix(".icon.png")
image_icon.save(image_icon_filename)
print(f' Dumped to: {image_icon_filename}')
if game.meta.data.splash is not None:
game_splash = game.meta.data.splash
print(f' Splash: {game_splash.data.width}x{game_splash.data.height} ({len(game_splash.data.palette)} colours) ({game_splash.type})')
if dump_images:
image_splash = self.blit_image_to_pil(game_splash)
image_splash_filename = file.with_suffix('.splash.png')
image_splash.save(image_splash_filename)
print(f' Dumped to: {image_splash_filename}')
else:
print('Metadata: No')
print('')
self.dump_game_metadata(file, game, dump_images)
return

self.setup_for_config(config, None)

if 'icon' in self.config:
icon = self.prepare_image_asset('icon', self.config['icon'], self.working_path)
else:
icon = struct_blit_image.parse(self.prepare_image_asset('icon', self.config['icon'], self.working_path))
if icon.data.width != 8 or icon.data.height != 8:
icon = None

if icon is None:
raise ValueError('An 8x8 pixel icon is required!"')

if 'splash' in self.config:
splash = self.prepare_image_asset('splash', self.config['splash'], self.working_path)
splash = struct_blit_image.parse(self.prepare_image_asset('splash', self.config['splash'], self.working_path))

if splash.data.width != 128 or splash.data.height != 96:
splash = None

if icns is not None:
if not icns.is_file() or force:
open(icns, 'wb').write(self.build_icns(self.config['splash'], self.working_path))
logging.info(f'Saved macOS icon to {icns}')
else:
raise ValueError('A 128x96 pixel splash is required!"')

if not game:
return
if splash is None:
raise ValueError('A 128x96 pixel splash is required!"')

title = self.config.get('title', None)
description = self.config.get('description', '')
Expand Down Expand Up @@ -170,6 +178,9 @@ def run(self, config, icns, file, force, dump_images):
if len(filetype) > 4:
raise ValueError('Filetype should be a maximum of 4 characters! (Hint, don\'t include the .)')

if not game:
return

if game.meta is not None:
if not force:
logging.critical(f'Refusing to overwrite metadata in {file}')
Expand All @@ -185,8 +196,8 @@ def run(self, config, icns, file, force, dump_images):
'category': category,
'filetypes': filetypes,
'url': url,
'icon': struct_blit_image.parse(icon),
'splash': struct_blit_image.parse(splash)
'icon': icon,
'splash': splash
}
}

Expand Down

0 comments on commit 7ec4b88

Please sign in to comment.