2017-12-08 15:56:21 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# Copyright 2018 Kiyoshi Aman
|
|
|
|
#
|
|
|
|
# Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
|
|
# copyright notice and this permission notice appear in all copies.
|
|
|
|
#
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
|
|
# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
|
|
# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
|
|
import sys
|
2017-12-08 16:14:30 +00:00
|
|
|
import os.path as op
|
2017-12-08 15:56:21 +00:00
|
|
|
|
|
|
|
from PyQt5.QtGui import QImage, QPixmap, QPainter, QGuiApplication
|
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
|
2017-12-08 22:53:34 +00:00
|
|
|
def linear1(tile, *args):
|
2017-12-08 15:56:21 +00:00
|
|
|
data = bytearray()
|
|
|
|
|
|
|
|
for y in range(8):
|
|
|
|
byte = 0
|
|
|
|
|
|
|
|
for x in range(8):
|
|
|
|
if tile.pixelIndex(x, y) > 0:
|
2017-12-08 20:03:59 +00:00
|
|
|
byte += 2**(8 - x)
|
2017-12-08 15:56:21 +00:00
|
|
|
|
2017-12-08 20:03:59 +00:00
|
|
|
data.append(byte)
|
2017-12-08 19:40:34 +00:00
|
|
|
|
2017-12-08 15:56:21 +00:00
|
|
|
return bytes(data)
|
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
|
2017-12-08 22:53:34 +00:00
|
|
|
def linear2(tile, palette):
|
2017-12-08 15:56:21 +00:00
|
|
|
data = bytearray()
|
|
|
|
|
|
|
|
for y in range(8):
|
|
|
|
bp1 = 0
|
|
|
|
bp2 = 0
|
|
|
|
|
|
|
|
for x in range(8):
|
|
|
|
pixel = tile.pixelIndex(x, y)
|
2017-12-08 22:53:34 +00:00
|
|
|
if palette is not None:
|
|
|
|
pixel = palette[pixel]
|
2017-12-08 15:56:21 +00:00
|
|
|
|
2017-12-08 20:03:59 +00:00
|
|
|
a = pixel & 0x1
|
|
|
|
b = pixel & 0x2
|
|
|
|
if a:
|
|
|
|
bp1 += 2**(7 - x)
|
|
|
|
if b:
|
|
|
|
bp2 += 2**(7 - x)
|
2017-12-08 15:56:21 +00:00
|
|
|
|
2017-12-08 20:03:59 +00:00
|
|
|
data.extend((bp1, bp2))
|
2017-12-08 19:40:34 +00:00
|
|
|
|
2017-12-08 15:56:21 +00:00
|
|
|
return bytes(data)
|
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
|
2017-12-08 22:53:34 +00:00
|
|
|
def planar2(tile, palette):
|
2017-12-08 15:56:21 +00:00
|
|
|
data = bytearray()
|
|
|
|
|
|
|
|
for y in range(8):
|
|
|
|
byte = 0
|
|
|
|
|
|
|
|
for x in range(4):
|
|
|
|
pixel = tile.pixelIndex(x, y)
|
2017-12-08 22:53:34 +00:00
|
|
|
if palette is not None:
|
|
|
|
pixel = palette[pixel]
|
2017-12-08 20:37:52 +00:00
|
|
|
pixel &= 0x3
|
|
|
|
byte = byte + (pixel << x * 2)
|
2017-12-08 15:56:21 +00:00
|
|
|
|
2017-12-08 20:03:59 +00:00
|
|
|
data.append(byte)
|
2017-12-08 15:56:21 +00:00
|
|
|
byte = 0
|
|
|
|
|
|
|
|
for x in range(4, 8):
|
|
|
|
pixel = tile.pixelIndex(x, y)
|
2017-12-08 20:37:52 +00:00
|
|
|
pixel &= 0x3
|
2017-12-17 00:44:36 +00:00
|
|
|
byte = byte + (pixel << x * 2)
|
2017-12-08 15:56:21 +00:00
|
|
|
|
2017-12-08 20:03:59 +00:00
|
|
|
data.append(byte)
|
2017-12-08 19:40:34 +00:00
|
|
|
|
2017-12-08 15:56:21 +00:00
|
|
|
return bytes(data)
|
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
|
2017-12-08 22:53:34 +00:00
|
|
|
def linear4(tile, palette):
|
2017-12-08 19:41:19 +00:00
|
|
|
data = bytearray()
|
|
|
|
|
|
|
|
for y in range(8):
|
|
|
|
bp1 = 0
|
|
|
|
bp2 = 0
|
|
|
|
|
|
|
|
for x in range(8):
|
|
|
|
pixel = tile.pixelIndex(x, y)
|
2017-12-08 22:53:34 +00:00
|
|
|
if palette is not None:
|
|
|
|
pixel = palette[pixel]
|
2017-12-08 19:41:19 +00:00
|
|
|
|
|
|
|
a = pixel & 0x1
|
|
|
|
b = pixel & 0x2
|
2017-12-17 00:44:36 +00:00
|
|
|
|
2017-12-08 19:41:19 +00:00
|
|
|
if a:
|
2017-12-08 20:37:52 +00:00
|
|
|
bp1 += 2**(7 - x)
|
2017-12-08 19:41:19 +00:00
|
|
|
if b:
|
2017-12-08 20:37:52 +00:00
|
|
|
bp2 += 2**(7 - x)
|
2017-12-08 19:41:19 +00:00
|
|
|
|
2017-12-17 00:44:36 +00:00
|
|
|
data.extend((bp1, bp2))
|
|
|
|
|
|
|
|
for y in range(8):
|
|
|
|
bp3 = 0
|
|
|
|
bp4 = 0
|
|
|
|
|
|
|
|
for x in range(8):
|
2018-06-06 16:54:48 +00:00
|
|
|
pixel = tile.pixelIndex(x, y)
|
2017-12-17 00:44:36 +00:00
|
|
|
a = pixel & 0x4
|
|
|
|
b = pixel & 0x8
|
|
|
|
|
|
|
|
if a:
|
|
|
|
bp3 += 2**(7-x)
|
|
|
|
if b:
|
|
|
|
bp4 += 2**(7-x)
|
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
data.extend((bp3, bp4))
|
2017-12-17 00:44:36 +00:00
|
|
|
|
|
|
|
return bytes(data)
|
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
|
2018-02-23 15:01:37 +00:00
|
|
|
def padded4_2(tile, palette):
|
|
|
|
data = bytearray()
|
|
|
|
|
|
|
|
for y in range(8):
|
|
|
|
bp1 = 0
|
|
|
|
bp2 = 0
|
|
|
|
|
|
|
|
for x in range(8):
|
|
|
|
pixel = tile.pixelIndex(x, y)
|
|
|
|
if palette is not None:
|
|
|
|
pixel = palette[pixel]
|
|
|
|
|
|
|
|
a = pixel & 0x1
|
|
|
|
b = pixel & 0x2
|
|
|
|
|
|
|
|
if a:
|
|
|
|
bp1 += 2**(7 - x)
|
|
|
|
if b:
|
|
|
|
bp2 += 2**(7 - x)
|
|
|
|
|
|
|
|
data.extend((bp1, bp2))
|
|
|
|
|
|
|
|
for y in range(8):
|
|
|
|
bp3 = 0
|
|
|
|
bp4 = 0
|
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
data.extend((bp3, bp4))
|
2018-02-23 15:01:37 +00:00
|
|
|
|
|
|
|
return bytes(data)
|
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
|
2017-12-17 00:44:36 +00:00
|
|
|
def planar4(tile, palette):
|
|
|
|
data = bytearray()
|
|
|
|
|
|
|
|
for y in range(8):
|
|
|
|
byte = 0
|
|
|
|
|
|
|
|
for x in range(2):
|
|
|
|
pixel = tile.pixelIndex(x, y)
|
|
|
|
if palette is not None:
|
|
|
|
pixel = palette[pixel]
|
|
|
|
pixel &= 0x7
|
|
|
|
byte = byte + (pixel << x * 4)
|
|
|
|
|
|
|
|
data.append(byte)
|
|
|
|
byte = 0
|
|
|
|
|
|
|
|
for x in range(2, 4):
|
|
|
|
pixel = tile.pixelIndex(x, y)
|
|
|
|
pixel &= 0x7
|
|
|
|
byte = byte + (pixel << x * 4)
|
|
|
|
|
|
|
|
data.append(byte)
|
|
|
|
byte = 0
|
|
|
|
|
|
|
|
for x in range(4, 6):
|
|
|
|
pixel = tile.pixelIndex(x, y)
|
|
|
|
pixel &= 0x7
|
|
|
|
byte = byte + (pixel << x * 4)
|
|
|
|
|
|
|
|
data.append(byte)
|
|
|
|
byte = 0
|
|
|
|
|
|
|
|
for x in range(6, 8):
|
|
|
|
pixel = tile.pixelIndex(x, y)
|
|
|
|
pixel &= 0x7
|
|
|
|
byte = byte + (pixel << x * 4)
|
|
|
|
|
|
|
|
data.append(byte)
|
2017-12-08 19:41:19 +00:00
|
|
|
|
|
|
|
return bytes(data)
|
|
|
|
|
2017-12-17 00:44:36 +00:00
|
|
|
|
2017-12-08 15:56:21 +00:00
|
|
|
# Add new formats to this dict as they are implemented.
|
|
|
|
formats = {
|
2018-02-23 15:01:37 +00:00
|
|
|
'1bpp': linear1,
|
|
|
|
'linear2': linear2,
|
|
|
|
'planar2': planar2,
|
|
|
|
'nes2': planar2,
|
|
|
|
'gb2': linear2,
|
|
|
|
'snes2': linear2,
|
|
|
|
'gbc2': linear2,
|
|
|
|
'linear4': linear4,
|
|
|
|
'planar4': planar4,
|
|
|
|
'snes4': linear4,
|
|
|
|
'pce4': linear4,
|
|
|
|
'padded4_2': padded4_2
|
2017-12-08 15:56:21 +00:00
|
|
|
}
|
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
|
2017-12-08 15:56:21 +00:00
|
|
|
def main():
|
|
|
|
app = QGuiApplication(sys.argv)
|
|
|
|
|
2017-12-08 16:17:54 +00:00
|
|
|
if len(sys.argv) < 3 or sys.argv[2] not in formats.keys():
|
2017-12-08 15:56:21 +00:00
|
|
|
print(
|
|
|
|
'''Usage: porygon.py image format
|
|
|
|
|
2017-12-08 22:53:34 +00:00
|
|
|
image is an image file in PNG. The base filename is also used to find a
|
|
|
|
mapper in order to force palette modifications.
|
2017-12-08 15:56:21 +00:00
|
|
|
|
2017-12-08 16:17:54 +00:00
|
|
|
format is one of the supported formats:'''
|
2017-12-08 15:56:21 +00:00
|
|
|
)
|
2018-06-06 16:54:48 +00:00
|
|
|
for fmt in formats.keys():
|
|
|
|
print('* {}'.format(fmt))
|
2017-12-08 16:14:30 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
(image, fmt) = sys.argv[1:3]
|
2017-12-08 16:14:30 +00:00
|
|
|
(image_base, ext) = op.splitext(image)
|
|
|
|
output = '{}.bin'.format(image_base)
|
2017-12-08 22:53:34 +00:00
|
|
|
mapper = image_base + '.txt'
|
2017-12-08 16:14:30 +00:00
|
|
|
|
|
|
|
if not op.exists('output/'):
|
|
|
|
import os
|
|
|
|
os.mkdir('output')
|
|
|
|
if not output.startswith('output/'):
|
|
|
|
output = 'output/{}'.format(output)
|
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
bpp = int(fmt[-1])
|
2017-12-08 16:14:30 +00:00
|
|
|
|
|
|
|
print('Loading image...')
|
|
|
|
data = QImage(image)
|
|
|
|
data = data.convertToFormat(QImage.Format_Indexed8)
|
|
|
|
if data.colorCount() > 2**bpp:
|
|
|
|
raise ValueError('image has too many colors')
|
|
|
|
|
|
|
|
width = data.width()
|
|
|
|
height = data.height()
|
|
|
|
rows = int(height / 8)
|
|
|
|
columns = int(width / 8)
|
2017-12-08 22:53:34 +00:00
|
|
|
|
|
|
|
palette = {}
|
|
|
|
if op.exists(mapper):
|
|
|
|
print('Palette found. Loading...')
|
|
|
|
with open(mapper, mode='r') as f:
|
|
|
|
text = f.read().split('\n')
|
|
|
|
|
|
|
|
for line in text:
|
|
|
|
if line == '':
|
|
|
|
continue
|
|
|
|
a = line.split('=')
|
|
|
|
|
|
|
|
key = int(a[0])
|
|
|
|
value = int(a[1])
|
|
|
|
palette[key] = value
|
|
|
|
else:
|
|
|
|
print('No palette found.')
|
|
|
|
palette = None
|
2017-12-08 16:14:30 +00:00
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
print('Converting to {}'.format(fmt))
|
2017-12-08 20:03:59 +00:00
|
|
|
counter = 0
|
2017-12-08 16:14:30 +00:00
|
|
|
with open(output, mode='wb') as f:
|
2017-12-08 20:05:15 +00:00
|
|
|
for row in range(rows):
|
|
|
|
for column in range(columns):
|
2017-12-08 20:03:59 +00:00
|
|
|
tile = data.copy(column * 8, row * 8, 8, 8)
|
2018-06-06 16:54:48 +00:00
|
|
|
f.write(formats[fmt](tile, palette))
|
2017-12-08 20:03:59 +00:00
|
|
|
counter += 1
|
2017-12-08 16:17:54 +00:00
|
|
|
|
2018-06-06 16:54:48 +00:00
|
|
|
|
2017-12-08 16:17:54 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|