Update scripts and NES compatibility, add PNG to NES CHR convert

This commit is contained in:
2023-11-01 23:22:11 +01:00
parent 9be90d7629
commit fa7bc447fc
14 changed files with 379 additions and 28 deletions

130
Tools/WindowsRefreshEnv.cmd Normal file
View File

@@ -0,0 +1,130 @@
:: Code generously provided by @beatcracker: https://github.com/beatcracker/detect-batch-subshell
@echo off
setlocal EnableDelayedExpansion
:: Dequote path to command processor and this script path
set ScriptPath=%~0
set CmdPath=%COMSPEC:"=%
:: Get command processor filename and filename with extension
for %%c in (!CmdPath!) do (
set CmdExeName=%%~nxc
set CmdName=%%~nc
)
:: Get this process' PID
:: Adapted from: http://www.dostips.com/forum/viewtopic.php?p=22675#p22675
set "uid="
for /l %%i in (1 1 128) do (
set /a "bit=!random!&1"
set "uid=!uid!!bit!"
)
for /f "tokens=2 delims==" %%i in (
'wmic Process WHERE "Name='!CmdExeName!' AND CommandLine LIKE '%%!uid!%%'" GET ParentProcessID /value'
) do (
rem Get commandline of parent
for /f "tokens=1,2,*" %%j in (
'wmic Process WHERE "Handle='%%i'" GET CommandLine /value'
) do (
rem Strip extra CR's from wmic output
rem http://www.dostips.com/forum/viewtopic.php?t=4266
for /f "delims=" %%x in ("%%l") do (
rem Dequote path to batch file, if any (3rd argument)
set ParentScriptPath=%%x
set ParentScriptPath=!ParentScriptPath:"=!
)
rem Get parent process path
for /f "tokens=2 delims==" %%y in ("%%j") do (
rem Dequote parent path
set ParentPath=%%y
set ParentPath=!ParentPath:"=!
rem Handle different invocations: C:\Windows\system32\cmd.exe , cmd.exe , cmd
for %%p in (!CmdPath! !CmdExeName! !CmdName!) do (
if !ParentPath!==%%p set IsCmdParent=1
)
rem Check if we're running in cmd.exe with /c switch and this script path as argument
if !IsCmdParent!==1 if %%k==/c if "!ParentScriptPath!"=="%ScriptPath%" set IsExternal=1
)
)
)
if !IsExternal!==1 (
echo %~nx0 does not work when run from this process. If you're in PowerShell, please 'Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1' and try again.
exit 1
)
endlocal
:: End code from @beatcracker
@echo off
::
:: RefreshEnv.cmd
::
:: Batch file to read environment variables from registry and
:: set session variables to these values.
::
:: With this batch file, there should be no need to reload command
:: environment every time you want environment changes to propagate
::echo "RefreshEnv.cmd only works from cmd.exe, please install the Chocolatey Profile to take advantage of refreshenv from PowerShell"
echo | set /p dummy="Refreshing environment variables from registry for cmd.exe. Please wait..."
goto main
:: Set one environment variable from registry key
:SetFromReg
"%WinDir%\System32\Reg" QUERY "%~1" /v "%~2" > "%TEMP%\_envset.tmp" 2>NUL
for /f "usebackq skip=2 tokens=2,*" %%A IN ("%TEMP%\_envset.tmp") do (
echo/set "%~3=%%B"
)
goto :EOF
:: Get a list of environment variables from registry
:GetRegEnv
"%WinDir%\System32\Reg" QUERY "%~1" > "%TEMP%\_envget.tmp"
for /f "usebackq skip=2" %%A IN ("%TEMP%\_envget.tmp") do (
if /I not "%%~A"=="Path" (
call :SetFromReg "%~1" "%%~A" "%%~A"
)
)
goto :EOF
:main
echo/@echo off >"%TEMP%\_env.cmd"
:: Slowly generating final file
call :GetRegEnv "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" >> "%TEMP%\_env.cmd"
call :GetRegEnv "HKCU\Environment">>"%TEMP%\_env.cmd" >> "%TEMP%\_env.cmd"
:: Special handling for PATH - mix both User and System
call :SetFromReg "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" Path Path_HKLM >> "%TEMP%\_env.cmd"
call :SetFromReg "HKCU\Environment" Path Path_HKCU >> "%TEMP%\_env.cmd"
:: Caution: do not insert space-chars before >> redirection sign
echo/set "Path=%%Path_HKLM%%;%%Path_HKCU%%" >> "%TEMP%\_env.cmd"
:: Cleanup
del /f /q "%TEMP%\_envset.tmp" 2>nul
del /f /q "%TEMP%\_envget.tmp" 2>nul
:: capture user / architecture
SET "OriginalUserName=%USERNAME%"
SET "OriginalArchitecture=%PROCESSOR_ARCHITECTURE%"
:: Set these variables
call "%TEMP%\_env.cmd"
:: Cleanup
del /f /q "%TEMP%\_env.cmd" 2>nul
:: reset user / architecture
SET "USERNAME=%OriginalUserName%"
SET "PROCESSOR_ARCHITECTURE=%OriginalArchitecture%"
echo | set /p dummy="Finished."
echo .

209
Tools/pilbmp2nes.py Normal file
View File

@@ -0,0 +1,209 @@
#!/usr/bin/env python3
#
# Bitmap to multi-console CHR converter using Pillow, the
# Python Imaging Library
#
# Copyright 2014-2015 Damian Yerrick
# Copying and distribution of this file, with or without
# modification, are permitted in any medium without royalty
# provided the copyright notice and this notice are preserved.
# This file is offered as-is, without any warranty.
#
from __future__ import with_statement, print_function, unicode_literals
from PIL import Image
from time import sleep
def formatTilePlanar(tile, planemap, hflip=False, little=False):
"""Turn a tile into bitplanes.
Planemap opcodes:
10 -- bit 1 then bit 0 of each tile
0,1 -- planar interleaved by rows
0;1 -- planar interlaved by planes
0,1;2,3 -- SNES/PCE format
"""
hflip = 7 if hflip else 0
if (tile.size != (8, 8)):
return None
pixels = list(tile.getdata())
pixelrows = [pixels[i:i + 8] for i in range(0, 64, 8)]
if hflip:
for row in pixelrows:
row.reverse()
out = bytearray()
planemap = [[[int(c) for c in row]
for row in plane.split(',')]
for plane in planemap.split(';')]
# format: [tile-plane number][plane-within-row number][bit number]
# we have five (!) nested loops
# outermost: separate planes
# within separate planes: pixel rows
# within pixel rows: row planes
# within row planes: pixels
# within pixels: bits
for plane in planemap:
for pxrow in pixelrows:
for rowplane in plane:
rowbits = 1
thisrow = bytearray()
for px in pxrow:
for bitnum in rowplane:
rowbits = (rowbits << 1) | ((px >> bitnum) & 1)
if rowbits >= 0x100:
thisrow.append(rowbits & 0xFF)
rowbits = 1
out.extend(thisrow[::-1] if little else thisrow)
return bytes(out)
def pilbmp2chr(im, tileWidth=8, tileHeight=8,
formatTile=lambda im: formatTilePlanar(im, "0;1")):
"""Convert a bitmap image into a list of byte strings representing tiles."""
im.load()
(w, h) = im.size
outdata = []
for mt_y in range(0, h, tileHeight):
for mt_x in range(0, w, tileWidth):
metatile = im.crop((mt_x, mt_y,
mt_x + tileWidth, mt_y + tileHeight))
for tile_y in range(0, tileHeight, 8):
for tile_x in range(0, tileWidth, 8):
tile = metatile.crop((tile_x, tile_y,
tile_x + 8, tile_y + 8))
data = formatTile(tile)
outdata.append(data)
return outdata
def parse_argv(argv):
from optparse import OptionParser
parser = OptionParser(usage="usage: %prog [options] [-i] INFILE [-o] OUTFILE")
parser.add_option("-i", "--image", dest="infilename",
help="read image from INFILE", metavar="INFILE")
parser.add_option("-o", "--output", dest="outfilename",
help="write CHR data to OUTFILE", metavar="OUTFILE")
parser.add_option("-W", "--tile-width", dest="tileWidth",
help="set width of metatiles", metavar="HEIGHT",
type="int", default=8)
parser.add_option("--packbits", dest="packbits",
help="use PackBits RLE compression",
action="store_true", default=False)
parser.add_option("-H", "--tile-height", dest="tileHeight",
help="set height of metatiles", metavar="HEIGHT",
type="int", default=8)
parser.add_option("-1", dest="planes",
help="set 1bpp mode (default: 2bpp NES)",
action="store_const", const="0", default="0;1")
parser.add_option("--planes", dest="planes",
help="set the plane map (1bpp: 0) (NES: 0;1) (GB: 0,1) (SMS:0,1,2,3) (TG16/SNES: 0,1;2,3) (MD: 3210)")
parser.add_option("--hflip", dest="hflip",
help="horizontally flip all tiles (most significant pixel on right)",
action="store_true", default=False)
parser.add_option("--little", dest="little",
help="reverse the bytes within each row-plane (needed for GBA and a few others)",
action="store_true", default=False)
parser.add_option("--add", dest="addamt",
help="value to add to each pixel",
type="int", default=0)
parser.add_option("--add0", dest="addamt0",
help="value to add to pixels of color 0 (if different)",
type="int", default=None)
(options, args) = parser.parse_args(argv[1:])
tileWidth = int(options.tileWidth)
if tileWidth <= 0:
raise ValueError("tile width '%d' must be positive" % tileWidth)
tileHeight = int(options.tileHeight)
if tileHeight <= 0:
raise ValueError("tile height '%d' must be positive" % tileHeight)
# Fill unfilled roles with positional arguments
argsreader = iter(args)
try:
infilename = options.infilename
if infilename is None:
infilename = next(argsreader)
except StopIteration:
raise ValueError("not enough filenames")
outfilename = options.outfilename
if outfilename is None:
try:
outfilename = next(argsreader)
except StopIteration:
outfilename = '-'
if outfilename == '-':
import sys
if sys.stdout.isatty():
raise ValueError("cannot write CHR to terminal")
addamt, addamt0 = options.addamt, options.addamt0
if addamt0 is None: addamt0 = addamt
return (infilename, outfilename, tileWidth, tileHeight,
options.packbits, options.planes, options.hflip, options.little,
addamt, addamt0)
argvTestingMode = True
def make_stdout_binary():
"""Ensure that sys.stdout is in binary mode, with no newline translation."""
# Recipe from
# http://code.activestate.com/recipes/65443-sending-binary-data-to-stdout-under-windows/
# via http://stackoverflow.com/a/2374507/2738262
if sys.platform == "win32":
import os, msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
def main(argv=None):
import sys
if argv is None:
argv = sys.argv
if (argvTestingMode and len(argv) < 2
and sys.stdin.isatty() and sys.stdout.isatty()):
argv.extend(input('args:').split())
try:
(infilename, outfilename, tileWidth, tileHeight,
usePackBits, planes, hflip, little,
addamt, addamt0) = parse_argv(argv)
except Exception as e:
sys.stderr.write("%s: %s\n" % (argv[0], str(e)))
sys.exit(1)
im = Image.open(infilename)
# Subpalette shift
if addamt or addamt0:
px = bytearray(im.getdata())
for i in range(len(px)):
thispixel = px[i]
px[i] = thispixel + (addamt if thispixel else addamt0)
im.putdata(px)
outdata = pilbmp2chr(im, tileWidth, tileHeight,
lambda im: formatTilePlanar(im, planes, hflip, little))
outdata = b''.join(outdata)
if usePackBits:
from packbits import PackBits
sz = len(outdata) % 0x10000
outdata = PackBits(outdata).flush().tostring()
outdata = b''.join([chr(sz >> 8), chr(sz & 0xFF), outdata])
# Write output file
outfp = None
try:
if outfilename != '-':
outfp = open(outfilename, 'wb')
else:
outfp = sys.stdout
make_stdout_binary()
outfp.write(outdata)
finally:
if outfp and outfilename != '-':
outfp.close()
if __name__=='__main__':
main()

5
Tools/python3.sh Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
[ "$(shell uname --operating-system)" = "Msys" ] \
&& cmd //c "$(cygpath --windows "$(dirname "$(realpath "$0")")/WindowsRefreshEnv.cmd") && python3 $@" \
|| python3 $@ \
;