commit 2eaa9449a2216ecc97603d6f1c46c430a12f7658 Author: olebeck Date: Sat May 28 14:27:06 2022 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6957a5c --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +/*.bin +out_*.bin +__pycache__ + +/player/*.o +/player/badapple +/player/badapple.bin +/player/badapple.mcs +/player/data/icon.bin +/player/data/title.txt + +/tools/ascii2sjis +/tools/bin2mcs +/tools/bmp2ps1b +/tools/mcpad diff --git a/README.md b/README.md new file mode 100644 index 0000000..a4a32ed --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +to build run: + +`make clean all target=pocketstation` + +extra options: +- target=[pocketstation] + specify to build for pocketstation else it builds for linux (default="") + +- frameskip=[everynthframe] + only display every nth frame (default=4) + +- loop=[1|0] + if it should loop forever (default=1) diff --git a/encoder/badapple_input.bin b/encoder/badapple_input.bin new file mode 100644 index 0000000..4b2bffe Binary files /dev/null and b/encoder/badapple_input.bin differ diff --git a/encoder/common.py b/encoder/common.py new file mode 100644 index 0000000..65f4861 --- /dev/null +++ b/encoder/common.py @@ -0,0 +1,20 @@ +import sys +from typing import List + +def diff(last: List[List[bool]], current: List[List[bool]]) -> List[List[bool]]: + out = [] + for i, row in enumerate(current): + out.append([]) + for j, pixel in enumerate(row): + out[i].append(pixel != last[i][j]) + return out + +def print_frame(pixels: List[List[bool]]): + p = "\033[H\033[J" + for y in range(res[1]): + for x in range(res[0]): + p += "##" if pixels[y][x] else " " + p += "\n" + sys.stdout.write(p) + +res = (32, 32) \ No newline at end of file diff --git a/encoder/encode.py b/encoder/encode.py new file mode 100644 index 0000000..093710e --- /dev/null +++ b/encoder/encode.py @@ -0,0 +1,110 @@ +import sys +from typing import List, Tuple + +def flatten(l): + for i in l: + if isinstance(i, list): + yield from flatten(i) + else: + yield i + +from common import * + + +def encode_RLE(data: List[bool]) -> bytes: + out = b"" + " values is a list of tuples where the first element is the count and the second is the value " + " the count can only go upto 7 bit " + values: List[Tuple[int,bool]] = [] + for i in range(len(data)): + if i == 0: + values.append((1, data[i])) + elif data[i] != data[i-1]: + values.append((1, data[i])) + else: + last = values[-1][0] + if last < 127: + values[-1] = (last+1, data[i]) + else: + values.append((1, data[i])) + + + for v in values: + # high bit is set if value is true + v = v[0] << 1 | int(v[1]) + out += v.to_bytes(1, "little") + return out + +stats = { + "avg_frame_size": 0, + "min_frame_size": float("inf"), + "max_frame_size": 0, + "total_size": 0, +} + +def diff_encode(frames: List[List[List[bool]]]) -> bytes: + out = b"" + for i, frame in enumerate(frames): + if i > 0: + last_frame = frames[i-1] + frame = diff(last_frame, frame) + + o = encode_RLE(list(flatten(frame))) + out += o + sz_b = len(o) + #print(f"Frame {i} size: {sz_b}") + stats["min_frame_size"] = min(stats["min_frame_size"], sz_b) + stats["max_frame_size"] = max(stats["max_frame_size"], sz_b) + return out + +def rle_encode(frames: List[List[List[bool]]]) -> bytes: + out = b"" + for frame in frames: + o = encode_RLE(list(flatten(frame))) + out += o + stats["min_frame_size"] = min(stats["min_frame_size"], len(o)) + stats["max_frame_size"] = max(stats["max_frame_size"], len(o)) + return out + +def get_frame(f): + pixels = [] + for y in range(res[1]): + b = f.read(4) + if len(b) < 4: + return None + row = [ int(a) == 1 for a in bin(int.from_bytes(b, "little"))[2:].zfill(32)] + pixels.append(row) + return pixels + + +def encode(): + f = open("badapple_input.bin", "rb") + frames = [] + i = 0 + while True: + i += 1 + pixels = get_frame(f) + if not pixels: + break + if i % frameskip == 0: + frames.append(pixels) + stats["frame_count"] = len(frames) + + with open("out_diff.bin", "wb") as fo: + o = diff_encode(frames) + stats["avg_frame_size"] = len(o) / len(frames) + stats["total_size"] = len(o) + fo.write(o) + print("diff", stats) + + with open("out_rle.bin", "wb") as fo: + o = rle_encode(frames) + stats["avg_frame_size"] = len(o) / len(frames) + stats["total_size"] = len(o) + fo.write(o) + print("rle", stats) + + +if __name__ == "__main__": + frameskip = int(sys.argv[1]) + encode() diff --git a/encoder/play.py b/encoder/play.py new file mode 100644 index 0000000..b0c7236 --- /dev/null +++ b/encoder/play.py @@ -0,0 +1,37 @@ +import time +from typing import IO +from common import * + + +def decode_frame(f: IO[bytes], last_frame: list[list[bool]]) -> list[list[bool]]: + pixels = [] + while len(pixels) < res[0]*res[1]: + count = int.from_bytes(f.read(1), "little") + # high bit stores value + val = bool(count & 1) + cnt = count >> 1 + for _ in range(cnt): + pixels.append(val) + # reshape to res + pixels = [pixels[i:i+res[0]] for i in range(0, len(pixels), res[0])] + + # apply diff + if last_frame: + pixels = diff(last_frame, pixels) + return pixels + +def run_encoded(): + f = open("out_diff.bin", "rb") + last_frame = None + while True: + t1 = time.time() + pixels = decode_frame(f, last_frame) + if len(pixels) == 0: + break + last_frame = pixels + print_frame(pixels) + t2 = time.time() + time.sleep(1/24 - (t2-t1)) + +if __name__ == "__main__": + run_encoded() \ No newline at end of file diff --git a/icon.BMP b/icon.BMP new file mode 100644 index 0000000..3dadf32 Binary files /dev/null and b/icon.BMP differ diff --git a/player/Makefile b/player/Makefile new file mode 100644 index 0000000..ecba7a7 --- /dev/null +++ b/player/Makefile @@ -0,0 +1,100 @@ +frameskip := 4 +loop := 1 +RLE := 1 +name = badapple + +DEFINES += -DFRAMESKIP=$(frameskip) + +ifneq ($(loop),1) +else +DEFINES += -DLOOP=$(loop) +endif + +ifneq ($(RLE),1) +input_type = diff +else +DEFINES += -DRLE +input_type = rle +endif + + +LDFLAGS = +CFLAGS = -Wl,--gc-sections -nostartfiles -s -static -nostdlib -Os -ffunction-sections -fdata-sections + + +# if pocketstation +ifeq ($(target),pocketstation) +POCKETSTATION = 1 +CC = arm-none-eabi-gcc +LD = arm-none-eabi-ld +OBJCOPY = arm-none-eabi-objcopy +AS = arm-none-eabi-as + +LDFLAGS += -Tldscript +CFLAGS += -march=armv4 -mtune=arm7tdmi +COPYFLAGS = -I binary -O elf32-littlearm + +DEFINES += -D__POCKETSTATION__ +extra_objects = head.o + +else ifeq ($(target),) +CC = gcc +LD = gcc +OBJCOPY = objcopy +AS = as + +COPYFLAGS = -B i386 -I binary -O elf64-x86-64 +DEFINES += -D__LINUX__ +else +$(error "unknown target") +endif + + +all: $(name) + +ifdef POCKETSTATION +all: $(name).mcs + +$(name).mcs: $(name).bin tools + ../tools/bin2mcs BESCEDP-00000BADAPPLE0 $< $@ + +$(name).bin: $(name) tools + $(OBJCOPY) -O binary $< $@ + ../tools/mcpad $@ + +head.o: head.s data/title.txt data/icon.bin +endif + +$(name): player.o badapple_$(input_type).o $(extra_objects) + $(LD) $(LDFLAGS) $^ -o $@ + +player.o: player.c + $(CC) -c $(CFLAGS) $(DEFINES) player.c -o player.o + + +badapple_$(input_type).o: out_$(input_type).bin + cp $< badapple_$(input_type).bin + $(OBJCOPY) $(COPYFLAGS) badapple_$(input_type).bin $@ + rm badapple_$(input_type).bin + +out_$(input_type).bin: + cd ../encoder && python3 encode.py $(frameskip) + cp ../encoder/out_$(input_type).bin . + + +tools: + cd ../tools && make + + +data/title.txt: tools + ../tools/ascii2sjis $(name) $@ + +data/icon.bin: ../icon.BMP tools + ../tools/bmp_to_1bit.py $< icon1bit.bmp + ../tools/bmp2ps1b b icon1bit.bmp + rm icon1bit.bmp + mv icon1bit.bin $@ + +clean: + rm -f *.o $(name) $(name).bin $(name).mcs out_*.bin data/icon.bin data/title.txt + cd ../tools && make clean diff --git a/player/data/psicon.bin b/player/data/psicon.bin new file mode 100644 index 0000000..a89a974 Binary files /dev/null and b/player/data/psicon.bin differ diff --git a/player/head.s b/player/head.s new file mode 100644 index 0000000..469c89e --- /dev/null +++ b/player/head.s @@ -0,0 +1,78 @@ + .text + .code 32 + + .globl _start +_start: + .ascii "SC" + .byte 0x11 + .byte 1 // Number of memory card blocks (use "mcpad" tool for auto-adjust) + + // Title 32*2 Shift-JIS zero padded (use "ascii2sjis" tool to generate this file) + .include "data/title.txt" + .space 12 + + .hword 1 // Number of File Viewer Mono Icon Frames + .ascii "MCX0" + .byte 1 // Number of entries in Executable Mono Icon List + .byte 0 // No Function Table + .word 0 + .word _progstart + +IconPS: .incbin "data/psicon.bin" // 16*word Palette + 16x16 4bits Icon +IconPK: .incbin "data/icon.bin" // File Viewer Mono Icon 32x32 + + .hword 1 // Number of Executable Mono Icon + .hword 1 // Icon Anim Speed + .word IconPK // Pointer to the same File Viewer Mono Icon + +_progstart: + b main + +//---------------------------------------- +// Bios Calls + + .globl SetCallbacks +SetCallbacks: + swi 1 + mov pc, lr + + .globl SetCpuSpeed +SetCpuSpeed: + swi 4 + mov pc, lr + + .globl PrepareExecute +PrepareExecute: + swi 8 + mov pc, lr + + .globl DoExecute +DoExecute: + swi 9 + + .globl SetComOnOff +SetComOnOff: + swi 17 + mov pc, lr + + .globl GetDirIndex +GetDirIndex: + swi 22 + mov pc, lr + + .globl IntIRQ +IntIRQ: + stmdb sp!,{r2-r3,lr} + bl IRQ_Handler + ldmia sp!,{r2-r3,pc} + + .globl IntFIQ +IntFIQ: + stmdb sp!,{r2-r3,lr} + bl FIQ_Handler + ldmia sp!,{r2-r3,pc} + + +//---------------------------------------- +// User Data + .end diff --git a/player/ldscript b/player/ldscript new file mode 100644 index 0000000..824048e --- /dev/null +++ b/player/ldscript @@ -0,0 +1,63 @@ +OUTPUT_ARCH(arm) +MEMORY +{ + ram : o = 0x00000204, l = 0x5fc + rom : o = 0x02000000, l = 0x1E000 +} +SECTIONS +{ + .text : + { + *(.text) + *(.strings) + *(.rodata) + *(.rdata) + _etext = . ; + } > rom + .rodata : + { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r*) + SORT(CONSTRUCTORS) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } > rom + .tors : + { + ___ctors = . ; + *(.ctors) + ___ctors_end = . ; + ___dtors = . ; + *(.dtors) + ___dtors_end = . ; + } > rom + .data : + { + *(.data) + _edata = . ; + } > rom + .glue_7 : + { + *(.glue_7) + _glut_7 = . ; + } > rom + .glue_7t : + { + *(.glue_7t) + _glut_7t = . ; + } > rom + .buffer : + { + *(.buffer) + _buffer = . ; + } > rom + .bss : + { + _bss_start = . ; + *(.bss) + *(COMMON) + _end = .; + } > ram +} diff --git a/player/player.c b/player/player.c new file mode 100644 index 0000000..adaa42b --- /dev/null +++ b/player/player.c @@ -0,0 +1,109 @@ +#include + +const int res = 32; + +#define setbit(arr, i) arr[i/8] |= (1 << (i%8)) +#define clrbit(arr, i) arr[(i)/8] &= ~(1 << ((i) % 8)) +#define flpbit(arr, i) arr[(i)/8] ^= 1 << ((i) % 8) +#define getbit(arr, i) arr[(i)/8] & (1 << ((i) % 8)) + +#ifdef RLE +#define NAME_BIN "badapple_rle_bin" +#else +#define NAME_BIN "badapple_diff_bin" +#endif + +extern uint8_t _binary_badapple_bin_start[] asm("_binary_" NAME_BIN "_start"); +extern uint8_t _binary_badapple_bin_end[] asm("_binary_" NAME_BIN "_end"); + +static uint8_t screen[0x80]; + +#ifdef __LINUX__ +#include +#define clear_screen "\033[2J\033[1;1H" + +void display() { + char buf[res*res*2 + res]; + char* p = buf; + for (int i = 0; i < res; i++) { + for (int j = 0; j < res; j++) { + uint8_t on = getbit(screen, (i * res) + j); + *p++ = on ? '#' : ' '; + *p++ = on ? '#' : ' '; + } + *p = '\n'; + p++; + } + write(1, clear_screen, sizeof(clear_screen)-1); + write(1, buf, sizeof(buf)); + // ""vsync""" + usleep(16666*FRAMESKIP); +} +#define ROMByteAccess(addr) *(uint8_t*)(addr) +#endif + +#ifdef __POCKETSTATION__ +#define FPS 24 +#define CLOCK_SPEED CLOCK_4M +#include "pocketlib.h" + +static void display() { + // ???? + for(int i = 0; i> 8); + return (data & 0xFF); +} +#endif + +int main(void) { + #ifdef __POCKETSTATION__ + PocketInit(); + #endif + + do { + // read all frames + uint8_t* p = _binary_badapple_bin_start; + while(p < _binary_badapple_bin_end) { + // read a frame + uint16_t px = 0; + while(px < res*res && p < _binary_badapple_bin_end) { + uint8_t value = ROMByteAccess(p++); + uint8_t changed = value & 1; + uint8_t count = value >> 1; + + #ifdef RLE + for(int i = px; i < count+px; i++) { + if(changed) setbit(screen, i); + else clrbit(screen, i); + } + #else // diff + rle + if(changed) { + for (int i = px; i < px+count; i++) flpbit(screen, i); + } + #endif + px += count; + } + display(); + } + #ifdef LOOP + } while(1); + #else + } while(0); + #endif + + #ifdef __POCKETSTATION__ + PocketExit(); + #endif + return 0; +} diff --git a/player/pocketlib.h b/player/pocketlib.h new file mode 100644 index 0000000..bd41ec4 --- /dev/null +++ b/player/pocketlib.h @@ -0,0 +1,218 @@ +/****************************************/ +/* PocketStation Lib - by Orion_ [2013] */ +/****************************************/ +// Based on Miko Hoshina & Martin Korth work. +// If you use it, please give credits ! +// v3.6 + +// Optional Functions, don't define this in your main .c file do reduce size +//#define USE_SPRITE +//#define USE_RANDOM +//#define USE_SOUND + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#ifndef CLOCK_SPEED +#define CLOCK_SPEED CLOCK_4M +#endif + +#ifndef FPS +#define FPS 64 +#endif + +#ifndef SOUND_FREQ +#define SOUND_FREQ 4000 // If 8000Hz, then use CLOCK_8M ! +#endif + +/**/ + +#define PAD_BUTTON 0x1 +#define PAD_RIGHT 0x2 +#define PAD_LEFT 0x4 +#define PAD_DOWN 0x8 +#define PAD_UP 0x10 + +#define PAD_STATUS ((IRQ_STATUS_RAW) & 0x1F) + +int PadOnRelease, PadOnPress; + +/**/ + +#define LCD_ON 0x40 +#define LCD_OFF 0x00 +#define LCD_64HZ 0x10 +#define LCD_32HZ 0x20 +#define LCD_16HZ 0x30 +#define LCD_CPEN 0x08 + +#define LCD_MODE *((volatile unsigned int *)0xD000000) +#define LCD_VRAM(_line_) *((volatile unsigned int *)(0xD000100+(_line_ << 2))) +#define LCD_VRAM_ADRS (volatile unsigned int *)0xD000100 + +/**/ + +#define CLOCK_62_5K 0x1 +#define CLOCK_125K 0x2 +#define CLOCK_250K 0x3 +#define CLOCK_500K 0x4 +#define CLOCK_1M 0x5 +#define CLOCK_2M 0x6 +#define CLOCK_4M 0x7 +#define CLOCK_8M 0x8 + +/**/ + +#define TIMER0_START() *((volatile unsigned int *)(0xA800008)) |= 0x4 +#define TIMER1_START() *((volatile unsigned int *)(0xA800018)) |= 0x4 +#define TIMER2_START() *((volatile unsigned int *)(0xA800028)) |= 0x4 +#define TIMER0_STOP() *((volatile unsigned int *)(0xA800008)) &= ~0x4 +#define TIMER1_STOP() *((volatile unsigned int *)(0xA800018)) &= ~0x4 +#define TIMER2_STOP() *((volatile unsigned int *)(0xA800028)) &= ~0x4 +#define TIMER0_SETCOUNT(_x_) *((volatile unsigned int *)(0xA800000)) = (_x_) +#define TIMER1_SETCOUNT(_x_) *((volatile unsigned int *)(0xA800010)) = (_x_) +#define TIMER2_SETCOUNT(_x_) *((volatile unsigned int *)(0xA800020)) = (_x_) + +/**/ + +#define FIQ_COMM 0x40 +#define IRQ_TIMER0 0x80 +#define IRQ_TIMER1 0x100 +#define IRQ_RTC 0x200 +#define IRQ_BATTERY 0x400 +#define IRQ_PSCOMM 0x800 +#define IRQ_INFRARED 0x1000 +#define FIQ_TIMER2 0x2000 + +#define IRQ_STATUS *((volatile unsigned int *)(0xA000000)) +#define IRQ_STATUS_RAW *((volatile unsigned int *)(0xA000004)) +#define IRQ_ENABLE *((volatile unsigned int *)(0xA000008)) +#define IRQ_DISABLE *((volatile unsigned int *)(0xA00000c)) +#define IRQ_CLEAR *((volatile unsigned int *)(0xA000010)) + +#define IRQ_DISABLE_ALL() IRQ_DISABLE = 0x3FFF; + +/**/ + +#define IOCTL_CONFIG *((volatile unsigned int *)0xD800000) +#define IOCTL_OFF *((volatile unsigned int *)0xD800004) +#define IOCTL_ON *((volatile unsigned int *)0xD800008) +#define IOCTL_DAC *((volatile unsigned int *)0xD800010) +#define IOCTL_DASOUND *((volatile unsigned int *)0xD800014) +#define IOCTL_BATTERY *((volatile unsigned int *)0xD800020) + +#define IOCTL_LED 0x02 +#define IOCTL_SPEAKER 0x20 +#define IOCTL_IRDA 0x40 + + +/****************************************/ +// From header.S + +// Bios Calls +void *SetCallbacks(int index, void (*function)); +int SetCpuSpeed(int speed); +int PrepareExecute(int flag, int dir_index, int param); +void DoExecute(int snapshot_flag); +void SetComOnOff(int flag); +int GetDirIndex(void); + +// IRQ/FIQ Asm +void IntIRQ(void); +void IntFIQ(void); + +void (*UserIRQ)(void); +int (*UserFIQ)(void); // Return 1 to Skip Library Sound Processing, else return 0 + + +/****************************************/ +// IRQ/Timer0 Handler (for VSync) + +volatile int VCount; + +void IRQ_Handler(void) +{ + int status; + + status = IRQ_STATUS; + status &= IRQ_ENABLE; + status &= ~(FIQ_TIMER2 | FIQ_COMM); // Clear FIQ Bits + + if (status & IRQ_TIMER0) + VCount++; + + if (UserIRQ) + UserIRQ(); + + IRQ_CLEAR = status; +} + +/****************************************/ +// FIQ/Timer2 Handler (for Sound) + +void FIQ_Handler(void) +{ + IRQ_CLEAR = FIQ_TIMER2; +} + +/****************************************/ +// Sync Functions + +void VSync() +{ + register int cnt = VCount; + register int PadState = PAD_STATUS; + + PadOnRelease = PadState; + PadOnRelease ^= PadOnPress; + PadOnRelease &= PadOnPress; + PadOnPress = PadState; + + while (VCount == cnt); // Wait For IRQ Timer0 +} + +/****************************************/ +// Init, need to be called at first ! + +void PocketInit(void) +{ + SetCpuSpeed(CLOCK_SPEED); + IRQ_DISABLE_ALL(); + + UserIRQ = NULL; + UserFIQ = NULL; + + VCount = 0; + PadOnRelease = 0; + PadOnPress = 0; + + SetCallbacks(1, IntIRQ); // VSync Interrupt + SetCallbacks(2, IntFIQ); // Audio Interrupt + + IRQ_CLEAR = IRQ_TIMER0 | FIQ_TIMER2; + IRQ_ENABLE = IRQ_TIMER0 | FIQ_TIMER2; + + // Start VSync Timer + TIMER0_SETCOUNT((15625 << CLOCK_SPEED) / FPS); + TIMER0_START(); + + while (PAD_STATUS & PAD_BUTTON); // Wait for Button Release (Button pressed when comming from Menu) +} + + +/****************************************/ +// Exit, need to be called to exit app ! + +void PocketExit(void) +{ + SetComOnOff(0); + TIMER0_STOP(); + TIMER2_STOP(); + IRQ_DISABLE_ALL(); + IRQ_ENABLE = IRQ_RTC | IRQ_PSCOMM; + PrepareExecute(1, 0, GetDirIndex() | 0x30); // Return to GUI on our Program Icon + DoExecute(0); +} + +/****************************************/ diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..e75de7c --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,9 @@ +all: ascii2sjis bin2mcs bmp2ps1b mcpad + +ascii2sjis: ascii2sjis.c +bin2mcs: bin2mcs.c +bmp2ps1b: bmp2ps1b.c +mcpad: mcpad.c + +clean: + rm -f ascii2sjis bin2mcs bmp2ps1b mcpad \ No newline at end of file diff --git a/tools/ascii2sjis.c b/tools/ascii2sjis.c new file mode 100644 index 0000000..d8e35f9 --- /dev/null +++ b/tools/ascii2sjis.c @@ -0,0 +1,105 @@ +#include +#include +#include + +char ShiftJISpunc[] = ",.:;?!^_-/\\~|()[]{}+-=<>'\"$£%#&*@"; +unsigned char ShiftJISpuncCode[] = {0x43,0x44,0x46,0x47,0x48,0x49,0x4F,0x51,0x5D,0x5E,0x5F,0x60,0x62,0x69,0x6A,0x6D,0x6E,0x6F,0x70,0x7B,0x7C,0x81,0x83,0x84,0x8C,0x8D,0x90,0x92,0x93,0x94,0x95,0x96,0x97}; + +void ASCII2ShiftJIS(char *src, unsigned char *dst) +{ + while (*src) + { + if (*src == ' ') + { + *dst++ = 0x81; + *dst++ = 0x40; + } + else if ((*src >= '0') && (*src <= '9')) + { + *dst++ = 0x82; + *dst++ = 0x4F + ((*src) - '0'); + } + else if ((*src >= 'A') && (*src <= 'Z')) + { + *dst++ = 0x82; + *dst++ = 0x60 + ((*src) - 'A'); + } + else if ((*src >= 'a') && (*src <= 'z')) + { + *dst++ = 0x82; + *dst++ = 0x81 + ((*src) - 'a'); + } + else + { + int i; + + for (i = 0; i < sizeof(ShiftJISpuncCode); i++) + if (*src == ShiftJISpunc[i]) + { + *dst++ = 0x81; + *dst++ = ShiftJISpuncCode[i]; + break; + } + } + src++; + } +} + +unsigned char buffer[64]; + +int main(int argc, char *argv[]) +{ + FILE *f; + int size; + + printf("ASCII2ShiftJIS for PocketStation Lib - by Orion_ [2013]\n\n"); + + if (argc > 2) + { + size = strlen(argv[1]); + if (size > 32) + { + printf("Title too long (must be <= 32)\n"); + return (-1); + } + memset(buffer, 0, sizeof(buffer)); + ASCII2ShiftJIS(argv[1], buffer); + + f = fopen(argv[2], "wb"); + if (!f) + { + printf("Cannot create file %s\n", argv[2]); + return (-1); + } + + // Title + fprintf(f, "\t/* %s */\r\n", argv[1]); // Just to keep a record of the Original Title :) + fprintf(f, "\t.ascii\t\""); + fwrite(buffer, 1, size*2, f); + fprintf(f, "\"\r\n"); + + // Padding + if (size < 32) + { + fprintf(f, "\t.hword\t"); + size = 32 - size; + while (size) + { + fprintf(f, "0"); + size--; + if (size) + fprintf(f, ","); + } + fprintf(f, "\r\n"); + } + + fclose(f); + } + else + { + printf("Usage: ascii2sjis ASCII_Title file.sjis\n\n"); + printf("Ex: ascii2sjis \"Test Program\" title.sjis\n\n"); + } + + return (0); +} diff --git a/tools/bin2mcs.c b/tools/bin2mcs.c new file mode 100644 index 0000000..becc991 --- /dev/null +++ b/tools/bin2mcs.c @@ -0,0 +1,77 @@ +#include +#include +#include + +unsigned char data[128*1025]; + +int main(int argc, char *argv[]) +{ + FILE *f; + int size, i; + + printf("MemoryCard BIN TO MCS - by Orion_ [2013]\n\n"); + + if (argc > 3) + { + f = fopen(argv[2], "rb"); + if (f) + { + memset(data, 0, sizeof(data)); + size = fread(&data[128], 1, sizeof(data)-128, f); + + if ((data[128] == 'S') && (data[129] == 'C')) + { + fclose(f); + + size = (size + 8191) >> 13; // One Block = 8k + data[128+3] = size; // Adjust Block Number + size <<= 13; // Pad to 8k + + f = fopen(argv[3], "wb"); + if (f) + { + // Generate MCS header + data[0] = 0x51; + data[4] = (size >> 0) & 0xFF; + data[5] = (size >> 8) & 0xFF; + data[6] = (size >> 16) & 0xFF; + data[7] = (size >> 24) & 0xFF; + data[8] = 1; // next block + strncpy(&data[10], argv[1], 20); + + for (i = 0; i < 127; i++) + data[127] ^= data[i]; + + fwrite(data, 1, 128+size, f); + fclose(f); + } + else + printf("Cannot create output file\n"); + } + else + printf("Bad MemoryCard BIN file format\n"); + } + else + printf("Cannot load input file\n"); + } + else + { + printf("Usage: bin2mcs FileID file.bin file.mcs\n\n"); + printf("FileID Description: BcSlCTxCCCCCFILENAME (20 char max)\n"); + printf(" Bc -> BI = Japan, BA = America, BE = Europe\n"); + printf(" Sl -> SC = Sony Computer, SL = Sony Licensed\n"); + printf(" C -> P = Japan, U = America, E = Europe\n"); + printf(" T -> S = Game, D = Demo, M = ?\n"); + printf(" x -> - = Normal Save, P = PocketStation\n"); + printf(" CCCCC -> Game Digit Code\n"); + printf(" FILENAME -> This Save Specific Filename\n"); + printf("\nFileID Example:\n"); + printf("BISLPS-00000GAMENAME Sony Licensed Japan, Game Code:00000 Filename: GAMENAME\n"); + printf("BASCUS-21042SAVEFILE Sony Computer America, Game Code:21042 Filename: SAVEFILE\n"); + printf("BESCES-88888FILENAME Sony Computer Europe, Game Code:88888 Filename: FILENAME\n"); + printf("BESLESP00000MINIGAME Sony Licensed Europe, PocketStation Filename: MINIGAME\n\n"); + getchar(); + } + + return (0); +} diff --git a/tools/bmp2ps1b.c b/tools/bmp2ps1b.c new file mode 100644 index 0000000..380d3ae --- /dev/null +++ b/tools/bmp2ps1b.c @@ -0,0 +1,102 @@ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + FILE *in, *out; + unsigned short x, y; + unsigned int a, b; + char n[255], *p; + int i, j, k, s; + + printf("2 Colors BMP - to - PocketStation Binary data -- by Orion_ [2007-2013]\n\n"); + s = 0; + + if (argc > 2) + { + if ((argv[1][0] == 's') || (argv[1][0] == 'h') || (argv[1][0] == 'b')) + { + in = fopen(argv[2], "rb"); + if (in) + { + strcpy(n, argv[2]); + p = strstr(n, ".bmp"); + if (p) + { + if (argv[1][0] == 'b') + { + strcpy(p + 1, "bin"); + out = fopen(n, "wb"); + } + else + { + strcpy(p + 1, argv[1]); + out = fopen(n, "w"); + } + if (out) + { + fseek(in,18,SEEK_SET); + fread(&x,2,1,in); + x /= 32; + if (x >= 1) + { + fseek(in,22,SEEK_SET); + fread(&y,2,1,in); + *(p - 2) = '\0'; + if (argv[1][0] == 'h') + fprintf(out, "const u32\t%s_gfx[] = {\n", n); + for (k = 0; k < y; k++) + { + fseek(in,-((k*x*4)+x*4),SEEK_END); + if (argv[1][0] == 's') + fprintf(out, "\t.word\t"); + for (i = 0; i < x; i++) + { + fread(&a,4,1,in); + b = 0; + for (j = 0; j < 32; j++) + b |= (((a >> (31-j)) & 1) << j); + b ^= 0xFFFFFFFF; + b = (b >> 24) | (((b >> 16) & 0xFF) << 8) | (((b >> 8) & 0xFF) << 16) | ((b & 0xFF) << 24); + if (argv[1][0] == 'b') + fwrite(&b, 1, 4, out); + else + fprintf(out, "0x%x", b); + if ((argv[1][0] == 'h') || ((argv[1][0] == 's') && (i != x - 1))) + fprintf(out, ","); + } + if (argv[1][0] != 'b') + fprintf(out, "\n"); + } + if (argv[1][0] == 'h') + fprintf(out, "};\n"); + s = 1; + } + else + printf("Width must be at least 32\n"); + } + else + printf("Cannot create output file\n"); + } + else + printf("Bad filename\n"); + } + else + printf("Cannot load input file\n"); + } + else + printf("First argument must be 'h', 's', or 'b' (H = C Header File, S = ASM File, B = Binary File)\n"); + } + + if (s) + printf("done...\n"); + else + { + printf("\nUsage: bmp2ps1b [h|s|b] file.bmp\n\n"); + printf("First argument must be 'h', 's', or 'b' (H = C Header File, S = ASM File, B = Binary File)\n"); + printf("BMP MUST BE 1BITS !! (Black and White 2 colors palette)\n\n"); + } + + return 0; +} diff --git a/tools/bmp_to_1bit.py b/tools/bmp_to_1bit.py new file mode 100755 index 0000000..280cc76 --- /dev/null +++ b/tools/bmp_to_1bit.py @@ -0,0 +1,8 @@ +#!/bin/python3 +import sys +from PIL import Image + +img = Image.open(sys.argv[1]) +img = img.convert("1", dither=Image.Dither.NONE) +img = img.resize((32,32), Image.Resampling.NEAREST) +img.save(sys.argv[2]) diff --git a/tools/mcpad.c b/tools/mcpad.c new file mode 100644 index 0000000..f219a14 --- /dev/null +++ b/tools/mcpad.c @@ -0,0 +1,51 @@ +#include +#include +#include + +unsigned char data[128*1024]; + +int main(int argc, char *argv[]) +{ + FILE *f; + int size, osize; + + printf("MemoryCard Block Padder & Adjust - by Orion_ [2013]\n\n"); + + if (argc > 1) + { + f = fopen(argv[1], "rb"); + if (f) + { + memset(data, 0, sizeof(data)); + osize = size = fread(data, 1, sizeof(data), f); + + if ((data[0] == 'S') && (data[1] == 'C')) + { + fclose(f); + + size = (size + 8191) >> 13; // One Block = 8k + data[3] = size; // Adjust Block Number + size <<= 13; // Pad to 8k + + f = fopen(argv[1], "wb"); + if (f) + { + fwrite(data, 1, size, f); + fclose(f); + printf("Free Space: %d\n", size - osize); + printf("%d Block(s)\n", data[3]); + } + else + printf("Cannot create output file\n"); + } + else + printf("Bad MemoryCard file format\n"); + } + else + printf("Cannot load input file\n"); + } + else + printf("Usage: mcpad mcfile.bin\n\n"); + + return (0); +}