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

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

View File

@ -51,12 +51,8 @@ else ifeq ($(MultiSpacc_Target), NES)
endif
CC = $(ToolsPrefix)gcc $(CFlags) $(Defines)
Shell = $(shell echo $$SHELL)
AppObjects = $(AppSources:.c=.o)
SpaccObjects = $(SpaccSources:.c=.o)
BuildObjects = $(AppObjects) $(SpaccObjects)
#BuildSources = $(AppSources) $(SpaccSources)
#Objects = $(BuildSources:.c=.o)
BuildSources = $(AppSources) $(SpaccSources)
BuildObjects = $(BuildSources:.c=.o)
All all: $(BuildProcess)
@ -73,9 +69,9 @@ __NDS__:
cp $(SpaccSources) $(SpaccHeaders) $(VirtualBuildDir)/source/.tmp/
cd $(VirtualBuildDir)/source/.tmp; for i in *; do mv ./$$i ../LibMultiSpacc_$$i; done
cp $(AppSources) $(AppHeaders) $(VirtualBuildDir)/source/
for i in $(VirtualBuildDir)/source/*; do sed -i 's|#include[ \t]"../../LibMultiSpacc/|#include "LibMultiSpacc_|g' $$i; done
for i in $(VirtualBuildDir)/source/*; do sed -i 's|#include[ \t]"../MultiSpacc|#include "LibMultiSpacc_MultiSpacc|g' $$i; done
for i in $(VirtualBuildDir)/source/*; do sed -i 's|#include[ \t]"./|#include "./LibMultiSpacc_|g' $$i; done
for i in $(VirtualBuildDir)/source/*.c $(VirtualBuildDir)/source/*.h; do sed -i 's|#include[ \t]"../../LibMultiSpacc/|#include "LibMultiSpacc_|g' $$i; done
for i in $(VirtualBuildDir)/source/*.c $(VirtualBuildDir)/source/*.h; do sed -i 's|#include[ \t]"../MultiSpacc|#include "LibMultiSpacc_MultiSpacc|g' $$i; done
for i in $(VirtualBuildDir)/source/*.c $(VirtualBuildDir)/source/*.h; do sed -i 's|#include[ \t]"./|#include "LibMultiSpacc_|g' $$i; done
cd $(VirtualBuildDir); make
__NES__:
@ -85,13 +81,14 @@ __NES__:
cp $(SpaccSources) $(SpaccHeaders) $(VirtualBuildDir)/.tmp/
cd $(VirtualBuildDir)/.tmp; for i in *; do mv ./$$i ../LibMultiSpacc_$$i; done
cp $(AppSources) $(AppHeaders) $(VirtualBuildDir)/
for i in $(VirtualBuildDir)/*; do sed -i 's|#include[ \t]"../../LibMultiSpacc/|#include "LibMultiSpacc_|g' $$i; done
for i in $(VirtualBuildDir)/*; do sed -i 's|#include[ \t]"../MultiSpacc|#include "LibMultiSpacc_MultiSpacc|g' $$i; done
for i in $(VirtualBuildDir)/*; do sed -i 's|#include[ \t]"./|#include "./LibMultiSpacc_|g' $$i; done
cp ../../neslib/*.cfg ../../neslib/crt0.o ../../neslib/chr_generic.o ../../neslib/*.lib ../../neslib/*.h $(VirtualBuildDir)/
echo "AppName='$(AppName)'; Defines='$(Defines)'; AppSources='$(AppSources)'; SpaccSources='$(SpaccSources)'; AppObjects='$(AppObjects)'; BuildObjects='$(BuildObjects)';" > $(VirtualBuildDir)/Make.sh
for i in $(VirtualBuildDir)/*.c $(VirtualBuildDir)/*.h; do sed -i 's|#include[ \t]"../../LibMultiSpacc/|#include "LibMultiSpacc_|g' $$i; done
for i in $(VirtualBuildDir)/*.c $(VirtualBuildDir)/*.h; do sed -i 's|#include[ \t]"../MultiSpacc|#include "LibMultiSpacc_MultiSpacc|g' $$i; done
for i in $(VirtualBuildDir)/*.c $(VirtualBuildDir)/*.h; do sed -i 's|#include[ \t]"./|#include "LibMultiSpacc_|g' $$i; done
cp ../../neslib/*.cfg ../../neslib/crt0.o ../../neslib/*.lib ../../neslib/*.h $(VirtualBuildDir)/
printf ".segment \"CHARS\"\n\t.incbin \"CHARS.chr\"" > $(VirtualBuildDir)/CHARS.s
echo "AppName='$(AppName)'; Defines='$(Defines)'; ProjectRoot=../..;" > $(VirtualBuildDir)/Make.sh
cat ../NES.mk.sh >> $(VirtualBuildDir)/Make.sh
cd $(VirtualBuildDir); $(Shell) ./Make.sh
cd $(VirtualBuildDir); sh ./Make.sh
Run run: All
./$(AppName)$(ExeSuffix)

Binary file not shown.

After

Width:  |  Height:  |  Size: 773 B

View File

@ -27,7 +27,7 @@ int main( int argc, char *argv[] )
MultiSpacc_Surface *Screen;
MultiSpacc_Surface *TilesImg;
WindowConfig.Width = 256;
WindowConfig.Width = 320;
WindowConfig.Height = 240;
WindowConfig.Bits = 16;
memcpy( WindowConfig.Palette, PALETTE, 32 );
@ -45,13 +45,13 @@ int main( int argc, char *argv[] )
// Bitmap font borrowed from: <https://github.com/nesdoug/01_Hello/blob/master/Alpha.chr>
// Copyright (c) 2018 Doug Fraker www.nesdoug.com (MIT)
//TilesImg = MultiSpacc_LoadImage( "Tiles.png", Screen, NULL );
TilesImg = MultiSpacc_LoadImage( "CHARS.png", Screen, NULL );
MultiSpacc_PrintText( "Hello, World!", Screen, &WindowConfig, 2, 2, TilesImg );
MultiSpacc_PrintDebug("[I] Ready!\n");
while(true)
{
MultiSpacc_Sprite( 0, spriteX, spriteY, 20/*128*/, TilesImg, Screen );
MultiSpacc_Sprite( 0, spriteX, spriteY, 1, TilesImg, Screen );
//scroll(spriteX,0);
spriteX += accelX;
@ -74,7 +74,7 @@ int main( int argc, char *argv[] )
};
// TODO: Implement cross-platform vblank-wait
//MultiSpacc_Sleep(16);
MultiSpacc_Sleep(16);
}
return 0;

View File

@ -1 +1 @@
include ../Common.mk
include ../Common.mk

Binary file not shown.

Before

Width:  |  Height:  |  Size: 664 B

View File

@ -1,7 +1,8 @@
#!/dev/null
set -e
SdkRoot="${ProjectRoot}/../../.."
# ${SpaccSources} ${AppSources} ${SpaccObjects} ${AppObjects} chr_generic.s chr_generic.o crt0.o
sh "${SdkRoot}/Tools/python3.sh" "${SdkRoot}/Tools/pilbmp2nes.py" -i "${ProjectRoot}/CHARS.png" -o ./CHARS.chr
for File in *.c
do cc65 -Oirs --target nes ${File} ${Defines}

View File

@ -98,4 +98,11 @@ void MultiSpacc_Sprite( int id, int x, int y, int sprite, MultiSpacc_Surface *Ti
ppu_wait_frame();
return 0;
}
void MultiSpacc_Sleep( int milliseconds ){}
MultiSpacc_Surface *MultiSpacc_LoadImage( char FilePath[], MultiSpacc_Surface *Screen, Uint32 *ColorKey )
{
return NULL;
};
#endif

View File

@ -64,6 +64,7 @@
#define MultiSpacc_Surface int
#define MultiSpacc_Event int
int MultiSpacc_UpdateWindowSurface( MultiSpacc_Window *Window );
void MultiSpacc_Sleep( int milliseconds );
#endif
typedef struct MultiSpacc_SurfaceConfig {

View File

@ -22,6 +22,8 @@ MultiSpacc_Window *MultiSpacc_SetWindow( MultiSpacc_SurfaceConfig *WindowConfig
#endif
#ifdef MultiSpacc_Target_NES
WindowConfig->Width = 256;
WindowConfig->Height = 240;
oam_clear();
pal_all(WindowConfig->Palette);
ppu_on_all();

View File

@ -1,5 +0,0 @@
*.o
*.lst
*~
crt0.lst
neslib2.lib

View File

@ -300,8 +300,12 @@ void __fastcall__ nmi_set_callback(void (*callback)(void));
#ifndef NULL
#define NULL 0
#endif
#define TRUE 1
#define FALSE 0
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define NT_UPD_HORZ 0x40
#define NT_UPD_VERT 0x80

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 $@ \
;