├── bin └── dummy ├── lib └── dummy ├── examples └── chenboot_triangle │ ├── system.cnf │ ├── manifest.txt │ ├── Makefile │ └── main.c ├── src ├── targets.make ├── seedy │ ├── targets.make │ └── seedy.c ├── sawpads │ ├── targets.make │ └── sawpads.c ├── orelei │ ├── targets.make │ ├── orelei.c │ └── midi.c └── chenboot │ ├── targets.make │ └── chenboot.S ├── .gitignore ├── toolsrc ├── elf2psx │ ├── targets.make │ └── elf2psx.c ├── targets.make ├── pscd-new │ ├── targets.make │ └── pscd-new.c ├── xainterleave │ ├── targets.make │ └── xainterleave.c ├── libpsxav │ ├── targets.make │ ├── cdrom.c │ ├── libpsxav.h │ └── adpcm.c └── psxavenc │ ├── targets.make │ ├── cdrom.c │ ├── common.h │ ├── filefmt.c │ ├── psxavenc.c │ ├── decoding.c │ └── mdec.c ├── LICENSE ├── Makefile ├── README.md └── include ├── psxdefs.h ├── psxdefs ├── joy.h ├── intc.h ├── spu.h ├── cdrom.h ├── gpu.h └── regs.h ├── sawpads.h ├── chenboot.h ├── seedy.h └── orelei.h /bin/dummy: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/dummy: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/chenboot_triangle/system.cnf: -------------------------------------------------------------------------------- 1 | BOOT = cdrom:\TRIANGLE.EXE;1 2 | TCB = 4 3 | EVENT = 10 4 | STACK = 801fff00 5 | 6 | -------------------------------------------------------------------------------- /examples/chenboot_triangle/manifest.txt: -------------------------------------------------------------------------------- 1 | bin=triangle.bin 2 | cue=triangle.cue 3 | lic=../../dat/isolicence.pal 4 | dat=./system.cnf 5 | dat=./triangle.exe 6 | -------------------------------------------------------------------------------- /src/targets.make: -------------------------------------------------------------------------------- 1 | 2 | OUTPUT_LIBS = 3 | OUTPUT_LIBS_OBJS = 4 | 5 | include src/chenboot/targets.make 6 | include src/orelei/targets.make 7 | include src/sawpads/targets.make 8 | include src/seedy/targets.make 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Output 2 | *.o 3 | *.a 4 | *.elf 5 | *.exe 6 | *.iso 7 | *.iso.bin 8 | *.iso.cue 9 | *.bin 10 | *.cue 11 | 12 | # Specific output 13 | bin/elf2psx 14 | bin/iso2raw 15 | bin/pscd-new 16 | bin/psxavenc 17 | bin/spuenc 18 | bin/xainterleave 19 | 20 | # VIM 21 | .*.sw? 22 | 23 | -------------------------------------------------------------------------------- /toolsrc/elf2psx/targets.make: -------------------------------------------------------------------------------- 1 | OUTPUT_TOOLS += $(OUTPUT_BINDIR)elf2psx$(EXEPOST) 2 | OUTPUT_TOOLS_OBJS += 3 | TOOLS_ELF2PSX_SRCS = toolsrc/elf2psx/elf2psx.c 4 | TOOLS_ELF2PSX_INCS = 5 | 6 | $(OUTPUT_BINDIR)elf2psx$(EXEPOST): $(TOOLS_ELF2PSX_SRCS) $(TOOLS_ELF2PSX_INCS) 7 | $(NATIVE_CC) -o $@ $(TOOLS_ELF2PSX_SRCS) $(NATIVE_CFLAGS) $(NATIVE_LDFLAGS) 8 | 9 | -------------------------------------------------------------------------------- /toolsrc/targets.make: -------------------------------------------------------------------------------- 1 | OUTPUT_TOOL_LIBS = 2 | OUTPUT_TOOL_LIBS_OBJS = 3 | 4 | include toolsrc/libpsxav/targets.make 5 | 6 | OUTPUT_TOOLS = 7 | OUTPUT_TOOLS_OBJS = 8 | 9 | include toolsrc/elf2psx/targets.make 10 | include toolsrc/pscd-new/targets.make 11 | include toolsrc/psxavenc/targets.make 12 | include toolsrc/xainterleave/targets.make 13 | 14 | -------------------------------------------------------------------------------- /toolsrc/pscd-new/targets.make: -------------------------------------------------------------------------------- 1 | OUTPUT_TOOLS += $(OUTPUT_BINDIR)pscd-new$(EXEPOST) 2 | OUTPUT_TOOLS_OBJS += 3 | TOOLS_PSCD_NEW_SRCS = toolsrc/pscd-new/pscd-new.c 4 | TOOLS_PSCD_NEW_INCS = 5 | 6 | $(OUTPUT_BINDIR)pscd-new$(EXEPOST): $(TOOLS_PSCD_NEW_SRCS) $(TOOLS_PSCD_NEW_INCS) 7 | $(NATIVE_CC) -o $@ -Wall -Wextra $(TOOLS_PSCD_NEW_SRCS) $(NATIVE_CFLAGS) $(NATIVE_LDFLAGS) 8 | 9 | -------------------------------------------------------------------------------- /src/seedy/targets.make: -------------------------------------------------------------------------------- 1 | LIBS_SEEDY_OBJS = \ 2 | src/seedy/seedy.o 3 | 4 | OUTPUT_LIBS += $(OUTPUT_LIBDIR)libseedy.a 5 | OUTPUT_LIBS_OBJS += $(LIBS_SEEDY_OBJS) 6 | 7 | LIBS_SEEDY_INCS = 8 | 9 | src/seedy/%.o: src/seedy/%.c 10 | $(TARGET_CC) -c -o $@ $< $(TARGET_CFLAGS) 11 | 12 | $(OUTPUT_LIBDIR)libseedy.a: $(LIBS_SEEDY_OBJS) $(LIBS_SEEDY_INCS) 13 | $(TARGET_AR) crs $@ $(LIBS_SEEDY_OBJS) 14 | 15 | -------------------------------------------------------------------------------- /src/sawpads/targets.make: -------------------------------------------------------------------------------- 1 | LIBS_SAWPADS_OBJS = \ 2 | src/sawpads/sawpads.o 3 | 4 | OUTPUT_LIBS += $(OUTPUT_LIBDIR)libsawpads.a 5 | OUTPUT_LIBS_OBJS += $(LIBS_SAWPADS_OBJS) 6 | 7 | LIBS_SAWPADS_INCS = 8 | 9 | src/sawpads/%.o: src/sawpads/%.c 10 | $(TARGET_CC) -c -o $@ $< $(TARGET_CFLAGS) 11 | 12 | $(OUTPUT_LIBDIR)libsawpads.a: $(LIBS_SAWPADS_OBJS) $(LIBS_SAWPADS_INCS) 13 | $(TARGET_AR) crs $@ $(LIBS_SAWPADS_OBJS) 14 | 15 | -------------------------------------------------------------------------------- /src/orelei/targets.make: -------------------------------------------------------------------------------- 1 | LIBS_ORELEI_OBJS = \ 2 | src/orelei/midi.o \ 3 | src/orelei/orelei.o 4 | 5 | OUTPUT_LIBS += $(OUTPUT_LIBDIR)liborelei.a 6 | OUTPUT_LIBS_OBJS += $(LIBS_ORELEI_OBJS) 7 | 8 | LIBS_ORELEI_INCS = 9 | 10 | src/orelei/%.o: src/orelei/%.c 11 | $(TARGET_CC) -c -o $@ $< $(TARGET_CFLAGS) 12 | 13 | $(OUTPUT_LIBDIR)liborelei.a: $(LIBS_ORELEI_OBJS) $(LIBS_ORELEI_INCS) 14 | $(TARGET_AR) crs $@ $(LIBS_ORELEI_OBJS) 15 | 16 | -------------------------------------------------------------------------------- /toolsrc/xainterleave/targets.make: -------------------------------------------------------------------------------- 1 | OUTPUT_TOOLS += $(OUTPUT_BINDIR)xainterleave$(EXEPOST) 2 | OUTPUT_TOOLS_OBJS += 3 | TOOLS_XAINTERLEAVE_SRCS = toolsrc/xainterleave/xainterleave.c 4 | TOOLS_XAINTERLEAVE_INCS = 5 | 6 | $(OUTPUT_BINDIR)xainterleave$(EXEPOST): $(TOOLS_XAINTERLEAVE_SRCS) $(TOOLS_XAINTERLEAVE_INCS) 7 | $(NATIVE_CC) -o $@ $(TOOLS_XAINTERLEAVE_SRCS) $(NATIVE_CFLAGS) $(NATIVE_LDFLAGS) -lavcodec -lavformat -lavutil -lswresample 8 | 9 | -------------------------------------------------------------------------------- /src/chenboot/targets.make: -------------------------------------------------------------------------------- 1 | LIBS_CHENBOOT_OBJS = \ 2 | src/chenboot/chenboot.o 3 | 4 | OUTPUT_LIBS += $(OUTPUT_LIBDIR)libchenboot.a 5 | OUTPUT_LIBS_OBJS += $(LIBS_CHENBOOT_OBJS) 6 | 7 | LIBS_CHENBOOT_INCS = 8 | 9 | src/chenboot/%.o: src/chenboot/%.S 10 | $(TARGET_CC) -g -c -o $@ $< $(TARGET_CFLAGS) 11 | 12 | $(OUTPUT_LIBDIR)libchenboot.a: $(LIBS_CHENBOOT_OBJS) $(LIBS_CHENBOOT_INCS) 13 | $(TARGET_AR) crs $@ $(LIBS_CHENBOOT_OBJS) 14 | 15 | -------------------------------------------------------------------------------- /toolsrc/libpsxav/targets.make: -------------------------------------------------------------------------------- 1 | TOOLS_LIBPSXAV_OBJS = 2 | TOOLS_LIBPSXAV_OBJS += toolsrc/libpsxav/adpcm.o 3 | TOOLS_LIBPSXAV_OBJS += toolsrc/libpsxav/cdrom.o 4 | 5 | TOOLS_LIBPSXAV_INCS = 6 | TOOLS_LIBPSXAV_INCS += toolsrc/libpsxav/libpsxav.h 7 | 8 | OUTPUT_TOOL_LIBS += toolsrc/libpsxav/libpsxav$(LIBPOST) 9 | OUTPUT_TOOL_LIBS_OBJS += $(TOOLS_LIBPSXAV_OBJS) 10 | 11 | toolsrc/libpsxav/%.o: toolsrc/libpsxav/%.c 12 | $(NATIVE_CC) -c -o $@ $< $(NATIVE_CFLAGS) 13 | 14 | toolsrc/libpsxav/libpsxav$(LIBPOST): $(TOOLS_LIBPSXAV_OBJS) $(TOOLS_LIBPSXAV_INCS) 15 | $(NATIVE_AR) crs $@ $(TOOLS_LIBPSXAV_OBJS) 16 | -------------------------------------------------------------------------------- /toolsrc/psxavenc/targets.make: -------------------------------------------------------------------------------- 1 | OUTPUT_TOOLS += $(OUTPUT_BINDIR)psxavenc$(EXEPOST) 2 | OUTPUT_TOOLS_OBJS += 3 | 4 | TOOLS_PSXAVENC_SRCS = 5 | TOOLS_PSXAVENC_SRCS += toolsrc/psxavenc/cdrom.c 6 | TOOLS_PSXAVENC_SRCS += toolsrc/psxavenc/decoding.c 7 | TOOLS_PSXAVENC_SRCS += toolsrc/psxavenc/filefmt.c 8 | TOOLS_PSXAVENC_SRCS += toolsrc/psxavenc/mdec.c 9 | TOOLS_PSXAVENC_SRCS += toolsrc/psxavenc/psxavenc.c 10 | 11 | TOOLS_PSXAVENC_INCS = 12 | TOOLS_PSXAVENC_INCS += toolsrc/psxavenc/common.h 13 | 14 | $(OUTPUT_BINDIR)psxavenc$(EXEPOST): $(TOOLS_PSXAVENC_SRCS) $(TOOLS_PSXAVENC_INCS) toolsrc/libpsxav/libpsxav.a 15 | $(NATIVE_CC) -o $@ $(TOOLS_PSXAVENC_SRCS) $(NATIVE_CFLAGS) $(NATIVE_LDFLAGS) \ 16 | -Itoolsrc/libpsxav -Ltoolsrc/libpsxav \ 17 | -lavcodec -lavformat -lavutil -lswresample -lswscale -lpsxav -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, 2018, 2019, 2020 Adrian "asie" Siekierka 2 | Copyright (c) 2017, 2018, 2019, 2020 Ben "GreaseMonkey" Russell 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 3. This notice may not be removed or altered from any source distribution. 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CROSSPREFIX = mipsel-elf- 2 | RM = rm 3 | RM_F = $(RM) -f 4 | NATIVE_AR = $(AR) 5 | NATIVE_CC = $(CC) 6 | NATIVE_CFLAGS = -O2 -g 7 | NATIVE_LDFLAGS = -O2 -g 8 | TARGET_AR = $(CROSSPREFIX)gcc-ar 9 | TARGET_CC = $(CROSSPREFIX)gcc 10 | TARGET_LD = $(CROSSPREFIX)ld 11 | TARGET_RANLIB = $(CROSSPREFIX)ranlib 12 | TARGET_CFLAGS = -flto -O2 -g -msoft-float -mips1 -Iinclude 13 | TARGET_LDFLAGS = -flto -O2 -g -msoft-float -mips1 14 | 15 | EXEPOST= 16 | LIBPOST=.a 17 | OUTPUT_BINDIR = bin/ 18 | OUTPUT_LIBDIR = lib/ 19 | 20 | fake_all: all 21 | 22 | include src/targets.make 23 | include toolsrc/targets.make 24 | 25 | all: tools libs 26 | 27 | clean: clean_tools clean_libs 28 | 29 | clean_tools: 30 | $(RM_F) $(OUTPUT_TOOL_LIBS) $(OUTPUT_TOOL_LIBS_OBJS) || true 31 | $(RM_F) $(OUTPUT_TOOLS) $(OUTPUT_TOOLS_OBJS) || true 32 | 33 | clean_libs: 34 | $(RM_F) $(OUTPUT_LIBS) $(OUTPUT_LIBS_OBJS) || true 35 | 36 | libs: $(OUTPUT_LIBS) 37 | 38 | tools: $(OUTPUT_TOOL_LIBS) $(OUTPUT_TOOLS) 39 | 40 | .DUMMY: fake_all all tools libs 41 | 42 | -------------------------------------------------------------------------------- /examples/chenboot_triangle/Makefile: -------------------------------------------------------------------------------- 1 | # Currently haven't put this with the rest of the fire. 2 | 3 | SRCS = \ 4 | main.c 5 | 6 | LIBS = -nostdlib -L../../lib -lgcc -lc -lchenboot 7 | INCLUDES = -I../../include 8 | 9 | CROSSPREFIX = mipsel-elf- 10 | TARGET_CC = $(CROSSPREFIX)gcc 11 | TARGET_CFLAGS = -g -flto -O2 -msoft-float -mips1 $(INCLUDES) 12 | TARGET_LDFLAGS = -g -flto -O2 -msoft-float -mips1 -Wl,-Ttext-segment=0x80010000 $(LIBS) 13 | ELF2PSX = ../../bin/elf2psx 14 | ISO2RAW = ../../bin/iso2raw 15 | PSCD_NEW = ../../bin/pscd-new 16 | MKISOFS = mkisofs 17 | RM_F = rm -f 18 | 19 | #all: triangle.iso.bin 20 | all: triangle.bin 21 | 22 | clean: 23 | $(RM_F) triangle.elf 24 | 25 | #triangle.iso.bin: triangle.iso 26 | # $(ISO2RAW) ../../dat/isolicence.pal triangle.iso 27 | 28 | #triangle.iso: triangle.exe 29 | # $(MKISOFS) -o triangle.iso system.cnf triangle.exe 30 | 31 | triangle.bin triangle.cue: triangle.exe system.cnf manifest.txt 32 | $(PSCD_NEW) manifest.txt 33 | 34 | triangle.exe: triangle.elf 35 | $(ELF2PSX) -p triangle.elf triangle.exe 36 | 37 | triangle.elf: $(SRCS) 38 | $(TARGET_CC) -o triangle.elf $(SRCS) $(TARGET_CFLAGS) $(TARGET_LDFLAGS) 39 | 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CandyK-PSX: A candy shop of tools and libraries for PlayStation development 2 | 3 | This readme will need to be better fleshed-out. 4 | 5 | ## Licensing 6 | 7 | CandyK, as a whole, is licensed under the "zlib license". Please check the LICENSE file for more information. 8 | 9 | ### Exceptions 10 | 11 | Toolchain examples are licensed under the Creative Commons Zero license - feel absolutely free to base your code on them! 12 | 13 | ## Installation 14 | 15 | ### Binary 16 | 17 | 1. Install or compile pacman (the Arch Linux package manager). 18 | 2. Append the following to /etc/pacman.conf: 19 | 20 | ``` 21 | [candyk] 22 | Server = http://candyk.asie.pl/repo/x86_64 23 | ``` 24 | 25 | 3. `pacman -Syu` 26 | 4. `pacman -S candyk-psx` 27 | 28 | ### Source 29 | 30 | TODO 31 | 32 | ## Architecture 33 | 34 | ### Directories 35 | 36 | * `bin/`: Target directory for host-native tools 37 | * `lib/`: Target directory for PlayStation libraries 38 | * `src/`: Source code for PlayStation libraries 39 | * `toolsrc/`: Source code for host-native tools 40 | 41 | ### Makefile system 42 | 43 | TODO: formalise this. 44 | 45 | Basically there's a bunch of `target.make` files which are included as needed. 46 | 47 | -------------------------------------------------------------------------------- /include/psxdefs.h: -------------------------------------------------------------------------------- 1 | /* 2 | psxdefs: PS register/constants definitions 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include "psxdefs/regs.h" 25 | 26 | #include "psxdefs/cdrom.h" 27 | #include "psxdefs/gpu.h" 28 | #include "psxdefs/intc.h" 29 | #include "psxdefs/joy.h" 30 | #include "psxdefs/spu.h" -------------------------------------------------------------------------------- /include/psxdefs/joy.h: -------------------------------------------------------------------------------- 1 | /* 2 | psxdefs: PS register/constants definitions 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #define PAD_SELECT 0x0001 25 | #define PAD_L3 0x0002 26 | #define PAD_R3 0x0004 27 | #define PAD_START 0x0008 28 | #define PAD_UP 0x0010 29 | #define PAD_RIGHT 0x0020 30 | #define PAD_DOWN 0x0040 31 | #define PAD_LEFT 0x0080 32 | #define PAD_L2 0x0100 33 | #define PAD_R2 0x0200 34 | #define PAD_L1 0x0400 35 | #define PAD_R1 0x0800 36 | #define PAD_T 0x1000 37 | #define PAD_O 0x2000 38 | #define PAD_X 0x4000 39 | #define PAD_S 0x8000 40 | 41 | #define PAD_MOUSE_L 0x0800 42 | #define PAD_MOUSE_R 0x0400 43 | -------------------------------------------------------------------------------- /include/sawpads.h: -------------------------------------------------------------------------------- 1 | /* 2 | sawpads: Actually Tested* Joypad Code 3 | 4 | Copyright (c) 2017, 2019 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | typedef struct sawpads_controller { 25 | uint8_t id, hid; 26 | uint16_t buttons; 27 | uint16_t analogs; 28 | uint8_t axes[4]; 29 | uint8_t rumble[2]; 30 | } sawpads_controller_t; 31 | 32 | int32_t sawpads_read_card_sector(uint8_t port, uint16_t address, uint8_t *buffer); 33 | int32_t sawpads_write_card_sector(uint8_t port, uint16_t address, uint8_t *buffer); 34 | 35 | void sawpads_isr_joy(void); 36 | void sawpads_isr_vblank(void); 37 | 38 | void sawpads_do_read_controller(uint8_t port); 39 | void sawpads_do_read(void); 40 | void sawpads_unlock_dualshock(uint8_t port); 41 | 42 | extern volatile sawpads_controller_t sawpads_controller[2]; 43 | -------------------------------------------------------------------------------- /include/psxdefs/intc.h: -------------------------------------------------------------------------------- 1 | /* 2 | psxdefs: PS register/constants definitions 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #define INTC_VBLANK 0x0001 25 | #define INTC_GPU 0x0002 26 | #define INTC_CDROM 0x0004 27 | #define INTC_DMA 0x0008 28 | #define INTC_TMR0 0x0010 29 | #define INTC_TMR1 0x0020 30 | #define INTC_TMR2 0x0040 31 | #define INTC_JOY 0x0080 /* also memcard */ 32 | #define INTC_SIO 0x0100 33 | #define INTC_SPU 0x0200 34 | #define INTC_JOYAUX 0x0400 35 | 36 | #define INTC_VBLANK_IDX 0 37 | #define INTC_GPU_IDX 1 38 | #define INTC_CDROM_IDX 2 39 | #define INTC_DMA_IDX 3 40 | #define INTC_TMR0_IDX 4 41 | #define INTC_TMR1_IDX 5 42 | #define INTC_TMR2_IDX 6 43 | #define INTC_JOY_IDX 7 /* also memcard */ 44 | #define INTC_SIO_IDX 8 45 | #define INTC_SPU_IDX 9 46 | #define INTC_JOYAUX_IDX 10 47 | 48 | -------------------------------------------------------------------------------- /include/chenboot.h: -------------------------------------------------------------------------------- 1 | /* 2 | chenboot: crt0 and interrupt bootstrap facilities for the PlayStation 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | */ 22 | 23 | #include 24 | 25 | // 26 | // Interrupt Service Routine handling 27 | // 28 | typedef struct chenboot_exception_frame { 29 | uint32_t epc; 30 | uint32_t thread_ra; 31 | uint32_t thread_at; 32 | uint32_t thread_v[2]; 33 | uint32_t thread_a[4]; 34 | uint32_t thread_t[10]; 35 | uint32_t sr; 36 | uint32_t cause; 37 | } __attribute__((__packed__)) chenboot_exception_frame_t; 38 | 39 | extern chenboot_exception_frame_t *(*chenboot_isr_hook)(chenboot_exception_frame_t *sp); 40 | chenboot_exception_frame_t *chenboot_isr_default(chenboot_exception_frame_t *sp); 41 | void chenboot_isr_install( 42 | chenboot_exception_frame_t *(*hook_cb)( 43 | chenboot_exception_frame_t *sp)); 44 | void chenboot_isr_enable(void); 45 | void chenboot_isr_disable(void); 46 | 47 | -------------------------------------------------------------------------------- /include/seedy.h: -------------------------------------------------------------------------------- 1 | /* 2 | seedy: CD-ROM driver 3 | 4 | Copyright (c) 2017, 2019 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #define SEEDY_READ_WHOLE_SECTORS 1 25 | #define SEEDY_READ_SINGLE_SPEED 2 26 | #define SEEDY_PLAY_XA_18900 4 27 | #define SEEDY_PLAY_XA_STEREO 8 28 | #define SEEDY_PLAY_XA_8BIT 16 29 | #define SEEDY_PLAY_XA_EMPHASIS 32 30 | 31 | // flag-less variants of the above 32 | #define SEEDY_READ_DOUBLE_SPEED 0 33 | #define SEEDY_PLAY_XA_37800 0 34 | #define SEEDY_PLAY_XA_MONO 0 35 | #define SEEDY_PLAY_XA_4BIT 0 36 | 37 | int seedy_is_xa_playing(void); 38 | void seedy_read_xa(int lba, int flags, int file, int channel); 39 | void seedy_stop_xa(void); 40 | 41 | int seedy_read_data_sync(int lba, int flags, uint8_t *buffer, int size); 42 | 43 | void seedy_drive_start(void); 44 | void seedy_drive_stop(void); 45 | 46 | void seedy_isr_cdrom(void); 47 | void seedy_init_cdrom(void); 48 | 49 | -------------------------------------------------------------------------------- /toolsrc/psxavenc/cdrom.c: -------------------------------------------------------------------------------- 1 | /* 2 | psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend 3 | 4 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 5 | Copyright (c) 2019 Ben "GreaseMonkey" Russell 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include "common.h" 25 | 26 | void init_sector_buffer_video(uint8_t *buffer, settings_t *settings) { 27 | memset(buffer,0,2352); 28 | memset(buffer+0x001,0xFF,10); 29 | 30 | buffer[0x00F] = 0x02; 31 | buffer[0x010] = settings->file_number; 32 | buffer[0x011] = settings->channel_number & 0x1F; 33 | buffer[0x012] = 0x08 | 0x40; 34 | buffer[0x013] = 0x00; 35 | memcpy(buffer + 0x014, buffer + 0x010, 4); 36 | } 37 | 38 | void calculate_edc_data(uint8_t *buffer) 39 | { 40 | uint32_t edc = 0; 41 | for (int i = 0x010; i < 0x818; i++) { 42 | edc ^= 0xFF&(uint32_t)buffer[i]; 43 | for (int ibit = 0; ibit < 8; ibit++) { 44 | edc = (edc>>1)^(0xD8018001*(edc&0x1)); 45 | } 46 | } 47 | buffer[0x818] = (uint8_t)(edc); 48 | buffer[0x819] = (uint8_t)(edc >> 8); 49 | buffer[0x81A] = (uint8_t)(edc >> 16); 50 | buffer[0x81B] = (uint8_t)(edc >> 24); 51 | 52 | // TODO: ECC 53 | } 54 | -------------------------------------------------------------------------------- /include/psxdefs/spu.h: -------------------------------------------------------------------------------- 1 | /* 2 | psxdefs: PS register/constants definitions 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #define SPU_CHANNEL_COUNT 24 25 | 26 | #define SPU_CTRL_MASTER_ENABLE 0x8000 27 | #define SPU_CTRL_MASTER_UNMUTE 0x4000 28 | #define SPU_CTRL_NOISE_SHIFT(n) (((n)&0xF)<<10) 29 | #define SPU_CTRL_NOISE_STEP(n) (((n)&0x3)<<8) 30 | #define SPU_CTRL_REVERB_ENABLE 0x0080 31 | #define SPU_CTRL_IRQ9_ENABLE 0x0040 32 | #define SPU_CTRL_TRANSFER_MODE(n) (((n)&0x3)<<4) 33 | #define SPU_CTRL_TRANSFER_MODE_MASK (((0x3)&0x3)<<4) 34 | #define SPU_CTRL_EXTERNAL_REVERB 0x0008 35 | #define SPU_CTRL_CDROM_REVERB 0x0004 36 | #define SPU_CTRL_EXTERNAL_ENABLE 0x0002 37 | #define SPU_CTRL_CDROM_ENABLE 0x0001 38 | 39 | #define SPU_STAT_TRANSFER_MODE(n) (((n)&0x3)<<4) 40 | #define SPU_STAT_TRANSFER_MODE_MASK (((0x3)&0x3)<<4) 41 | #define SPU_STAT_EXTERNAL_REVERB 0x0008 42 | #define SPU_STAT_CDROM_REVERB 0x0004 43 | #define SPU_STAT_EXTERNAL_ENABLE 0x0002 44 | #define SPU_STAT_CDROM_ENABLE 0x0001 45 | 46 | #define SPU_TRANSFER_MODE_STOP 0x0 47 | #define SPU_TRANSFER_MODE_FIFO_WRITE 0x1 48 | #define SPU_TRANSFER_MODE_DMA_WRITE 0x2 49 | #define SPU_TRANSFER_MODE_DMA_READ 0x3 50 | 51 | -------------------------------------------------------------------------------- /include/orelei.h: -------------------------------------------------------------------------------- 1 | /* 2 | orelei: PS SPU sound driver 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | // orelei.c 24 | int orelei_note_to_pitch(int note, int cxoctave, unsigned int cxspeed); 25 | void orelei_commit_key_changes(void); 26 | void orelei_play_note(int ch, int sram_addr, int adsr, int voll, int volr, int pitch); 27 | void orelei_stop_note(int ch); 28 | 29 | void orelei_sram_write_blocking(int sram_addr, void const* data, size_t len); 30 | void orelei_pack_spu(uint8_t *outbuf, const int16_t *inbuf, int16_t *pred1, int16_t *pred2, int blocks, int loopbeg, int loopend, bool fade_on_loop); 31 | 32 | void orelei_init_spu(void); 33 | 34 | // orelei.c - cd audio 35 | void orelei_open_cd_audio(int voll, int volr); 36 | void orelei_close_cd_audio(); 37 | 38 | // midi.c 39 | #define ORELEI_MIDI_MASTER_COUNT 16 40 | #define ORELEI_MIDI_MAX_TRACKS 32 41 | struct midi_master { 42 | uint8_t rpn_lo, rpn_hi; 43 | uint8_t prg; 44 | uint16_t wheel_depth; 45 | uint16_t real_wheel; 46 | int16_t wheel; 47 | }; 48 | struct midi_slave { 49 | uint32_t last_touched; 50 | uint8_t ch; 51 | uint8_t note; 52 | }; 53 | struct midi_track { 54 | int32_t ticks_waiting; 55 | uint8_t last_v0; 56 | const uint8_t *mptr; 57 | const uint8_t *mptr_beg; 58 | const uint8_t *mptr_end; 59 | }; 60 | 61 | void orelei_midi_reset(void); 62 | void orelei_midi_update(int32_t time_advanced_us, void (*f_play_note)(int hwch, int ch, int prg, int note, int vel, int wheel)); 63 | void orelei_midi_load_from_data(const uint8_t *midi_data); 64 | 65 | -------------------------------------------------------------------------------- /include/psxdefs/cdrom.h: -------------------------------------------------------------------------------- 1 | /* 2 | psxdefs: PS register/constants definitions 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #define CD_CMD_SYNC_XX 0x00 25 | #define CD_CMD_GETSTAT 0x01 26 | #define CD_CMD_SETLOC 0x02 27 | #define CD_CMD_PLAY 0x03 28 | #define CD_CMD_FORWARD 0x04 29 | #define CD_CMD_BACKWARD 0x05 30 | #define CD_CMD_READN 0x06 31 | #define CD_CMD_MOTORON 0x07 32 | #define CD_CMD_STOP 0x08 33 | #define CD_CMD_PAUSE 0x09 34 | #define CD_CMD_INIT 0x0A 35 | #define CD_CMD_MUTE 0x0B 36 | #define CD_CMD_DEMUTE 0x0C 37 | #define CD_CMD_SETFILTER 0x0D 38 | #define CD_CMD_SETMODE 0x0E 39 | #define CD_CMD_GETPARAM 0x0F 40 | #define CD_CMD_GETLOCL 0x10 41 | #define CD_CMD_GETLOCP 0x11 42 | #define CD_CMD_SETSESSION 0x12 43 | #define CD_CMD_GETTN 0x13 44 | #define CD_CMD_GETTD 0x14 45 | #define CD_CMD_SEEKL 0x15 46 | #define CD_CMD_SEEKP 0x16 47 | #define CD_CMD_SETCLOCK_XX 0x17 48 | #define CD_CMD_GETCLOCK_XX 0x18 49 | #define CD_CMD_TEST 0x19 50 | #define CD_CMD_GETID 0x1A 51 | #define CD_CMD_READS 0x1B 52 | #define CD_CMD_RESET 0x1C 53 | #define CD_CMD_GETQ 0x1D 54 | #define CD_CMD_READTOC 0x1E 55 | 56 | #define CD_CMD_SECRET_1 0x50 57 | #define CD_CMD_SECRET_2 0x51 58 | #define CD_CMD_SECRET_3 0x52 59 | #define CD_CMD_SECRET_4 0x53 60 | #define CD_CMD_SECRET_5 0x54 61 | #define CD_CMD_SECRET_6 0x55 62 | #define CD_CMD_SECRET_7 0x56 63 | #define CD_CMD_SECRETLOCK 0x57 64 | 65 | -------------------------------------------------------------------------------- /toolsrc/libpsxav/cdrom.c: -------------------------------------------------------------------------------- 1 | /* 2 | libpsxav: MDEC video + SPU/XA-ADPCM audio library 3 | 4 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 5 | Copyright (c) 2019 Ben "GreaseMonkey" Russell 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include 25 | #include "libpsxav.h" 26 | 27 | static uint32_t psx_cdrom_calculate_edc(uint8_t *sector, uint32_t offset, uint32_t size) 28 | { 29 | uint32_t edc = 0; 30 | for (int i = offset; i < offset+size; i++) { 31 | edc ^= 0xFF&(uint32_t)sector[i]; 32 | for (int ibit = 0; ibit < 8; ibit++) { 33 | edc = (edc>>1)^(0xD8018001*(edc&0x1)); 34 | } 35 | } 36 | return edc; 37 | } 38 | 39 | void psx_cdrom_calculate_checksums(uint8_t *sector, psx_cdrom_sector_type_t type) 40 | { 41 | switch (type) { 42 | case PSX_CDROM_SECTOR_TYPE_MODE1: { 43 | uint32_t edc = psx_cdrom_calculate_edc(sector, 0x0, 0x810); 44 | sector[0x810] = (uint8_t)(edc); 45 | sector[0x811] = (uint8_t)(edc >> 8); 46 | sector[0x812] = (uint8_t)(edc >> 16); 47 | sector[0x813] = (uint8_t)(edc >> 24); 48 | 49 | memset(sector + 0x814, 0, 8); 50 | // TODO: ECC 51 | } break; 52 | case PSX_CDROM_SECTOR_TYPE_MODE2_FORM1: { 53 | uint32_t edc = psx_cdrom_calculate_edc(sector, 0x10, 0x808); 54 | sector[0x818] = (uint8_t)(edc); 55 | sector[0x819] = (uint8_t)(edc >> 8); 56 | sector[0x81A] = (uint8_t)(edc >> 16); 57 | sector[0x81B] = (uint8_t)(edc >> 24); 58 | 59 | // TODO: ECC 60 | } break; 61 | case PSX_CDROM_SECTOR_TYPE_MODE2_FORM2: { 62 | uint32_t edc = psx_cdrom_calculate_edc(sector, 0x10, 0x91C); 63 | sector[0x92C] = (uint8_t)(edc); 64 | sector[0x92D] = (uint8_t)(edc >> 8); 65 | sector[0x92E] = (uint8_t)(edc >> 16); 66 | sector[0x92F] = (uint8_t)(edc >> 24); 67 | } break; 68 | } 69 | } -------------------------------------------------------------------------------- /toolsrc/libpsxav/libpsxav.h: -------------------------------------------------------------------------------- 1 | /* 2 | libpsxav: MDEC video + SPU/XA-ADPCM audio library 3 | 4 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 5 | Copyright (c) 2019 Ben "GreaseMonkey" Russell 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #ifndef __LIBPSXAV_H__ 25 | #define __LIBPSXAV_H__ 26 | 27 | #include 28 | #include 29 | 30 | // audio.c 31 | 32 | #define PSX_AUDIO_XA_FREQ_SINGLE 18900 33 | #define PSX_AUDIO_XA_FREQ_DOUBLE 37800 34 | 35 | typedef enum { 36 | PSX_AUDIO_XA_FORMAT_XA, // .xa file 37 | PSX_AUDIO_XA_FORMAT_XACD // 2352-byte sector 38 | } psx_audio_xa_format_t; 39 | 40 | typedef struct { 41 | psx_audio_xa_format_t format; 42 | bool stereo; // false or true 43 | int frequency; // 18900 or 37800 Hz 44 | int bits_per_sample; // 4 or 8 45 | int file_number; // 00-FF 46 | int channel_number; // 00-1F 47 | } psx_audio_xa_settings_t; 48 | 49 | typedef struct { 50 | int qerr; // quanitisation error 51 | uint64_t mse; // mean square error 52 | int prev1, prev2; 53 | } psx_audio_encoder_channel_state_t; 54 | 55 | typedef struct { 56 | psx_audio_encoder_channel_state_t left; 57 | psx_audio_encoder_channel_state_t right; 58 | } psx_audio_encoder_state_t; 59 | 60 | #define PSX_AUDIO_SPU_LOOP_END 1 61 | #define PSX_AUDIO_SPU_LOOP_REPEAT 3 62 | #define PSX_AUDIO_SPU_LOOP_START 4 63 | 64 | uint32_t psx_audio_xa_get_buffer_size(psx_audio_xa_settings_t settings, int sample_count); 65 | uint32_t psx_audio_spu_get_buffer_size(int sample_count); 66 | uint32_t psx_audio_xa_get_buffer_size_per_sector(psx_audio_xa_settings_t settings); 67 | uint32_t psx_audio_spu_get_buffer_size_per_block(void); 68 | uint32_t psx_audio_xa_get_samples_per_sector(psx_audio_xa_settings_t settings); 69 | uint32_t psx_audio_spu_get_samples_per_block(void); 70 | int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output); 71 | int psx_audio_xa_encode_simple(psx_audio_xa_settings_t settings, int16_t* samples, int sample_count, uint8_t *output); 72 | int psx_audio_spu_encode(psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output); 73 | int psx_audio_spu_encode_simple(int16_t* samples, int sample_count, uint8_t *output, int loop_start); 74 | int psx_audio_xa_encode_finalize(psx_audio_xa_settings_t settings, uint8_t *output, int output_length); 75 | void psx_audio_spu_set_flag_at_sample(uint8_t* spu_data, int sample_pos, int flag); 76 | 77 | // cdrom.c 78 | 79 | #define PSX_CDROM_SECTOR_SIZE 2352 80 | 81 | typedef enum { 82 | PSX_CDROM_SECTOR_TYPE_MODE1, 83 | PSX_CDROM_SECTOR_TYPE_MODE2_FORM1, 84 | PSX_CDROM_SECTOR_TYPE_MODE2_FORM2 85 | } psx_cdrom_sector_type_t; 86 | 87 | void psx_cdrom_calculate_checksums(uint8_t *sector, psx_cdrom_sector_type_t type); 88 | 89 | #endif /* __LIBPSXAV_H__ */ -------------------------------------------------------------------------------- /toolsrc/psxavenc/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend 3 | 4 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 5 | Copyright (c) 2019 Ben "GreaseMonkey" Russell 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #define FORMAT_XA 0 40 | #define FORMAT_XACD 1 41 | #define FORMAT_SPU 2 42 | #define FORMAT_STR2 3 43 | 44 | #define MAX_UNMUXED_BLOCKS 9 45 | typedef struct { 46 | int frame_index; 47 | int frame_block_index; 48 | int frame_block_count; 49 | int frame_block_base_overflow; 50 | int frame_block_overflow_num; 51 | int frame_block_overflow_den; 52 | uint16_t bits_value; 53 | int bits_left; 54 | uint8_t unmuxed[2016*MAX_UNMUXED_BLOCKS]; 55 | int bytes_used; 56 | int blocks_used; 57 | int uncomp_hwords_used; 58 | int quant_scale; 59 | int32_t *dct_block_lists[6]; 60 | } vid_encoder_state_t; 61 | 62 | typedef struct { 63 | int video_frame_src_size; 64 | int video_frame_dst_size; 65 | int audio_stream_index; 66 | int video_stream_index; 67 | AVFormatContext* format; 68 | AVStream* audio_stream; 69 | AVStream* video_stream; 70 | AVCodecContext* audio_codec_context; 71 | AVCodecContext* video_codec_context; 72 | AVCodec* audio_codec; 73 | AVCodec* video_codec; 74 | struct SwrContext* resampler; 75 | struct SwsContext* scaler; 76 | AVFrame* frame; 77 | 78 | int sample_count_mul; 79 | 80 | double video_next_pts; 81 | } av_decoder_state_t; 82 | 83 | typedef struct { 84 | int format; // FORMAT_* 85 | bool stereo; // false or true 86 | int frequency; // 18900 or 37800 Hz 87 | int bits_per_sample; // 4 or 8 88 | int file_number; // 00-FF 89 | int channel_number; // 00-1F 90 | 91 | int video_width; 92 | int video_height; 93 | int video_fps_num; // FPS numerator 94 | int video_fps_den; // FPS denominator 95 | 96 | int16_t *audio_samples; 97 | int audio_sample_count; 98 | uint8_t *video_frames; 99 | int video_frame_count; 100 | 101 | av_decoder_state_t decoder_state_av; 102 | 103 | vid_encoder_state_t state_vid; 104 | } settings_t; 105 | 106 | // cdrom.c 107 | void init_sector_buffer_video(uint8_t *buffer, settings_t *settings); 108 | void calculate_edc_data(uint8_t *buffer); 109 | 110 | // decoding.c 111 | bool open_av_data(const char *filename, settings_t *settings); 112 | bool poll_av_data(settings_t *settings); 113 | bool ensure_av_data(settings_t *settings, int needed_audio_samples, int needed_video_frames); 114 | void pull_all_av_data(settings_t *settings); 115 | void retire_av_data(settings_t *settings, int retired_audio_samples, int retired_video_frames); 116 | void close_av_data(settings_t *settings); 117 | 118 | // filefmt.c 119 | void encode_file_spu(int16_t *audio_samples, int audio_sample_count, settings_t *settings, FILE *output); 120 | void encode_file_xa(int16_t *audio_samples, int audio_sample_count, settings_t *settings, FILE *output); 121 | void encode_file_str(settings_t *settings, FILE *output); 122 | 123 | // mdec.c 124 | void encode_block_str(uint8_t *video_frames, int video_frame_count, uint8_t *output, settings_t *settings); 125 | -------------------------------------------------------------------------------- /toolsrc/xainterleave/xainterleave.c: -------------------------------------------------------------------------------- 1 | /* 2 | xainterleave: simple sector interleaving tool 3 | 4 | Copyright (c) 2019 Adrian "asie" Siekierka 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define ENTRY_MAX 64 29 | 30 | #define TYPE_NULL 0 31 | #define TYPE_RAW 1 32 | #define TYPE_XA 2 33 | #define TYPE_XACD 3 34 | 35 | typedef struct { 36 | int sectors, type; 37 | 38 | FILE *file; 39 | 40 | int xa_file; 41 | int xa_channel; 42 | } entry_t; 43 | 44 | static entry_t entries[ENTRY_MAX]; 45 | 46 | int parse(char *filename) { 47 | entry_t e; 48 | char type_str[65]; 49 | char fn_str[257]; 50 | int entry_count = 0; 51 | FILE *file = fopen(filename, "r"); 52 | if (file == NULL) return 0; 53 | 54 | while (fscanf(file, " %d %64s", &(e.sectors), type_str) > 0) { 55 | if (strcmp(type_str, "null") == 0) e.type = TYPE_NULL; 56 | else if (strcmp(type_str, "raw") == 0) e.type = TYPE_RAW; 57 | else if (strcmp(type_str, "xacd") == 0) e.type = TYPE_XACD; 58 | else if (strcmp(type_str, "xa") == 0) e.type = TYPE_XA; 59 | else { fprintf(stderr, "Unknown type: %s\n", type_str); continue; } 60 | 61 | switch (e.type) { 62 | case TYPE_RAW: 63 | case TYPE_XA: 64 | case TYPE_XACD: 65 | if (fscanf(file, " %256s", fn_str) > 0) { 66 | if ((e.file = fopen(fn_str, "rb")) == NULL) return 0; 67 | } else return 0; 68 | break; 69 | } 70 | 71 | switch (e.type) { 72 | case TYPE_XA: 73 | case TYPE_XACD: 74 | if (fscanf(file, " %d %d", &(e.xa_file), &(e.xa_channel)) > 0) { 75 | // nop 76 | } else { 77 | e.xa_file = 0; 78 | e.xa_channel = 0; 79 | } 80 | break; 81 | } 82 | 83 | entries[entry_count] = e; 84 | entry_count++; 85 | } 86 | 87 | fclose(file); 88 | return entry_count; 89 | } 90 | 91 | int main(int argc, char** argv) { 92 | uint8_t buffer[2352]; 93 | 94 | if (argc < 3) { 95 | fprintf(stderr, "Usage: xainterleave \n"); 96 | return 1; 97 | } 98 | 99 | int entry_count = parse(argv[1]); 100 | if (entry_count <= 0) { 101 | fprintf(stderr, "Empty manifest?\n"); 102 | return 1; 103 | } 104 | 105 | int sector_div = 0; 106 | for (int i = 0; i < entry_count; i++) { 107 | sector_div += entries[i].sectors; 108 | } 109 | printf("Interleaving into %d-sector chunks\n", sector_div); 110 | 111 | FILE *output = fopen(argv[2], "wb"); 112 | 113 | while (1) { 114 | int can_read = 0; 115 | for (int i = 0; i < entry_count; i++) { 116 | entry_t *e = &entries[i]; 117 | if (e->file != NULL) { 118 | if (!feof(e->file)) can_read++; 119 | } 120 | } 121 | if (can_read <= 0) break; 122 | 123 | for (int i = 0; i < entry_count; i++) { 124 | entry_t *e = &entries[i]; 125 | for (int is = 0; is < e->sectors; is++) { 126 | int write_null = 0; 127 | switch (e->type) { 128 | case TYPE_NULL: 129 | write_null = 1; 130 | break; 131 | case TYPE_RAW: 132 | if (!fread(buffer, 2352, 1, e->file)) { write_null = 1; break; } 133 | fwrite(buffer, 2352, 1, output); 134 | break; 135 | case TYPE_XA: 136 | case TYPE_XACD: 137 | if (e->type == TYPE_XACD) { 138 | if (!fread(buffer, 2352, 1, e->file)) { write_null = 1; break; } 139 | } else { 140 | if (!fread(buffer + 0x10, 2336, 1, e->file)) { write_null = 1; break; } 141 | memset(buffer, 0, 15); 142 | buffer[15] = 0x02; 143 | } 144 | if (e->xa_file >= 0) buffer[0x010] = buffer[0x014] = e->xa_file; 145 | if (e->xa_channel >= 0) buffer[0x011] = buffer[0x015] = e->xa_channel & 0x1F; 146 | buffer[0x92F] = 0xFF; // make pscd-new generate EDC 147 | fwrite(buffer, 2352, 1, output); break; 148 | } 149 | 150 | if (write_null) { 151 | for (int j = 0; j < 2352; j++) fputc(0, output); 152 | } 153 | } 154 | } 155 | } 156 | 157 | fclose(output); 158 | return 0; 159 | } 160 | -------------------------------------------------------------------------------- /examples/chenboot_triangle/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | chenboot_triangle: Hello, triangle! (Bare minimum setup w/ chenboot) 3 | Copyright (C) GreaseMonkey, 2017, licensed under Creative Commons Zero: 4 | https://creativecommons.org/publicdomain/zero/1.0/ 5 | 6 | ... yeah, this is way easier than the PS2. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | volatile uint32_t vblank_counter = 0; 23 | volatile int out_vidy = 0; 24 | volatile int real_out_vidy = 0; 25 | 26 | void gpu_write_gp0_command(uint32_t v) 27 | { 28 | int i; 29 | 30 | for(i = 0; i < 1000; i+=1) { 31 | if((PSXREG_GPU_GPUSTAT & (1<<26)) != 0) { 32 | break; 33 | } 34 | } 35 | 36 | PSXREG_GPU_GP0 = v; 37 | } 38 | 39 | void gpu_write_gp0_data(uint32_t v) 40 | { 41 | int i; 42 | 43 | for(i = 0; i < 1000; i+=1) { 44 | if((PSXREG_GPU_GPUSTAT & (1<<28)) != 0) { 45 | break; 46 | } 47 | } 48 | 49 | PSXREG_GPU_GP0 = v; 50 | } 51 | 52 | void gpu_write_gp1(uint32_t v) 53 | { 54 | PSXREG_GPU_GP1 = v; 55 | } 56 | 57 | chenboot_exception_frame_t *isr_main(chenboot_exception_frame_t *sp) 58 | { 59 | // If this isn't an interrupt, spin 60 | if((sp->cause&0xFC) != 0) { 61 | chenboot_isr_disable(); 62 | for(;;) { 63 | } 64 | } 65 | 66 | // Get interrupt flags 67 | int iflags = PSXREG_I_STAT; 68 | 69 | // vblank handler 70 | if((iflags & INTC_VBLANK) != 0) { 71 | vblank_counter += 1; 72 | gpu_write_gp1(GP1_DISPLAY_START(0, out_vidy)); 73 | real_out_vidy = out_vidy; 74 | } 75 | 76 | // Acknowledge all interrupts 77 | PSXREG_I_STAT = ~iflags; 78 | 79 | // Return 80 | return sp; 81 | } 82 | 83 | int main(int argc, char *argv[]) 84 | { 85 | int vidy = 0; 86 | int x = 0; 87 | int y = -45; 88 | int vx = 4; 89 | int vy = 0; 90 | 91 | // Install ISR handler 92 | chenboot_isr_disable(); 93 | PSXREG_I_MASK = 0x0000; 94 | PSXREG_I_STAT = 0x0000; 95 | chenboot_isr_install(isr_main); 96 | chenboot_isr_enable(); 97 | PSXREG_I_MASK = (0 98 | | INTC_VBLANK 99 | ); 100 | 101 | // Reset GPU 102 | gpu_write_gp1(GP1_RESET_GPU()); 103 | 104 | // Set video mode 105 | gpu_write_gp1(GP1_DISPLAY_MODE( 106 | GPU_DISPMODE_W_320, 107 | GPU_DISPMODE_H_240P, 108 | GPU_DISPMODE_BPP_15, 109 | GPU_DISPMODE_STD_PAL)); 110 | gpu_write_gp1(GP1_DISPLAY_RANGE_X(0x260, 0x260 + 320*8)); 111 | gpu_write_gp1(GP1_DISPLAY_RANGE_Y(0xA3 - 200/2, 0xA3 + 200/2)); 112 | gpu_write_gp1(GP1_DISPLAY_START(0, vidy)); 113 | 114 | // Clear screen to colour 115 | gpu_write_gp0_command(GP0_MEM_FILL(GPU_RGB8(0x00, 0x20, 0x40))); 116 | gpu_write_gp0_data(GPU_BOX_OFFS( 0, 0+vidy)); 117 | gpu_write_gp0_data(GPU_BOX_SIZE(320, 200)); 118 | 119 | // Enable display 120 | gpu_write_gp1(GP1_DISPLAY_ENABLE()); 121 | 122 | // Reset vblank counter 123 | vblank_counter = 0; 124 | uint32_t expected_vblank_counter = 0; 125 | uint32_t vblanks = 0; 126 | 127 | // Main loop 128 | for(;;) { 129 | // 130 | // DRAW 131 | // 132 | 133 | // Swap vidy 134 | vidy = 200-vidy; 135 | 136 | // Set rendering attribs 137 | gpu_write_gp0_command(GP0_ATTR_TEXPAGE( 138 | 0, 139 | 0, 140 | GPU_TEXPAGE_BLEND_HALF, 141 | GPU_TEXPAGE_TEXBPP_4, 142 | GPU_TEXPAGE_DITHER | GPU_TEXPAGE_DRAWTODISPLAY)); 143 | gpu_write_gp0_command(GP0_ATTR_TEXWINDOW(0,0,0,0)); 144 | gpu_write_gp0_command(GP0_ATTR_DRAW_RANGE_MIN(0, 0+vidy)); 145 | gpu_write_gp0_command(GP0_ATTR_DRAW_RANGE_MAX(320-1, (200-1)+vidy)); 146 | gpu_write_gp0_command(GP0_ATTR_DRAW_OFFSET(320/2, (200/2)+vidy)); 147 | gpu_write_gp0_command(GP0_ATTR_MASKBIT(0)); 148 | 149 | // Clear screen to colour 150 | gpu_write_gp0_command(GP0_MEM_FILL(GPU_RGB8(0x00, 0x20, 0x40))); 151 | gpu_write_gp0_data(GPU_BOX_OFFS( 0, 0+vidy)); 152 | gpu_write_gp0_data(GPU_BOX_SIZE(320, 200)); 153 | 154 | // Draw a triangle 155 | gpu_write_gp0_command(GP0_TRI_FLAT(GPU_RGB8(0xFF, 0x80, 0x00))); 156 | gpu_write_gp0_data(GPU_VERTEX(( 60)+x, (-50)+y)); 157 | gpu_write_gp0_data(GPU_VERTEX(( 0)+x, ( 70)+y)); 158 | gpu_write_gp0_data(GPU_VERTEX((-60)+x, (-50)+y)); 159 | 160 | // 161 | // PHYSICS 162 | // 163 | 164 | // Move 165 | for(int i = 0; i < vblanks; i++) { 166 | vy += 1; 167 | x += vx; 168 | y += vy; 169 | if(y >= 30) { 170 | y = 30*2-y; 171 | vy = -vy; 172 | } 173 | if(x >= 100 && vx > 0) { 174 | x = 100*2-x; 175 | vx = -vx; 176 | } else if(x <= -100 && vx < 0) { 177 | x = -100*2-x; 178 | vx = -vx; 179 | } 180 | } 181 | 182 | out_vidy = vidy; 183 | 184 | // Wait for at least one vblank 185 | do { 186 | } while(out_vidy != real_out_vidy); 187 | vblanks = vblank_counter - expected_vblank_counter; 188 | expected_vblank_counter += vblanks; 189 | } 190 | 191 | return 0; 192 | } 193 | -------------------------------------------------------------------------------- /src/chenboot/chenboot.S: -------------------------------------------------------------------------------- 1 | /* 2 | chenboot: crt0 and interrupt bootstrap facilities for the PlayStation 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | */ 22 | 23 | #define I_STAT 0x1070 24 | #define I_MASK 0x1074 25 | 26 | #define FRAME_EPC ( 0*4) 27 | #define FRAME_SR (19*4) 28 | #define FRAME_CAUSE (20*4) 29 | 30 | .text 31 | .set noreorder 32 | 33 | .extern main 34 | .extern _gp 35 | .extern _edata 36 | .extern _end 37 | 38 | .global _start 39 | .global chenboot_isr_default 40 | .global chenboot_isr_install 41 | .global chenboot_isr_disable 42 | .global chenboot_isr_enable 43 | 44 | .ent __chenboot_isr 45 | .type __chenboot_isr, @function 46 | __chenboot_isr: 47 | /* Prologue */ 48 | .set noat 49 | mfc0 $k1, $14 50 | addiu $sp, $sp, -32*4 51 | sw $k1, 0*4($sp) 52 | sw $ra, 1*4($sp) 53 | sw $at, 2*4($sp) 54 | sw $v0, 3*4($sp) 55 | sw $v1, 4*4($sp) 56 | sw $a0, 5*4($sp) 57 | sw $a1, 6*4($sp) 58 | sw $a2, 7*4($sp) 59 | sw $a3, 8*4($sp) 60 | sw $t0, 9*4($sp) 61 | sw $t1,10*4($sp) 62 | sw $t2,11*4($sp) 63 | sw $t3,12*4($sp) 64 | sw $t4,13*4($sp) 65 | sw $t5,14*4($sp) 66 | sw $t6,15*4($sp) 67 | sw $t7,16*4($sp) 68 | sw $t8,17*4($sp) 69 | sw $t9,18*4($sp) 70 | 71 | /* Load sr/cause */ 72 | mfc0 $t0, $12 73 | nop 74 | mfc0 $t1, $13 75 | nop 76 | sw $t0,FRAME_SR($sp) 77 | sw $t1,FRAME_CAUSE($sp) 78 | 79 | /* Jump */ 80 | la $t2, __chenboot_isr_hook 81 | lw $t3, 0($t2) 82 | move $a0, $sp 83 | jalr $t3 84 | addiu $sp, $sp, -32 85 | addiu $sp, $sp, 32 86 | 87 | /* Epilogue */ 88 | lw $v0, 3*4($sp) 89 | lw $v1, 4*4($sp) 90 | lw $a0, 5*4($sp) 91 | lw $a1, 6*4($sp) 92 | lw $a2, 7*4($sp) 93 | lw $a3, 8*4($sp) 94 | lw $t0, 9*4($sp) 95 | lw $t1,10*4($sp) 96 | lw $t2,11*4($sp) 97 | lw $t3,12*4($sp) 98 | lw $t4,13*4($sp) 99 | lw $t5,14*4($sp) 100 | lw $t6,15*4($sp) 101 | lw $t7,16*4($sp) 102 | lw $t8,17*4($sp) 103 | lw $t9,18*4($sp) 104 | 105 | lw $at, 2*4($sp) 106 | lw $ra, 1*4($sp) 107 | lw $k0, 0*4($sp) 108 | addiu $sp, $sp, 32*4 109 | jr $k0 110 | rfe 111 | .set at 112 | .end __chenboot_isr 113 | 114 | .ent __chenboot_isr_template 115 | .type __chenboot_isr_template, @function 116 | __chenboot_isr_template: 117 | j __chenboot_isr 118 | .end __chenboot_isr_template 119 | 120 | .ent chenboot_isr_install 121 | .type chenboot_isr_install, @function 122 | chenboot_isr_install: 123 | /* Prologue */ 124 | addiu $sp, $sp, -16 125 | sw $ra, 0x00($sp) 126 | 127 | /* Change ISR hook */ 128 | la $t0, __chenboot_isr_hook 129 | sw $a0, 0($t0) 130 | 131 | /* Replace interrupt handler */ 132 | la $t0, __chenboot_isr_template 133 | lw $t2, 0($t0) 134 | lui $t1, 0x8000 135 | sw $t2, 0x0080($t1) 136 | 137 | /* Call FlushCache */ 138 | addiu $sp, $sp, -16 139 | la $t0, 0xA0 140 | jalr $t0 141 | li $t1, 0x44 142 | addiu $sp, $sp, 16 143 | 144 | /* Epilogue */ 145 | lw $ra, 0x00($sp) 146 | nop 147 | jr $ra 148 | addiu $sp, $sp, 16 149 | .end chenboot_isr_install 150 | 151 | .ent chenboot_isr_default 152 | .type chenboot_isr_default, @function 153 | chenboot_isr_default: 154 | /* Read cause register */ 155 | /* Lock up if not an interrupt */ 156 | lw $t0, FRAME_CAUSE($a0) 157 | nop 158 | andi $t0, $t0, 0x00FC 159 | 1: 160 | bne $t0, $zero, 1b 161 | nop 162 | 163 | /* Clear all interrupts */ 164 | lui $t0, 0x1F80 165 | lh $t1, I_STAT($t0) 166 | nop 167 | nor $t1, $t1, $t1 168 | sh $t1, I_STAT($t0) 169 | 170 | /* Return */ 171 | jr $ra 172 | nop 173 | .end chenboot_isr_default 174 | 175 | .ent chenboot_isr_disable 176 | .type chenboot_isr_disable, @function 177 | chenboot_isr_disable: 178 | /* Clear Im2+IEc */ 179 | mfc0 $t0, $12 180 | li $t1, ~0x0401 181 | and $t0, $t0, $t1 182 | mtc0 $t0, $12 183 | 184 | /* Return */ 185 | jr $ra 186 | nop 187 | .end chenboot_isr_disable 188 | 189 | .ent chenboot_isr_enable 190 | .type chenboot_isr_enable, @function 191 | chenboot_isr_enable: 192 | /* Set Im2+IEc */ 193 | mfc0 $t0, $12 194 | li $t1, 0x0401 195 | or $t0, $t0, $t1 196 | mtc0 $t0, $12 197 | 198 | /* Return */ 199 | jr $ra 200 | nop 201 | .end chenboot_isr_enable 202 | 203 | .ent _start 204 | .type _start, @function 205 | _start: 206 | /* Preset some regs */ 207 | la $gp, _gp 208 | 209 | /* Clear heap */ 210 | la $t0, _edata 211 | la $t1, _end 212 | subu $t1, $t1, $t0 /* t1 to delta */ 213 | addiu $t1, $t1, 0x000F /* 16-byte align, round up */ 214 | ori $t1, $t1, 0x000F 215 | xori $t1, $t1, 0x000F 216 | addu $t1, $t1, $t0 /* t1 to ptr */ 217 | 1: 218 | sw $zero, 0x00($t0) 219 | sw $zero, 0x04($t0) 220 | sw $zero, 0x08($t0) 221 | sw $zero, 0x0C($t0) 222 | bne $t0, $t1, 1b 223 | addiu $t0, $t0, 0x10 224 | 225 | /* Jump to main */ 226 | jal main 227 | nop 228 | 229 | /* INFINITE LOOP */ 230 | 1: 231 | j 1b 232 | nop 233 | .end _start 234 | 235 | .data 236 | __chenboot_isr_hook: 237 | .word chenboot_isr_default 238 | 239 | -------------------------------------------------------------------------------- /toolsrc/psxavenc/filefmt.c: -------------------------------------------------------------------------------- 1 | /* 2 | psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend 3 | 4 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 5 | Copyright (c) 2019 Ben "GreaseMonkey" Russell 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include "common.h" 25 | #include "libpsxav.h" 26 | 27 | static psx_audio_xa_settings_t settings_to_libpsxav_xa_audio(settings_t *settings) { 28 | psx_audio_xa_settings_t new_settings; 29 | new_settings.bits_per_sample = settings->bits_per_sample; 30 | new_settings.frequency = settings->frequency; 31 | new_settings.stereo = settings->stereo; 32 | new_settings.file_number = settings->file_number; 33 | new_settings.channel_number = settings->channel_number; 34 | 35 | switch (settings->format) { 36 | case FORMAT_XA: 37 | new_settings.format = PSX_AUDIO_XA_FORMAT_XA; 38 | break; 39 | default: 40 | new_settings.format = PSX_AUDIO_XA_FORMAT_XACD; 41 | break; 42 | } 43 | 44 | return new_settings; 45 | }; 46 | 47 | void encode_file_spu(int16_t *audio_samples, int audio_sample_count, settings_t *settings, FILE *output) { 48 | psx_audio_encoder_state_t audio_state; 49 | int audio_samples_per_block = psx_audio_spu_get_samples_per_block(); 50 | uint8_t buffer[16]; 51 | 52 | memset(&audio_state, 0, sizeof(psx_audio_encoder_state_t)); 53 | 54 | for (int i = 0; i < audio_sample_count; i += audio_samples_per_block) { 55 | int samples_length = audio_sample_count - i; 56 | if (samples_length > audio_samples_per_block) samples_length = audio_samples_per_block; 57 | int length = psx_audio_spu_encode(&audio_state, audio_samples + i, samples_length, buffer); 58 | if (i == 0) { 59 | buffer[1] = PSX_AUDIO_SPU_LOOP_START; 60 | } else if ((i + audio_samples_per_block) >= audio_sample_count) { 61 | buffer[1] = PSX_AUDIO_SPU_LOOP_END; 62 | } 63 | fwrite(buffer, length, 1, output); 64 | } 65 | } 66 | 67 | void encode_file_xa(int16_t *audio_samples, int audio_sample_count, settings_t *settings, FILE *output) { 68 | psx_audio_xa_settings_t xa_settings = settings_to_libpsxav_xa_audio(settings); 69 | psx_audio_encoder_state_t audio_state; 70 | int audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings); 71 | int av_sample_mul = settings->stereo ? 2 : 1; 72 | uint8_t buffer[2352]; 73 | 74 | memset(&audio_state, 0, sizeof(psx_audio_encoder_state_t)); 75 | 76 | for (int i = 0; i < audio_sample_count; i += audio_samples_per_sector) { 77 | int samples_length = audio_sample_count - i; 78 | if (samples_length > audio_samples_per_sector) samples_length = audio_samples_per_sector; 79 | int length = psx_audio_xa_encode(xa_settings, &audio_state, audio_samples + (i * av_sample_mul), samples_length, buffer); 80 | if ((i + audio_samples_per_sector) >= audio_sample_count) { 81 | psx_audio_xa_encode_finalize(xa_settings, buffer, length); 82 | } 83 | fwrite(buffer, length, 1, output); 84 | } 85 | } 86 | 87 | void encode_file_str(settings_t *settings, FILE *output) { 88 | uint8_t buffer[2352*8]; 89 | psx_audio_xa_settings_t xa_settings = settings_to_libpsxav_xa_audio(settings); 90 | psx_audio_encoder_state_t audio_state; 91 | int audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings); 92 | int av_sample_mul = settings->stereo ? 2 : 1; 93 | 94 | memset(&audio_state, 0, sizeof(psx_audio_encoder_state_t)); 95 | 96 | settings->state_vid.frame_index = 0; 97 | settings->state_vid.bits_value = 0; 98 | settings->state_vid.bits_left = 16; 99 | settings->state_vid.frame_block_index = 0; 100 | settings->state_vid.frame_block_count = 0; 101 | 102 | settings->state_vid.frame_block_overflow_num = 0; 103 | 104 | // Number of total sectors per second: 150 105 | // Proportion of sectors for video due to A/V interleave: 7/8 106 | // 15FPS = (150*7/8/15) = 8.75 blocks per frame 107 | settings->state_vid.frame_block_base_overflow = 150*7*settings->video_fps_den; 108 | settings->state_vid.frame_block_overflow_den = 8*settings->video_fps_num; 109 | //fprintf(stderr, "%f\n", ((double)settings->state_vid.frame_block_base_overflow)/((double)settings->state_vid.frame_block_overflow_den)); abort(); 110 | 111 | // FIXME: this needs an extra frame to prevent A/V desync 112 | const int frames_needed = 2; 113 | for (int j = 0; ensure_av_data(settings, audio_samples_per_sector*av_sample_mul*frames_needed, 1*frames_needed); j+=18) { 114 | psx_audio_xa_encode(xa_settings, &audio_state, settings->audio_samples, audio_samples_per_sector, buffer + 2352 * 7); 115 | 116 | // TODO: the final buffer 117 | for(int k = 0; k < 7; k++) { 118 | init_sector_buffer_video(buffer + 2352*k, settings); 119 | } 120 | encode_block_str(settings->video_frames, settings->video_frame_count, buffer, settings); 121 | for(int k = 0; k < 8; k++) { 122 | int t = k + (j/18)*8 + 75*2; 123 | 124 | // Put the time in 125 | buffer[0x00C + 2352*k] = ((t/75/60)%10)|(((t/75/60)/10)<<4); 126 | buffer[0x00D + 2352*k] = (((t/75)%60)%10)|((((t/75)%60)/10)<<4); 127 | buffer[0x00E + 2352*k] = ((t%75)%10)|(((t%75)/10)<<4); 128 | 129 | if(k != 7) { 130 | calculate_edc_data(buffer + 2352*k); 131 | } 132 | } 133 | retire_av_data(settings, audio_samples_per_sector*av_sample_mul, 0); 134 | fwrite(buffer, 2352*8, 1, output); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /toolsrc/psxavenc/psxavenc.c: -------------------------------------------------------------------------------- 1 | /* 2 | psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend 3 | 4 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 5 | Copyright (c) 2019 Ben "GreaseMonkey" Russell 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include "common.h" 25 | 26 | void print_help(void) { 27 | fprintf(stderr, "Usage: psxavenc [-f freq] [-b bitdepth] [-c channels] [-F num] [-C num] [-t xa|xacd|spu|str2] \n\n"); 28 | fprintf(stderr, " -f freq Use specified frequency\n"); 29 | fprintf(stderr, " -t format Use specified output type:\n"); 30 | fprintf(stderr, " xa [A.] .xa 2336-byte sectors\n"); 31 | fprintf(stderr, " xacd [A.] .xa 2352-byte sectors\n"); 32 | fprintf(stderr, " spu [A.] raw SPU-ADPCM data\n"); 33 | fprintf(stderr, " str2 [AV] v2 .str video 2352-byte sectors\n"); 34 | fprintf(stderr, " -b bitdepth Use specified bit depth (only 4 bits supported)\n"); 35 | fprintf(stderr, " -c channels Use specified channel count (1 or 2)\n"); 36 | fprintf(stderr, " -F num [.xa] Set the file number to num (0-255)\n"); 37 | fprintf(stderr, " -C num [.xa] Set the channel number to num (0-31)\n"); 38 | } 39 | 40 | int parse_args(settings_t* settings, int argc, char** argv) { 41 | int c; 42 | while ((c = getopt(argc, argv, "t:f:b:c:F:C:")) != -1) { 43 | switch (c) { 44 | case 't': { 45 | if (strcmp(optarg, "xa") == 0) { 46 | settings->format = FORMAT_XA; 47 | } else if (strcmp(optarg, "xacd") == 0) { 48 | settings->format = FORMAT_XACD; 49 | } else if (strcmp(optarg, "spu") == 0) { 50 | settings->format = FORMAT_SPU; 51 | } else if (strcmp(optarg, "str2") == 0) { 52 | settings->format = FORMAT_STR2; 53 | } else { 54 | fprintf(stderr, "Invalid format: %s\n", optarg); 55 | return -1; 56 | } 57 | } break; 58 | case 'f': { 59 | settings->frequency = atoi(optarg); 60 | } break; 61 | case 'b': { 62 | settings->bits_per_sample = atoi(optarg); 63 | if (settings->bits_per_sample != 4) { 64 | fprintf(stderr, "Invalid bit depth: %d\n", settings->frequency); 65 | return -1; 66 | } 67 | } break; 68 | case 'c': { 69 | int ch = atoi(optarg); 70 | if (ch <= 0 || ch > 2) { 71 | fprintf(stderr, "Invalid channel count: %d\n", ch); 72 | return -1; 73 | } 74 | settings->stereo = (ch == 2 ? 1 : 0); 75 | } break; 76 | case 'F': { 77 | settings->file_number = atoi(optarg); 78 | if (settings->file_number < 0 || settings->file_number > 255) { 79 | fprintf(stderr, "Invalid file number: %d\n", settings->file_number); 80 | return -1; 81 | } 82 | } break; 83 | case 'C': { 84 | settings->channel_number = atoi(optarg); 85 | if (settings->channel_number < 0 || settings->channel_number > 31) { 86 | fprintf(stderr, "Invalid channel number: %d\n", settings->channel_number); 87 | return -1; 88 | } 89 | } break; 90 | case '?': 91 | case 'h': { 92 | print_help(); 93 | return -1; 94 | } break; 95 | } 96 | } 97 | 98 | if (settings->format == FORMAT_XA || settings->format == FORMAT_XACD) { 99 | if (settings->frequency != PSX_AUDIO_XA_FREQ_SINGLE && settings->frequency != PSX_AUDIO_XA_FREQ_DOUBLE) { 100 | fprintf(stderr, "Invalid frequency: %d Hz\n", settings->frequency); 101 | return -1; 102 | } 103 | } 104 | 105 | if (settings->format == FORMAT_SPU) { 106 | settings->stereo = false; 107 | } 108 | 109 | return optind; 110 | } 111 | 112 | int main(int argc, char **argv) { 113 | settings_t settings; 114 | int arg_offset; 115 | FILE* output; 116 | 117 | memset(&settings,0,sizeof(settings_t)); 118 | 119 | settings.file_number = 0; 120 | settings.channel_number = 0; 121 | settings.stereo = true; 122 | settings.frequency = PSX_AUDIO_XA_FREQ_DOUBLE; 123 | settings.bits_per_sample = 4; 124 | 125 | settings.video_width = 320; 126 | settings.video_height = 240; 127 | 128 | settings.audio_samples = NULL; 129 | settings.audio_sample_count = 0; 130 | settings.video_frames = NULL; 131 | settings.video_frame_count = 0; 132 | 133 | // TODO: make this adjustable 134 | // also for some reason ffmpeg seems to hard-code the framerate to 15fps 135 | settings.video_fps_num = 15; 136 | settings.video_fps_den = 1; 137 | for(int i = 0; i < 6; i++) { 138 | settings.state_vid.dct_block_lists[i] = NULL; 139 | } 140 | 141 | arg_offset = parse_args(&settings, argc, argv); 142 | if (arg_offset < 0) { 143 | return 1; 144 | } else if (argc < arg_offset + 2) { 145 | print_help(); 146 | return 1; 147 | } 148 | 149 | fprintf(stderr, "Using settings: %d Hz @ %d bit depth, %s. F%d C%d\n", 150 | settings.frequency, settings.bits_per_sample, 151 | settings.stereo ? "stereo" : "mono", 152 | settings.file_number, settings.channel_number 153 | ); 154 | 155 | bool did_open_data = open_av_data(argv[arg_offset + 0], &settings); 156 | if (!did_open_data) { 157 | fprintf(stderr, "Could not open input file!\n"); 158 | return 1; 159 | } 160 | 161 | output = fopen(argv[arg_offset + 1], "wb"); 162 | if (output == NULL) { 163 | fprintf(stderr, "Could not open output file!\n"); 164 | return 1; 165 | } 166 | 167 | int av_sample_mul = settings.stereo ? 2 : 1; 168 | 169 | switch (settings.format) { 170 | case FORMAT_XA: 171 | case FORMAT_XACD: 172 | pull_all_av_data(&settings); 173 | encode_file_xa(settings.audio_samples, settings.audio_sample_count / av_sample_mul, &settings, output); 174 | break; 175 | case FORMAT_SPU: 176 | pull_all_av_data(&settings); 177 | encode_file_spu(settings.audio_samples, settings.audio_sample_count / av_sample_mul, &settings, output); 178 | break; 179 | case FORMAT_STR2: 180 | encode_file_str(&settings, output); 181 | break; 182 | } 183 | 184 | fclose(output); 185 | close_av_data(&settings); 186 | return 0; 187 | } 188 | -------------------------------------------------------------------------------- /src/orelei/orelei.c: -------------------------------------------------------------------------------- 1 | /* 2 | orelei: PS SPU sound driver 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | // Required due to SPU writes being a bit unstable 36 | #define TWICE(x) x;x 37 | 38 | static const unsigned int notetab[12] = { 39 | 0x8000, 0x879C, 0x8FAD, 0x9838, 0xA145, 0xAADC, 40 | 0xB505, 0xBFC9, 0xCB30, 0xD745, 0xE412, 0xF1A2, 41 | }; 42 | 43 | static uint16_t spu_ctrl_shadow; 44 | 45 | static uint32_t spu_new_key_off = 0x000000; 46 | static uint32_t spu_new_key_on = 0x000000; 47 | 48 | int orelei_note_to_pitch(int note, int cxoctave, unsigned int cxspeed) 49 | { 50 | int octave = (note < 0 ? -((12-note)/12) : note/12); 51 | int subnote = note - octave*12; 52 | unsigned int shift_amt = (15+cxoctave-octave); 53 | unsigned int pitch = (notetab[subnote]*cxspeed + (1<<(shift_amt-1)))>>shift_amt; 54 | return pitch; 55 | } 56 | 57 | void orelei_commit_key_changes(void) 58 | { 59 | TWICE(PSXREG_SPU_KOFF = spu_new_key_off); 60 | TWICE(PSXREG_SPU_KON = spu_new_key_on); 61 | spu_new_key_off = 0x000000; 62 | spu_new_key_on = 0x000000; 63 | } 64 | 65 | void orelei_play_note(int ch, int sram_addr, int adsr, int voll, int volr, int pitch) 66 | { 67 | int addr = (sram_addr >> 4) << 1; 68 | int volboth = (voll & 0xFFFF) | (volr<<16); 69 | TWICE(PSXREG_SPU_n_VOL(ch) = volboth); 70 | TWICE(PSXREG_SPU_n_PITCH(ch) = pitch); 71 | TWICE(PSXREG_SPU_n_ADDR(ch) = addr); 72 | TWICE(PSXREG_SPU_n_ADSR(ch) = adsr); 73 | spu_new_key_on |= (0x1<>= 4; 93 | len >>= 4; 94 | 95 | TWICE(PSXREG_SPU_MEM_CTRL = 0x0004); 96 | orelei_set_transfer_mode(SPU_TRANSFER_MODE_STOP); 97 | TWICE(PSXREG_SPU_MEM_ADDR = (sram_addr<<1)); 98 | orelei_set_transfer_mode(SPU_TRANSFER_MODE_DMA_WRITE); 99 | 100 | PSXREG_Dn_CHCR(4) = 0x00000201; 101 | PSXREG_Dn_MADR(4) = (uint32_t)data; 102 | PSXREG_Dn_BCR(4) = (len<<16)|0x0010; 103 | PSXREG_Dn_CHCR(4) = 0x01000201; 104 | PSXREG_DPCR |= (0x8<<(4<<2)); 105 | while((PSXREG_Dn_CHCR(4) & (0x01<<24)) != 0) { 106 | } 107 | PSXREG_DPCR &= ~(0x8<<(4<<2)); 108 | orelei_set_transfer_mode(SPU_TRANSFER_MODE_STOP); 109 | } 110 | 111 | void orelei_pack_spu(uint8_t *outbuf, const int16_t *inbuf, int16_t *pred1, int16_t *pred2, int blocks, int loopbeg, int loopend, bool fade_on_loop) 112 | { 113 | // TODO: non-format-1 blocks 114 | for(int ctr = 0; ctr < blocks; ctr++) { 115 | const int16_t *iptr = inbuf+28*ctr; 116 | uint8_t *optr = outbuf+16*ctr; 117 | 118 | static int32_t unfilter_buf[28]; 119 | 120 | // Perform delta encoding 121 | int32_t s1 = *pred1; 122 | int32_t s2 = *pred2; 123 | int32_t best_power = 0; 124 | for(int i = 0; i < 28; i++) { 125 | unfilter_buf[i] = iptr[i] - s1; 126 | s2 = s1; 127 | s1 = iptr[i]; 128 | int aval = unfilter_buf[i]; 129 | while((aval < ((-17<>1) || aval >= ((15<>1)) && best_power < 12) { 130 | best_power += 1; 131 | } 132 | } 133 | 134 | // Pack 135 | optr[0] = 0x10 | (12-best_power); 136 | optr[1] = 0x00; 137 | if(ctr == loopbeg) { 138 | optr[1] |= 0x04; 139 | } 140 | if(ctr == loopend) { 141 | optr[1] |= 0x01; 142 | if(!fade_on_loop) { 143 | optr[1] |= 0x02; 144 | } 145 | } 146 | 147 | int32_t outval; 148 | for(int i = 0; i < 28; i++) { 149 | int nyb = unfilter_buf[i]; 150 | if(best_power >= 1) { 151 | nyb += (1<<(best_power-1)); 152 | nyb >>= best_power; 153 | } 154 | if(nyb < -0x8) { nyb = -0x8; } 155 | if(nyb > 0x7) { nyb = 0x7; } 156 | if((i & 0x1) == 0) { optr[(i>>1)+2] = 0x00; } 157 | optr[(i>>1)+2] |= (nyb&0xF) << ((i&0x1)<<2); 158 | outval = *pred1 + (nyb< 0x7FFF) { outval = 0x7FFF; } 161 | *pred2 = *pred1; 162 | *pred1 = outval; 163 | } 164 | } 165 | } 166 | 167 | void orelei_open_cd_audio(int voll, int volr) 168 | { 169 | int volboth = (voll & 0xFFFF) | (volr<<16); 170 | TWICE(PSXREG_SPU_CD_VOL = volboth); 171 | spu_ctrl_shadow |= 1; 172 | TWICE(PSXREG_SPU_CTRL = spu_ctrl_shadow); 173 | while((PSXREG_SPU_STAT & 0x3F) != (spu_ctrl_shadow & 0x3F)) { 174 | } 175 | } 176 | 177 | void orelei_close_cd_audio() 178 | { 179 | TWICE(PSXREG_SPU_CD_VOL = 0); 180 | spu_ctrl_shadow &= ~1; 181 | TWICE(PSXREG_SPU_CTRL = spu_ctrl_shadow); 182 | while((PSXREG_SPU_STAT & 0x3F) != (spu_ctrl_shadow & 0x3F)) { 183 | } 184 | } 185 | 186 | void orelei_init_spu(void) 187 | { 188 | for(int i = 0; i < SPU_CHANNEL_COUNT; i++) { 189 | TWICE(PSXREG_SPU_n_VOL(i) = 0x00000000); 190 | TWICE(PSXREG_SPU_n_PITCH(i) = 0x0000); 191 | TWICE(PSXREG_SPU_n_ADDR(i) = 0x0000); 192 | TWICE(PSXREG_SPU_n_ADSR(i) = 0x00000000); 193 | } 194 | 195 | TWICE(PSXREG_SPU_MVOL = 0x3FFF3FFF); 196 | TWICE(PSXREG_SPU_EXT_VOL = 0x00000000); 197 | TWICE(PSXREG_SPU_KOFF = 0x00FFFFFF); 198 | TWICE(PSXREG_SPU_PMON = 0x00000000); 199 | TWICE(PSXREG_SPU_NON = 0x00000000); 200 | TWICE(PSXREG_SPU_EON = 0x00000000); 201 | 202 | TWICE(PSXREG_SPU_EFFECT_mBASE = 0xFFFE); 203 | TWICE(PSXREG_SPU_IRQ_ADDR = 0xFFFD); 204 | spu_ctrl_shadow = (0 205 | | SPU_CTRL_MASTER_ENABLE 206 | | SPU_CTRL_MASTER_UNMUTE 207 | | SPU_CTRL_REVERB_ENABLE 208 | | SPU_CTRL_TRANSFER_MODE(SPU_TRANSFER_MODE_STOP) 209 | ); 210 | TWICE(PSXREG_SPU_CTRL = spu_ctrl_shadow); 211 | TWICE(PSXREG_SPU_MEM_CTRL = 0x0004); 212 | TWICE(PSXREG_SPU_CD_VOL = 0x00000000); 213 | TWICE(PSXREG_SPU_EXT_VOL = 0x00000000); 214 | 215 | // Disable reverb fully 216 | TWICE(PSXREG_SPU_EFFECT_dAPF1 = 0x0000); 217 | TWICE(PSXREG_SPU_EFFECT_dAPF2 = 0x0000); 218 | TWICE(PSXREG_SPU_EFFECT_vIIR = 0x0000); 219 | TWICE(PSXREG_SPU_EFFECT_vCOMB1 = 0x0000); 220 | TWICE(PSXREG_SPU_EFFECT_vCOMB2 = 0x0000); 221 | TWICE(PSXREG_SPU_EFFECT_vCOMB3 = 0x0000); 222 | TWICE(PSXREG_SPU_EFFECT_vCOMB4 = 0x0000); 223 | TWICE(PSXREG_SPU_EFFECT_vWALL = 0x0000); 224 | TWICE(PSXREG_SPU_EFFECT_vAPF1 = 0x0000); 225 | TWICE(PSXREG_SPU_EFFECT_vAPF2 = 0x0000); 226 | TWICE(PSXREG_SPU_EFFECT_mSAME = 0x00010001); 227 | TWICE(PSXREG_SPU_EFFECT_mCOMB1 = 0x00010001); 228 | TWICE(PSXREG_SPU_EFFECT_mCOMB2 = 0x00010001); 229 | TWICE(PSXREG_SPU_EFFECT_dSAME = 0x00000000); 230 | TWICE(PSXREG_SPU_EFFECT_mDIFF = 0x00010001); 231 | TWICE(PSXREG_SPU_EFFECT_mCOMB3 = 0x00010001); 232 | TWICE(PSXREG_SPU_EFFECT_mCOMB4 = 0x00010001); 233 | TWICE(PSXREG_SPU_EFFECT_dDIFF = 0x00000000); 234 | TWICE(PSXREG_SPU_EFFECT_mAPF1 = 0x00010001); 235 | TWICE(PSXREG_SPU_EFFECT_mAPF2 = 0x00010001); 236 | TWICE(PSXREG_SPU_EFFECT_vIN = 0x00000000); 237 | } 238 | 239 | -------------------------------------------------------------------------------- /include/psxdefs/gpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | psxdefs: PS register/constants definitions 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | // Data 25 | #define GPU_VERTEX(x, y) (((x)&0xFFFF) | ((y)<<16)) 26 | #define GPU_TEXCOORD_UPPER(x, y, upper) (((x)&0xFF) | (((y)&0xFF)<<8) | ((upper)<<16)) 27 | #define GPU_TEXCOORD(x, y) GPU_TEXCOORD_UPPER((x), (y), 0) 28 | #define GPU_TEXCOORD_TEXPAGE(x, y, xbase, ybase, blend, bpp) \ 29 | GPU_TEXCOORD_UPPER((x), (y), ( \ 30 | ((xbase)&0xF) | (((ybase)&0x1)<<4) | blend | bpp)) 31 | #define GPU_TEXCOORD_CLUT(x, y, cx, cy) \ 32 | GPU_TEXCOORD_UPPER((x), (y), ((cx)&0x3F)|(((cy)&0x1FF)<<6)) 33 | #define GPU_BOX_OFFS(x, y) (((x)&0xFFFF) | ((y)<<16)) 34 | #define GPU_BOX_SIZE(x, y) (((x)&0xFFFF) | ((y)<<16)) 35 | #define GPU_RGB8(r, g, b) ((((b)&0xFF)<<16) | (((g)&0xFF)<<8) | ((r)&0xFF)) 36 | #define GPU_BGR8(b, g, r) ((((b)&0xFF)<<16) | (((g)&0xFF)<<8) | ((r)&0xFF)) 37 | 38 | // Misc ops 39 | #define GP0_NOP() (0x00000000) 40 | #define GP0_CLEAR_CACHE() (0x01000000) 41 | #define GP0_INTERRUPT_REQ() (0x1F000000) 42 | 43 | // Memory ops 44 | #define GP0_MEM_FILL(bgr) (0x02000000 | ((bgr)&0xFFFFFF)) 45 | #define GP0_MEM_COPY_WITHIN() (0x80000000) 46 | #define GP0_MEM_COPY_TO_VRAM() (0xA0000000) 47 | #define GP0_MEM_COPY_FROM_VRAM() (0xC0000000) 48 | 49 | // Polygons 50 | #define GP0_TRI_FLAT(bgr) (0x20000000 | ((bgr)&0xFFFFFF)) 51 | #define GP0_TRI_FLAT_BLEND(bgr) (0x22000000 | ((bgr)&0xFFFFFF)) 52 | #define GP0_TRI_FLAT_TEX(bgr) (0x24000000 | ((bgr)&0xFFFFFF)) 53 | #define GP0_TRI_TEX() (0x25000000) 54 | #define GP0_TRI_FLAT_TEX_BLEND(bgr) (0x26000000 | ((bgr)&0xFFFFFF)) 55 | #define GP0_TRI_TEX_BLEND() (0x27000000) 56 | #define GP0_QUAD_FLAT(bgr) (0x28000000 | ((bgr)&0xFFFFFF)) 57 | #define GP0_QUAD_FLAT_BLEND(bgr) (0x2A000000 | ((bgr)&0xFFFFFF)) 58 | #define GP0_QUAD_FLAT_TEX(bgr) (0x2C000000 | ((bgr)&0xFFFFFF)) 59 | #define GP0_QUAD_TEX() (0x2D000000) 60 | #define GP0_QUAD_FLAT_TEX_BLEND(bgr) (0x2E000000 | ((bgr)&0xFFFFFF)) 61 | #define GP0_QUAD_TEX_BLEND() (0x2F000000) 62 | #define GP0_TRI_GOURAUD(bgr) (0x30000000 | ((bgr)&0xFFFFFF)) 63 | #define GP0_TRI_GOURAUD_BLEND(bgr) (0x32000000 | ((bgr)&0xFFFFFF)) 64 | #define GP0_TRI_GOURAUD_TEX(bgr) (0x34000000 | ((bgr)&0xFFFFFF)) 65 | #define GP0_TRI_GOURAUD_TEX_BLEND(bgr) (0x36000000 | ((bgr)&0xFFFFFF)) 66 | #define GP0_QUAD_GOURAUD(bgr) (0x38000000 | ((bgr)&0xFFFFFF)) 67 | #define GP0_QUAD_GOURAUD_BLEND(bgr) (0x3A000000 | ((bgr)&0xFFFFFF)) 68 | #define GP0_QUAD_GOURAUD_TEX(bgr) (0x3C000000 | ((bgr)&0xFFFFFF)) 69 | #define GP0_QUAD_GOURAUD_TEX_BLEND(bgr) (0x3E000000 | ((bgr)&0xFFFFFF)) 70 | 71 | // Lines 72 | #define GPU_POLYLINE_END 0x55555555 73 | 74 | #define GP0_LINE_FLAT(bgr) (0x40000000 | ((bgr)&0xFFFFFF)) 75 | #define GP0_LINE_FLAT_BLEND(bgr) (0x42000000 | ((bgr)&0xFFFFFF)) 76 | #define GP0_POLYLINE_FLAT(bgr) (0x48000000 | ((bgr)&0xFFFFFF)) 77 | #define GP0_POLYLINE_FLAT_BLEND(bgr) (0x4A000000 | ((bgr)&0xFFFFFF)) 78 | #define GP0_LINE_GOURAUD(bgr) (0x50000000 | ((bgr)&0xFFFFFF)) 79 | #define GP0_LINE_GOURAUD_BLEND(bgr) (0x52000000 | ((bgr)&0xFFFFFF)) 80 | #define GP0_POLYLINE_GOURAUD(bgr) (0x58000000 | ((bgr)&0xFFFFFF)) 81 | #define GP0_POLYLINE_GOURAUD_BLEND(bgr) (0x5A000000 | ((bgr)&0xFFFFFF)) 82 | 83 | // Rects 84 | #define GP0_RECT_FLAT_FLEX(bgr) (0x60000000 | ((bgr)&0xFFFFFF)) 85 | #define GP0_RECT_FLAT_FLEX_BLEND(bgr) (0x62000000 | ((bgr)&0xFFFFFF)) 86 | #define GP0_RECT_FLAT_1(bgr) (0x68000000 | ((bgr)&0xFFFFFF)) 87 | #define GP0_RECT_FLAT_1_BLEND(bgr) (0x6A000000 | ((bgr)&0xFFFFFF)) 88 | #define GP0_RECT_FLAT_8(bgr) (0x70000000 | ((bgr)&0xFFFFFF)) 89 | #define GP0_RECT_FLAT_8_BLEND(bgr) (0x72000000 | ((bgr)&0xFFFFFF)) 90 | #define GP0_RECT_FLAT_16(bgr) (0x78000000 | ((bgr)&0xFFFFFF)) 91 | #define GP0_RECT_FLAT_16_BLEND(bgr) (0x7A000000 | ((bgr)&0xFFFFFF)) 92 | 93 | #define GP0_RECT_FLAT_TEX_FLEX(bgr) (0x64000000 | ((bgr)&0xFFFFFF)) 94 | #define GP0_RECT_TEX_FLEX(bgr) (0x65000000 | ((bgr)&0xFFFFFF)) 95 | #define GP0_RECT_FLAT_TEX_FLEX_BLEND(bgr) (0x66000000 | ((bgr)&0xFFFFFF)) 96 | #define GP0_RECT_TEX_FLEX_BLEND(bgr) (0x67000000 | ((bgr)&0xFFFFFF)) 97 | #define GP0_RECT_FLAT_TEX_1(bgr) (0x6C000000 | ((bgr)&0xFFFFFF)) 98 | #define GP0_RECT_TEX_1(bgr) (0x6D000000 | ((bgr)&0xFFFFFF)) 99 | #define GP0_RECT_FLAT_TEX_1_BLEND(bgr) (0x6E000000 | ((bgr)&0xFFFFFF)) 100 | #define GP0_RECT_TEX_1_BLEND(bgr) (0x6F000000 | ((bgr)&0xFFFFFF)) 101 | #define GP0_RECT_FLAT_TEX_8(bgr) (0x74000000 | ((bgr)&0xFFFFFF)) 102 | #define GP0_RECT_TEX_8(bgr) (0x75000000 | ((bgr)&0xFFFFFF)) 103 | #define GP0_RECT_FLAT_TEX_8_BLEND(bgr) (0x76000000 | ((bgr)&0xFFFFFF)) 104 | #define GP0_RECT_TEX_8_BLEND(bgr) (0x77000000 | ((bgr)&0xFFFFFF)) 105 | #define GP0_RECT_FLAT_TEX_16(bgr) (0x7C000000 | ((bgr)&0xFFFFFF)) 106 | #define GP0_RECT_TEX_16(bgr) (0x7D000000 | ((bgr)&0xFFFFFF)) 107 | #define GP0_RECT_FLAT_TEX_16_BLEND(bgr) (0x7E000000 | ((bgr)&0xFFFFFF)) 108 | #define GP0_RECT_TEX_16_BLEND(bgr) (0x7F000000 | ((bgr)&0xFFFFFF)) 109 | 110 | // Attributes 111 | #define GP0_ATTR_TEXPAGE(xbase, ybase, blend, bpp, flags) \ 112 | (0xE1000000 | ((xbase)&0xF) | (((ybase)&0x1)<<4) | blend | bpp | flags) 113 | 114 | #define GPU_TEXPAGE_BLEND_HALF 0x00 115 | #define GPU_TEXPAGE_BLEND_ADD 0x20 116 | #define GPU_TEXPAGE_BLEND_SUB 0x40 117 | #define GPU_TEXPAGE_BLEND_ADDQUARTER 0x60 118 | 119 | #define GPU_TEXPAGE_TEXBPP_4 0x000 120 | #define GPU_TEXPAGE_TEXBPP_8 0x080 121 | #define GPU_TEXPAGE_TEXBPP_15 0x100 122 | 123 | #define GPU_TEXPAGE_DITHER 0x0200 124 | #define GPU_TEXPAGE_DRAWTODISPLAY 0x0400 125 | #define GPU_TEXPAGE_TEXOFFDEBUG 0x0800 126 | #define GPU_TEXPAGE_XFLIP 0x1000 127 | #define GPU_TEXPAGE_YFLIP 0x2000 128 | 129 | #define GP0_ATTR_TEXWINDOW(xmask, ymask, xoffs, yoffs) \ 130 | (0xE2000000 \ 131 | | ((xmask)&0x1F) \ 132 | | (((ymask)&0x1F)<<5) \ 133 | | (((xoffs)&0x1F)<<10) \ 134 | | (((yoffs)&0x1F)<<15) \ 135 | ) 136 | 137 | #define GP0_ATTR_DRAW_RANGE_MIN(x, y) (0xE3000000 | ((x)&0x3FF) | (((y)&0x3FF)<<10)) 138 | #define GP0_ATTR_DRAW_RANGE_MAX(x, y) (0xE4000000 | ((x)&0x3FF) | (((y)&0x3FF)<<10)) 139 | #define GP0_ATTR_DRAW_OFFSET(x, y) (0xE5000000 | ((x)&0x7FF) | (((y)&0x7FF)<<11)) 140 | 141 | #define GP0_ATTR_MASKBIT(flags) (0xE6000000 | (flags)) 142 | #define GPU_MASKBIT_ALWAYS1 0x1 143 | #define GPU_MASKBIT_DRAWIF0 0x2 144 | 145 | // GP1 ops 146 | #define GP1_RESET_GPU() 0x00000000 147 | #define GP1_RESET_COMMAND_BUFFER() 0x01000000 148 | #define GP1_INTERRUPT_ACK() 0x02000000 149 | #define GP1_DISPLAY_ENABLE() 0x03000000 150 | #define GP1_DISPLAY_DISABLE() 0x03000001 151 | 152 | #define GP1_DMA_DIRECTION(dmadir) (0x04000000 | ((dmadir)&3)) 153 | #define GPU_DMADIR_OFF 0x0 154 | #define GPU_DMADIR_FIFO 0x1 155 | #define GPU_DMADIR_TO_GPU 0x2 156 | #define GPU_DMADIR_TO_CPU 0x3 157 | 158 | #define GP1_DISPLAY_START(x, y) \ 159 | (0x05000000 | ((x)&0x3FF) | (((y)&0x1FF)<<10)) 160 | #define GP1_DISPLAY_RANGE_X(xbeg, xend) \ 161 | (0x06000000 | ((xbeg)&0xFFF) | (((xend)&0xFFF)<<12)) 162 | #define GP1_DISPLAY_RANGE_Y(ybeg, yend) \ 163 | (0x07000000 | ((ybeg)&0x3FF) | (((yend)&0x3FF)<<10)) 164 | 165 | // GP1: Display modes 166 | #define GP1_DISPLAY_MODE(w, h, bpp, std) \ 167 | (0x08000000 | (w) | (h) | (bpp) | (std)) 168 | 169 | #define GPU_DISPMODE_W_256 0x00 170 | #define GPU_DISPMODE_W_320 0x01 171 | #define GPU_DISPMODE_W_512 0x02 172 | #define GPU_DISPMODE_W_640 0x03 173 | #define GPU_DISPMODE_W_368 0x40 174 | 175 | #define GPU_DISPMODE_H_240P 0x00 176 | #define GPU_DISPMODE_H_240I 0x20 177 | #define GPU_DISPMODE_H_480I 0x24 178 | 179 | #define GPU_DISPMODE_BPP_15 0x00 180 | #define GPU_DISPMODE_BPP_24 0x10 181 | 182 | #define GPU_DISPMODE_STD_NTSC 0x00 183 | #define GPU_DISPMODE_STD_PAL 0x08 184 | 185 | 186 | //define GP0_INTERRUPT_REQ() 0x 187 | 188 | -------------------------------------------------------------------------------- /src/orelei/midi.c: -------------------------------------------------------------------------------- 1 | /* 2 | orelei: PS SPU sound driver 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | */ 22 | 23 | // MIDI component 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | #define ASSERT(x) if (!(x)) { for(;;) {} } 36 | 37 | static struct midi_master midi_masters[ORELEI_MIDI_MASTER_COUNT]; 38 | static struct midi_slave midi_slaves[SPU_CHANNEL_COUNT]; 39 | static struct midi_track midi_tracks[ORELEI_MIDI_MAX_TRACKS]; 40 | static uint32_t midi_slaves_on = 0x000000; 41 | static int midi_track_count = 0; 42 | static int midi_divisions = 0; 43 | static uint32_t midi_touch_counter = 0; 44 | static uint32_t midi_tempo = 500000; // 120 BPM 45 | 46 | static uint32_t midi_read_u8(uint8_t const** ptr) 47 | { 48 | uint32_t v = 0; 49 | 50 | v |= ((uint32_t)(*((*ptr)++))); 51 | 52 | return v; 53 | } 54 | 55 | static uint32_t midi_read_u16be(uint8_t const** ptr) 56 | { 57 | uint32_t v = 0; 58 | 59 | v |= ((uint32_t)(*((*ptr)++)))<<8; 60 | v |= ((uint32_t)(*((*ptr)++))); 61 | 62 | return v; 63 | } 64 | 65 | static uint32_t midi_read_u32be(uint8_t const** ptr) 66 | { 67 | uint32_t v = 0; 68 | 69 | v |= ((uint32_t)(*((*ptr)++)))<<24; 70 | v |= ((uint32_t)(*((*ptr)++)))<<16; 71 | v |= ((uint32_t)(*((*ptr)++)))<<8; 72 | v |= ((uint32_t)(*((*ptr)++))); 73 | 74 | return v; 75 | } 76 | 77 | static uint32_t midi_read_delta(uint8_t const** ptr) 78 | { 79 | uint32_t v = 0; 80 | int ctr = 0; 81 | 82 | for(;;) { 83 | uint32_t iv = *((*ptr)++); 84 | v <<= 7; 85 | v |= (iv&0x7F); 86 | if((iv & 0x80) == 0) { 87 | break; 88 | } 89 | ctr++; 90 | ASSERT(ctr < 4); 91 | } 92 | 93 | return v; 94 | } 95 | 96 | void orelei_midi_reset(void) 97 | { 98 | midi_touch_counter = 0; 99 | for(int i = 0; i < SPU_CHANNEL_COUNT; i++) { 100 | struct midi_slave *S = &midi_slaves[i]; 101 | S->last_touched = 0; 102 | S->ch = 0xFF; 103 | S->note = 0; 104 | orelei_stop_note(i); 105 | } 106 | 107 | for(int i = 0; i < ORELEI_MIDI_MASTER_COUNT; i++) { 108 | struct midi_master *M = &midi_masters[i]; 109 | M->prg = 0; 110 | M->rpn_lo = 0x7F; 111 | M->rpn_hi = 0x7F; 112 | M->real_wheel = 0x2000; 113 | M->wheel = 0x0000; 114 | M->wheel_depth = 0x0200; 115 | } 116 | 117 | for(int i = 0; i < midi_track_count; i++) { 118 | struct midi_track *T = &midi_tracks[i]; 119 | T->mptr = T->mptr_beg; 120 | T->ticks_waiting = midi_read_delta(&T->mptr); 121 | T->last_v0 = 0x00; 122 | } 123 | } 124 | 125 | static void midi_note_on(int ch, int note, int vel, void (*f_play_note)(int hwch, int ch, int prg, int note, int vel, int wheel)) 126 | { 127 | int slave_idx = -1; 128 | 129 | // Do we already have this as a slave? 130 | if(slave_idx == -1) { 131 | for(int i = 0; i < SPU_CHANNEL_COUNT; i++) { 132 | struct midi_slave *S = &midi_slaves[i]; 133 | if(S->ch == ch && S->note == note) { 134 | orelei_stop_note(i); 135 | S->ch = 0xFF; 136 | slave_idx = i; 137 | break; 138 | } 139 | } 140 | } 141 | 142 | // Find the longest-untouched unused slave 143 | if(slave_idx == -1) { 144 | for(int i = 0; i < SPU_CHANNEL_COUNT; i++) { 145 | struct midi_slave *S = &midi_slaves[i]; 146 | if(S->ch == 0xFF) { 147 | if(slave_idx == -1 || S->last_touched < midi_slaves[slave_idx].last_touched) { 148 | slave_idx = i; 149 | } 150 | } 151 | } 152 | } 153 | 154 | // Find the longest-untouched slave 155 | if(slave_idx == -1) { 156 | for(int i = 0; i < SPU_CHANNEL_COUNT; i++) { 157 | struct midi_slave *S = &midi_slaves[i]; 158 | if(slave_idx == -1 || S->last_touched < midi_slaves[slave_idx].last_touched) { 159 | slave_idx = i; 160 | } 161 | } 162 | } 163 | 164 | ASSERT(slave_idx >= 0 && slave_idx < SPU_CHANNEL_COUNT); 165 | 166 | struct midi_slave *S = &midi_slaves[slave_idx]; 167 | S->ch = ch; 168 | S->note = note; 169 | S->last_touched = ++midi_touch_counter; 170 | 171 | // Play note 172 | struct midi_master *M = &midi_masters[ch]; 173 | f_play_note(slave_idx, ch, M->prg, note, vel, M->wheel); 174 | } 175 | 176 | static void midi_note_off(int ch, int note, int vel) 177 | { 178 | for(int i = 0; i < SPU_CHANNEL_COUNT; i++) { 179 | struct midi_slave *S = &midi_slaves[i]; 180 | if(S->ch == ch && S->note == note) { 181 | orelei_stop_note(i); 182 | S->ch = 0xFF; 183 | break; 184 | } 185 | } 186 | } 187 | 188 | static int32_t midi_tick_accum = 0; 189 | void orelei_midi_update(int32_t time_advanced_us, void (*f_play_note)(int hwch, int ch, int prg, int note, int vel, int wheel)) 190 | { 191 | /* 192 | Divisions = ticks per 1/4-note 193 | Tempo = whole-notes per second 194 | */ 195 | int64_t tickbase = midi_divisions; 196 | tickbase *= time_advanced_us; 197 | //tickbase += midi_tempo/2; 198 | midi_tick_accum += (tickbase % midi_tempo); 199 | tickbase /= midi_tempo; 200 | tickbase += midi_tick_accum / midi_tempo; 201 | midi_tick_accum %= midi_tempo; 202 | int32_t ticks_advanced = tickbase; 203 | 204 | for(int i = 0; i < midi_track_count; i++) { 205 | struct midi_track *T = &midi_tracks[i]; 206 | uint8_t const** mpp = &T->mptr; 207 | 208 | T->ticks_waiting -= ticks_advanced; 209 | while(T->ticks_waiting <= 0) { 210 | if(T->mptr >= T->mptr_end || T->mptr < T->mptr_beg) { 211 | break; 212 | } 213 | uint8_t v0 = midi_read_u8(mpp); 214 | uint8_t v1; 215 | if((v0 & 0x80) != 0) { 216 | v1 = midi_read_u8(mpp); 217 | T->last_v0 = v0; 218 | } else { 219 | v1 = v0; 220 | v0 = T->last_v0; 221 | } 222 | //*(volatile uint32_t *)0x801FFFF0 = v0; 223 | 224 | struct midi_master *M = &midi_masters[v0&0xF]; 225 | 226 | uint8_t v2, v3; 227 | switch(v0>>4) { 228 | case 0x8: // Note off 229 | v2 = midi_read_u8(mpp); 230 | midi_note_off(v0&0xF, v1, v2); 231 | break; 232 | case 0x9: // Note on 233 | v2 = midi_read_u8(mpp); 234 | if(v2 == 0x00) { 235 | // DON'T ASK ME WHY THIS IS A THING. 236 | // I DIDN'T MAKE THIS STUPID THING. 237 | midi_note_off(v0&0xF, v1, 0x00); 238 | } else { 239 | midi_note_on(v0&0xF, v1, v2, f_play_note); 240 | } 241 | break; 242 | case 0xA: // Key aftertouch 243 | v2 = midi_read_u8(mpp); 244 | break; 245 | case 0xB: // Controller 246 | v2 = midi_read_u8(mpp); 247 | switch(v1) { 248 | case 0x06: 249 | if(M->rpn_lo == 0x00 && M->rpn_hi == 0x00) { 250 | // Pitch wheel depth 251 | M->wheel_depth = ((int)v2)<<7; 252 | } 253 | break; 254 | case 0x64: 255 | M->rpn_lo = v2; 256 | break; 257 | case 0x65: 258 | M->rpn_hi = v2; 259 | break; 260 | } 261 | break; 262 | case 0xC: // Program change 263 | M->prg = v1 & 0x7F; 264 | break; 265 | case 0xD: // Instrument aftertouch 266 | break; 267 | case 0xE: // Pitch bend 268 | v2 = midi_read_u8(mpp); 269 | // ...is LSB first. Good job, MIDI. Slow clap. 270 | M->real_wheel = (((int32_t)v2)<<7)|((int32_t)v1); 271 | M->wheel = ((M->real_wheel-0x2000)*M->wheel_depth + (1<<12))>>13; 272 | for(int i = 0; i < 24; i++) { 273 | struct midi_slave *S = &midi_slaves[i]; 274 | if(S->ch == (v0&0xF)) { 275 | f_play_note(i, -1, M->prg, 0, 0, M->wheel); 276 | } 277 | } 278 | break; 279 | case 0xF: switch(v0) { 280 | case 0xFF: 281 | v2 = midi_read_u8(mpp); 282 | if(v1 == 0x51) { 283 | ASSERT(v2 == 3); 284 | midi_tempo = 0; 285 | midi_tempo |= ((uint32_t)((*mpp)[0]))<<16; 286 | midi_tempo |= ((uint32_t)((*mpp)[0]))<<8; 287 | midi_tempo |= ((uint32_t)((*mpp)[0])); 288 | } 289 | (*mpp) += v2; // SKIP 290 | break; 291 | default: 292 | //*(volatile uint32_t *)0x801FFFF0 = 'K'; 293 | //*(volatile uint32_t *)0x801FFFF4 = v0; 294 | //ASSERT(false); 295 | (*mpp) += v1; // SKIP 296 | break; 297 | } break; 298 | } 299 | 300 | // Read next delta 301 | int delta = midi_read_delta(mpp); 302 | ASSERT(delta >= 0); 303 | T->ticks_waiting += delta; 304 | } 305 | } 306 | 307 | orelei_commit_key_changes(); 308 | } 309 | 310 | void orelei_midi_load_from_data(const uint8_t *midi_data) 311 | { 312 | // Read header 313 | ASSERT(!memcmp(midi_data, "MThd", 4)); 314 | midi_data += 4; 315 | uint32_t mthd_len = midi_read_u32be(&midi_data); 316 | ASSERT(mthd_len == 6); 317 | uint32_t midi_format = midi_read_u16be(&midi_data); 318 | ASSERT(midi_format == 0 || midi_format == 1 || midi_format == 2); 319 | midi_track_count = midi_read_u16be(&midi_data); 320 | ASSERT(midi_track_count >= 0 && midi_track_count <= ORELEI_MIDI_MAX_TRACKS); 321 | midi_divisions = midi_read_u16be(&midi_data); 322 | 323 | // Get each track pointer 324 | for(int i = 0; i < midi_track_count; i++) { 325 | struct midi_track *T = &midi_tracks[i]; 326 | ASSERT(!memcmp(midi_data, "MTrk", 4)); 327 | midi_data += 4; 328 | uint32_t mtrk_len = midi_read_u32be(&midi_data); 329 | //ASSERT(mtrk_len < 0x20000); 330 | T->mptr_beg = midi_data; 331 | midi_data += mtrk_len; 332 | T->mptr_end = midi_data; 333 | T->mptr = T->mptr_beg; 334 | } 335 | 336 | orelei_midi_reset(); 337 | } 338 | -------------------------------------------------------------------------------- /src/sawpads/sawpads.c: -------------------------------------------------------------------------------- 1 | /* 2 | sawpads: Actually Tested* Joypad Code 3 | 4 | Copyright (c) 2017, 2019 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | volatile sawpads_controller_t sawpads_controller[2]; 36 | volatile uint8_t sawpads_has_ack = 0; 37 | volatile uint8_t sawpads_error = 0; 38 | volatile uint16_t sawpads_buffer[16]; 39 | 40 | static void sawpads_stop_read(void) 41 | { 42 | PSXREG_JOY_CTRL = 0x0010; 43 | 44 | // Also kill time 45 | for(uint32_t i = 0; i < 1000; i++) { 46 | asm volatile (""); 47 | } 48 | sawpads_has_ack = 0; 49 | } 50 | 51 | static uint8_t sawpads_recv_solo(void) 52 | { 53 | while((PSXREG_JOY_STAT & (1<<1)) == 0) {} 54 | return PSXREG_JOY_DATA; 55 | } 56 | 57 | #define SAWPADS_WAIT_RESPONSE 1 58 | #define SAWPADS_WAIT_ACK 2 59 | #define SAWPADS_WAIT_LONG 4 60 | 61 | static uint8_t sawpads_send(uint8_t data, uint32_t flags) 62 | { 63 | // PS1 expects about 3300 cycles ACK wait delay for controllers 64 | uint32_t ack_length = (flags & SAWPADS_WAIT_LONG) ? (0x44 * 400) : 750; 65 | sawpads_has_ack = 0; 66 | sawpads_error = 0; 67 | PSXREG_JOY_DATA = data; 68 | 69 | if (flags & SAWPADS_WAIT_ACK) { 70 | for (uint32_t i = 0; i < ack_length; i++) { 71 | asm volatile (""); 72 | if(sawpads_has_ack != 0) { 73 | sawpads_has_ack--; 74 | return PSXREG_JOY_DATA; 75 | } 76 | } 77 | 78 | sawpads_error = 1; 79 | } else if (flags & SAWPADS_WAIT_RESPONSE) { 80 | for (uint32_t i = 0; i < ack_length; i++) { 81 | asm volatile (""); 82 | if((PSXREG_JOY_STAT & (1<<1)) != 0) { 83 | // But wait anyway 84 | for(uint32_t i = 0; i < 0x44*10; i++) { asm volatile (""); } 85 | return PSXREG_JOY_DATA; 86 | } 87 | } 88 | 89 | sawpads_error = 1; 90 | } 91 | 92 | return 0xFF; // no data 93 | } 94 | 95 | static void sawpads_start_read(uint8_t port) 96 | { 97 | PSXREG_JOY_CTRL = 0x1013 | ((port & 1) << 13); 98 | 99 | // Kill time (more than 2000 cycles is overkill according to nocash) 100 | for(uint32_t i = 0; i < 1500; i++) { 101 | asm volatile (""); 102 | } 103 | } 104 | 105 | static uint8_t sawpads_read_words(uint8_t cmd, uint8_t port, uint8_t* response, int response_len) 106 | { 107 | uint8_t sawpads_hwords = 0; 108 | volatile sawpads_controller_t *c = &sawpads_controller[port]; 109 | 110 | sawpads_start_read(port); 111 | sawpads_send(0x01, SAWPADS_WAIT_ACK); 112 | if (sawpads_error) { 113 | // never received ACK 114 | sawpads_stop_read(); 115 | return 0; 116 | } 117 | c->id = sawpads_send(cmd, SAWPADS_WAIT_ACK); 118 | sawpads_hwords = (uint8_t) c->id; 119 | sawpads_hwords -= 1; 120 | sawpads_hwords &= 0x0F; 121 | sawpads_hwords += 1; 122 | c->hid = sawpads_send(0x00, SAWPADS_WAIT_ACK); 123 | if (c->hid == 0xFF) { 124 | // no controller connected 125 | sawpads_stop_read(); 126 | return 0; 127 | } 128 | int j = 0; 129 | for (int i = 0; i < sawpads_hwords; i++) { 130 | sawpads_buffer[i] = sawpads_send(j < response_len ? response[j++] : 0xFF, SAWPADS_WAIT_ACK); 131 | sawpads_buffer[i] |= sawpads_send(j < response_len ? response[j++] : 0xFF, 132 | (i != (sawpads_hwords - 1)) ? SAWPADS_WAIT_ACK : SAWPADS_WAIT_RESPONSE) << 8; 133 | } 134 | sawpads_stop_read(); 135 | 136 | return sawpads_hwords; 137 | } 138 | 139 | int32_t sawpads_read_card_sector(uint8_t port, uint16_t address, uint8_t *buffer) 140 | { 141 | uint8_t flag; 142 | uint16_t id; 143 | int8_t attempts = 3; 144 | uint8_t checksum = 0; 145 | 146 | while ((--attempts) >= 0) { 147 | uint16_t conf_addr; 148 | 149 | sawpads_start_read(port); 150 | sawpads_send(0x81, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 151 | flag = sawpads_send(0x52, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 152 | id = sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK) << 8; 153 | id |= sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 154 | 155 | // TODO: is this the right way to sense? guessing 156 | if (id != 0x5A5D) { 157 | sawpads_stop_read(); 158 | return 0; 159 | } 160 | 161 | sawpads_send(address >> 8, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 162 | sawpads_send(address & 0xFF, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 163 | 164 | sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); // ACK 165 | sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); // ACK 166 | 167 | conf_addr = sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK) << 8; 168 | conf_addr |= sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 169 | if ((conf_addr & 0x3FF) != (address & 0x3FF)) { 170 | // try again, loop back 171 | sawpads_stop_read(); 172 | continue; 173 | } 174 | 175 | checksum = (address >> 8) ^ (address & 0xFF); 176 | 177 | for (int i = 0; i < 128; i++) { 178 | uint8_t val = sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 179 | buffer[i] = val; 180 | checksum ^= val; 181 | } 182 | 183 | if (sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK) != checksum) { 184 | sawpads_stop_read(); 185 | continue; 186 | } 187 | 188 | uint8_t result = sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_RESPONSE); 189 | sawpads_stop_read(); 190 | switch (result) { 191 | case 0x47: 192 | return 128; 193 | default: 194 | continue; 195 | } 196 | } 197 | 198 | return 0; 199 | } 200 | 201 | int32_t sawpads_write_card_sector(uint8_t port, uint16_t address, uint8_t *buffer) 202 | { 203 | uint8_t flag; 204 | uint16_t id; 205 | int8_t attempts = 3; 206 | uint8_t checksum = 0; 207 | 208 | while ((--attempts) >= 0) { 209 | uint16_t conf_addr; 210 | 211 | sawpads_start_read(port); 212 | sawpads_send(0x81, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 213 | if (sawpads_error) { 214 | // never received ACK 215 | sawpads_stop_read(); 216 | return 0; 217 | } 218 | 219 | flag = sawpads_send(0x57, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 220 | id = sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK) << 8; 221 | id |= sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 222 | 223 | // TODO: is this the right way to sense? guessing 224 | if (id != 0x5A5D) { 225 | sawpads_stop_read(); 226 | continue; 227 | } 228 | 229 | sawpads_send(address >> 8, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 230 | sawpads_send(address & 0xFF, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 231 | 232 | checksum = (address >> 8) ^ (address & 0xFF); 233 | for (int i = 0; i < 128; i++) { 234 | uint8_t val = buffer[i]; 235 | checksum ^= val; 236 | sawpads_send(val, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 237 | } 238 | 239 | sawpads_send(checksum, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); 240 | 241 | sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); // ACK 242 | sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_ACK); // ACK 243 | 244 | uint8_t result = sawpads_send(0x00, SAWPADS_WAIT_LONG | SAWPADS_WAIT_RESPONSE); 245 | sawpads_stop_read(); 246 | switch (result) { 247 | case 0x47: 248 | return 128; 249 | case 0x4E: 250 | continue; 251 | case 0xFF: 252 | default: 253 | continue; 254 | } 255 | } 256 | 257 | return 0; 258 | } 259 | 260 | void sawpads_do_read_controller(uint8_t port) 261 | { 262 | volatile sawpads_controller_t *c = &sawpads_controller[port]; 263 | uint8_t rumble_buf[2]; 264 | rumble_buf[0] = c->rumble[0]; 265 | rumble_buf[1] = c->rumble[1]; 266 | uint8_t sawpads_words = sawpads_read_words(0x42, port, rumble_buf, 2); 267 | c->analogs = 0; 268 | 269 | if(sawpads_words >= 1) { 270 | c->buttons = sawpads_buffer[0]; 271 | if (sawpads_words >= 2) { 272 | c->analogs = 2; 273 | c->axes[0] = sawpads_buffer[1] & 0xFF; 274 | c->axes[1] = sawpads_buffer[1] >> 8; 275 | if (sawpads_words >= 3) { 276 | c->analogs = 4; 277 | c->axes[2] = sawpads_buffer[2] & 0xFF; 278 | c->axes[3] = sawpads_buffer[2] >> 8; 279 | } 280 | } 281 | 282 | if (!(c->id == 0x12 && c->hid == 0x5A) /* mouse */) { 283 | for (int i = 0; i < c->analogs; i++) { 284 | if (c->axes[i] > 0x60 && c->axes[i] < 0xA0) 285 | c->axes[i] = 0x00; 286 | else 287 | c->axes[i] ^= 0x80; 288 | } 289 | } 290 | } else { 291 | c->buttons = 0xFFFF; 292 | } 293 | 294 | for (int i = c->analogs; i < 4; i++) { 295 | c->axes[i] = 0x00; 296 | } 297 | } 298 | 299 | void sawpads_do_read(void) 300 | { 301 | sawpads_do_read_controller(0); 302 | sawpads_do_read_controller(1); 303 | } 304 | 305 | void sawpads_unlock_dualshock(uint8_t port) 306 | { 307 | static uint8_t response[6]; 308 | 309 | // Kick joypad into config mode 310 | response[0] = 0x01; 311 | response[1] = 0x00; 312 | sawpads_read_words(0x43, port, response, 2); 313 | 314 | // Turn on analog mode 315 | response[0] = 0x01; 316 | response[1] = 0x03; 317 | sawpads_read_words(0x44, port, response, 2); 318 | 319 | // Enable rumble 320 | response[0] = 0x00; 321 | response[1] = 0x01; 322 | response[2] = 0xFF; 323 | response[3] = 0xFF; 324 | response[4] = 0xFF; 325 | response[5] = 0xFF; 326 | sawpads_read_words(0x4D, port, response, 6); 327 | 328 | // Enable pressure 329 | response[0] = 0xFF; 330 | response[1] = 0xFF; 331 | response[2] = 0x03; 332 | response[3] = 0x00; 333 | response[4] = 0x00; 334 | response[5] = 0x00; 335 | sawpads_read_words(0x4F, port, response, 6); 336 | 337 | // Revert to normal mode 338 | response[0] = 0x00; 339 | response[1] = 0x5A; 340 | response[2] = 0x5A; 341 | response[3] = 0x5A; 342 | response[4] = 0x5A; 343 | response[5] = 0x5A; 344 | sawpads_read_words(0x43, port, response, 6); 345 | } 346 | 347 | void sawpads_isr_joy(void) 348 | { 349 | while((PSXREG_JOY_STAT & (1<<7)) != 0) {} 350 | PSXREG_JOY_CTRL |= 0x0010; // ACK 351 | sawpads_has_ack++; 352 | } 353 | 354 | -------------------------------------------------------------------------------- /toolsrc/elf2psx/elf2psx.c: -------------------------------------------------------------------------------- 1 | /* 2 | elf2psx: Converts ELFs to PlayStation executables 3 | 4 | I normally embed this into an assembly file and do some linker wizardry, 5 | but I've decided to write a proper tool for it this time. 6 | 7 | DO NOT ATTEMPT TO RUN THIS ON A NON-LITTLE-ENDIAN CPU. 8 | Big-endian is fairly rare these days, and it also sucks. 9 | 10 | Copyright (c) 2017, 2018, 2019 Ben "GreaseMonkey" Russell 11 | Copyright (c) 2019 Adrian "asie" Siekierka 12 | 13 | This software is provided 'as-is', without any express or implied 14 | warranty. In no event will the authors be held liable for any damages 15 | arising from the use of this software. 16 | 17 | Permission is granted to anyone to use this software for any purpose, 18 | including commercial applications, and to alter it and redistribute it 19 | freely, subject to the following restrictions: 20 | 21 | 1. The origin of this software must not be misrepresented; you must not 22 | claim that you wrote the original software. If you use this software 23 | in a product, an acknowledgment in the product documentation would be 24 | appreciated but is not required. 25 | 2. Altered source versions must be plainly marked as such, and must not be 26 | misrepresented as being the original software. 27 | 3. This notice may not be removed or altered from any source distribution. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | typedef enum region { 39 | REGION_NTSC_JAPAN, 40 | REGION_NTSC_NORTH_AMERICA, 41 | REGION_PAL 42 | } region_t; 43 | 44 | typedef struct elf_header { 45 | uint8_t magic[4]; 46 | uint8_t bitness; 47 | uint8_t endianness; 48 | uint8_t elf_version_1; 49 | uint8_t os_abi; 50 | uint8_t _pad1[8]; 51 | 52 | uint16_t elf_type; 53 | uint16_t cpu_type; 54 | uint32_t elf_version_2; 55 | uint32_t entry_point; 56 | uint32_t phdr_offs; 57 | uint32_t shdr_offs; 58 | uint32_t flags; 59 | uint16_t ehdr_size; 60 | uint16_t phdr_ent_size; 61 | uint16_t phdr_ent_count; 62 | uint16_t shdr_ent_size; 63 | uint16_t shdr_ent_count; 64 | uint16_t strtab_idx; 65 | 66 | } __attribute__((__packed__)) elf_header_t; 67 | 68 | typedef struct elf_program_header { 69 | uint32_t type; 70 | uint32_t offset; 71 | uint32_t vaddr; 72 | uint32_t paddr; 73 | uint32_t filesz; 74 | uint32_t memsz; 75 | uint32_t flags; 76 | uint32_t align; 77 | } __attribute__((__packed__)) elf_program_header_t; 78 | #define PT_LOAD 0x00000001 79 | 80 | typedef struct psx_header { 81 | uint8_t magic[8]; 82 | uint8_t _pad1[8]; 83 | uint32_t pc; 84 | uint32_t gp; 85 | uint32_t ftext; 86 | uint32_t filesz; 87 | uint32_t unk1[2]; 88 | uint32_t bss_offs; 89 | uint32_t bss_len; 90 | uint32_t sp_base; 91 | uint32_t sp_offs; 92 | uint32_t rsv1[5]; 93 | uint8_t ascii_marker[0x800-0x4C]; 94 | } __attribute__((__packed__)) psx_header_t; 95 | 96 | void show_usage(const char *arg0) 97 | { 98 | printf( 99 | "elf2psx: Converts ELFs to PlayStation executables\n" 100 | "Copyright (C) GreaseMonkey, 2017, licensed under Creative Commons Zero:\n" 101 | "https://creativecommons.org/publicdomain/zero/1.0/\n" 102 | "\n" 103 | "usage:\n" 104 | "\t%s -pnj infile.elf outfile.exe\n" 105 | "\n" 106 | "use one of the -p, -n, or -j flags to denote the intended region:\n" 107 | "\t-j: NTSC Japan\n" 108 | "\t-n: NTSC North America\n" 109 | "\t-p: PAL\n" 110 | "\n" 111 | , arg0); 112 | } 113 | 114 | int main(int argc, char *argv[]) 115 | { 116 | int i; 117 | uint32_t addr; 118 | 119 | const char *region_flag_str; 120 | const char *fname_elf; 121 | const char *fname_psx; 122 | region_t region_flag; 123 | elf_header_t ehdr; 124 | elf_program_header_t *phdrs; 125 | elf_program_header_t *phdr; 126 | psx_header_t psxh; 127 | uint8_t blank_page[0x800]; 128 | uint8_t *tmp_copy_buf; 129 | FILE *elf; 130 | FILE *psx; 131 | 132 | uint32_t target_ftext; 133 | uint32_t target_edata; 134 | uint32_t target_aedata; 135 | 136 | // Read arguments 137 | if (argc <= 3) { 138 | fprintf(stderr, "not enough arguments\n"); 139 | show_usage(argv[0]); 140 | return 1; 141 | } 142 | 143 | region_flag_str = argv[1]; 144 | fname_elf = argv[2]; 145 | fname_psx = argv[3]; 146 | 147 | // Read region flag 148 | if(!strcmp(region_flag_str, "-j")) { 149 | region_flag = REGION_NTSC_JAPAN; 150 | } else if(!strcmp(region_flag_str, "-n")) { 151 | region_flag = REGION_NTSC_NORTH_AMERICA; 152 | } else if(!strcmp(region_flag_str, "-p")) { 153 | region_flag = REGION_PAL; 154 | } else { 155 | fprintf(stderr, "invalid region flag\n"); 156 | show_usage(argv[0]); 157 | return 1; 158 | } 159 | 160 | // Open source ELF 161 | elf = fopen(fname_elf, "rb"); 162 | if (elf == NULL) { 163 | perror("fopen(elf)"); 164 | return 1; 165 | } 166 | 167 | // Read header 168 | if(fread(&ehdr, sizeof(ehdr), 1, elf) != 1) { 169 | perror("fread(elf)"); 170 | goto fail_close_elf; 171 | } 172 | 173 | // Check magic 174 | if(memcmp(ehdr.magic, "\x7F""ELF", 4)) { 175 | fprintf(stderr, "invalid ELF magic\n"); 176 | goto fail_close_elf; 177 | } 178 | 179 | // Check if we can actually read the ELF contents 180 | if(ehdr.bitness != 1) { 181 | fprintf(stderr, "not a 32-bit ELF\n"); 182 | goto fail_close_elf; 183 | } 184 | if(ehdr.endianness != 1) { 185 | fprintf(stderr, "not a little-endian ELF\n"); 186 | goto fail_close_elf; 187 | } 188 | if(ehdr.phdr_ent_size != 0x0020) { 189 | fprintf(stderr, "unusual PHdr entry size\n"); 190 | goto fail_close_elf; 191 | } 192 | 193 | // Check if what we're about to do makes sense 194 | if(ehdr.cpu_type != 8) { 195 | fprintf(stderr, "not a MIPS ELF\n"); 196 | goto fail_close_elf; 197 | } 198 | if(ehdr.elf_type != 2) { 199 | fprintf(stderr, "not an executable ELF\n"); 200 | goto fail_close_elf; 201 | } 202 | 203 | // Spew out some info 204 | // (we'll assume you're trying 205 | printf("ELF version: %02X\n", ehdr.elf_version_1); 206 | printf("ELF version: %08X\n", ehdr.elf_version_2); 207 | printf("OS ABI: %02X\n", ehdr.os_abi); 208 | printf("ELF type: %04X\n", ehdr.elf_type); 209 | printf("CPU type: %04X\n", ehdr.cpu_type); 210 | printf("Entry point: %08X\n", ehdr.entry_point); 211 | printf("PHdr offset: %08X\n", ehdr.phdr_offs); 212 | printf("PHdr esize: %04X\n", ehdr.phdr_ent_size); 213 | printf("PHdr ecount: %04X\n", ehdr.phdr_ent_count); 214 | 215 | // Load program headers 216 | phdrs = malloc(sizeof(*phdr)*ehdr.phdr_ent_count); 217 | if(fseek(elf, ehdr.phdr_offs, SEEK_SET) != 0) { 218 | perror("fseek(elf phdr)"); 219 | goto fail_free_phdr_elf; 220 | } 221 | if(fread(phdrs, sizeof(*phdr)*ehdr.phdr_ent_count, 1, elf) != 1) { 222 | perror("fread(elf phdrs)"); 223 | goto fail_free_phdr_elf; 224 | } 225 | 226 | // Find start and end of program 227 | target_ftext = 0xFFFFFFFF; 228 | target_edata = 0x00000000; 229 | target_aedata = 0x00000000; 230 | for(i = 0; i < ehdr.phdr_ent_count; i++) { 231 | phdr = &phdrs[i]; 232 | 233 | printf("%05u: %p %08X %08X %08X %08X %08X %08X %08X %08X\n" 234 | , i 235 | , phdr 236 | , phdr->type 237 | , phdr->offset 238 | , phdr->vaddr 239 | , phdr->paddr 240 | , phdr->filesz 241 | , phdr->memsz 242 | , phdr->flags 243 | , phdr->align 244 | ); 245 | 246 | if(phdr->type == PT_LOAD && phdr->filesz > 0) { 247 | if(phdr->vaddr < target_ftext) { 248 | target_ftext = phdr->vaddr; 249 | } 250 | if(phdr->vaddr + phdr->filesz > target_edata) { 251 | target_edata = phdr->vaddr + phdr->filesz; 252 | } 253 | } 254 | } 255 | 256 | // Sanity-check the range 257 | if(target_ftext > target_edata) { 258 | fprintf(stderr, "couldn't find any PT_LOAD segments!\n"); 259 | goto fail_free_phdr_elf; 260 | } 261 | if(target_ftext < 0x80010000) { 262 | fprintf(stderr, "text segment starts too early\n"); 263 | goto fail_free_phdr_elf; 264 | } 265 | if(target_edata > 0x80200000) { 266 | fprintf(stderr, "data segment ends too late\n"); 267 | goto fail_free_phdr_elf; 268 | } 269 | if((target_ftext & 0x7FF) != 0) { 270 | fprintf(stderr, "text segment start not 2KB-aligned\n"); 271 | goto fail_free_phdr_elf; 272 | } 273 | 274 | // Get 8KB-aligned end 275 | target_aedata = (target_edata + 0x7FF) & ~0x7FF; 276 | printf("-----\n"); 277 | printf("File memory range: %08X -> %08X\n", target_ftext, target_edata); 278 | printf("Adjusted memory range: %08X -> %08X\n", target_ftext, target_aedata); 279 | 280 | // Open destination PS-X EXE 281 | psx = fopen(fname_psx, "w+b"); 282 | if(psx == NULL) { 283 | perror("fopen(psx)"); 284 | goto fail_free_phdr_elf; 285 | } 286 | 287 | // Prepare header 288 | memset(&psxh, 0, sizeof(psxh)); 289 | memcpy(psxh.magic, "PS-X EXE", 8); 290 | psxh.pc = ehdr.entry_point; 291 | psxh.gp = 0; 292 | psxh.ftext = target_ftext; 293 | psxh.filesz = target_aedata - target_ftext; 294 | psxh.bss_offs = 0; 295 | psxh.bss_len = 0; 296 | psxh.sp_base = 0x801FFFF0; 297 | psxh.sp_offs = 0; 298 | 299 | switch(region_flag) { 300 | case REGION_NTSC_JAPAN: 301 | strncpy(psxh.ascii_marker, 302 | "Sony Computer Entertainment Inc. for Japan area", 303 | sizeof(psxh.ascii_marker)); 304 | break; 305 | 306 | case REGION_NTSC_NORTH_AMERICA: 307 | strncpy(psxh.ascii_marker, 308 | "Sony Computer Entertainment Inc. for North America area", 309 | sizeof(psxh.ascii_marker)); 310 | break; 311 | 312 | case REGION_PAL: 313 | strncpy(psxh.ascii_marker, 314 | "Sony Computer Entertainment Inc. for Europe area", 315 | sizeof(psxh.ascii_marker)); 316 | break; 317 | } 318 | 319 | // Write header 320 | if(fwrite(&psxh, sizeof(psxh), 1, psx) != 1) { 321 | perror("fwrite(psx head)"); 322 | goto fail_close_psx; 323 | } 324 | 325 | // Zerofill EXE space 326 | memset(&blank_page, 0, 0x800); 327 | for(i = 0; i < psxh.filesz; i += 0x800) { 328 | if(fwrite(&blank_page, 0x800, 1, psx) != 1) { 329 | perror("fwrite(psx zero)"); 330 | goto fail_close_psx; 331 | } 332 | } 333 | 334 | // Apply PT_LOAD sections 335 | for(i = 0; i < ehdr.phdr_ent_count; i++) { 336 | phdr = &phdrs[i]; 337 | 338 | if(phdr->type != PT_LOAD) { 339 | continue; 340 | } 341 | 342 | if(phdr->filesz == 0) { 343 | continue; 344 | } 345 | 346 | // Calculate output address 347 | addr = phdr->vaddr - target_ftext; 348 | if(addr < 0 || addr >= psxh.filesz || addr+phdr->filesz > psxh.filesz) { 349 | fprintf(stderr, "PT_LOAD destination out of range\n"); 350 | goto fail_close_psx; 351 | } 352 | 353 | // Seek 354 | if(fseek(elf, phdr->offset, SEEK_SET)) { 355 | perror("fseek(psx copy - elf)"); 356 | goto fail_close_psx; 357 | } 358 | 359 | if(fseek(psx, addr + 0x800, SEEK_SET)) { 360 | perror("fseek(psx copy - psx)"); 361 | goto fail_close_psx; 362 | } 363 | 364 | // Allocate 365 | tmp_copy_buf = malloc(phdr->filesz); 366 | 367 | // Copy 368 | if(fread(tmp_copy_buf, phdr->filesz, 1, elf) != 1) { 369 | perror("fread(psx copy)"); 370 | goto fail_free_tmp_copy_buf; 371 | } 372 | if(fwrite(tmp_copy_buf, phdr->filesz, 1, psx) != 1) { 373 | perror("fwrite(psx copy)"); 374 | goto fail_free_tmp_copy_buf; 375 | } 376 | free(tmp_copy_buf); 377 | } 378 | 379 | // Close files 380 | fclose(psx); 381 | fclose(elf); 382 | 383 | free(phdrs); 384 | return 0; 385 | 386 | // FAILURES 387 | fail_free_tmp_copy_buf: 388 | free(tmp_copy_buf); 389 | fail_close_psx: 390 | fclose(psx); 391 | fail_free_phdr_elf: 392 | free(phdrs); 393 | fail_close_elf: 394 | fclose(elf); 395 | return 1; 396 | } 397 | 398 | -------------------------------------------------------------------------------- /toolsrc/psxavenc/decoding.c: -------------------------------------------------------------------------------- 1 | /* 2 | psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend 3 | 4 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 5 | Copyright (c) 2019 Ben "GreaseMonkey" Russell 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include "common.h" 25 | 26 | static void poll_av_packet(settings_t *settings, AVPacket *packet); 27 | 28 | int decode_audio_frame(AVCodecContext *codec, AVFrame *frame, int *frame_size, AVPacket *packet) { 29 | int ret; 30 | 31 | if (packet != NULL) { 32 | ret = avcodec_send_packet(codec, packet); 33 | if (ret != 0) { 34 | return 0; 35 | } 36 | } 37 | 38 | ret = avcodec_receive_frame(codec, frame); 39 | if (ret >= 0) { 40 | *frame_size = ret; 41 | return 1; 42 | } else { 43 | return ret == AVERROR(EAGAIN) ? 1 : 0; 44 | } 45 | } 46 | 47 | int decode_video_frame(AVCodecContext *codec, AVFrame *frame, int *frame_size, AVPacket *packet) { 48 | int ret; 49 | 50 | if (packet != NULL) { 51 | ret = avcodec_send_packet(codec, packet); 52 | if (ret != 0) { 53 | return 0; 54 | } 55 | } 56 | 57 | ret = avcodec_receive_frame(codec, frame); 58 | if (ret >= 0) { 59 | *frame_size = ret; 60 | return 1; 61 | } else { 62 | return ret == AVERROR(EAGAIN) ? 1 : 0; 63 | } 64 | } 65 | 66 | bool open_av_data(const char *filename, settings_t *settings) 67 | { 68 | AVPacket packet; 69 | 70 | av_decoder_state_t* av = &(settings->decoder_state_av); 71 | av->video_next_pts = 0.0; 72 | av->frame = NULL; 73 | av->video_frame_src_size = 0; 74 | av->video_frame_dst_size = 0; 75 | av->audio_stream_index = -1; 76 | av->video_stream_index = -1; 77 | av->format = NULL; 78 | av->audio_stream = NULL; 79 | av->video_stream = NULL; 80 | av->audio_codec_context = NULL; 81 | av->video_codec_context = NULL; 82 | av->audio_codec = NULL; 83 | av->video_codec = NULL; 84 | av->resampler = NULL; 85 | av->scaler = NULL; 86 | 87 | av->format = avformat_alloc_context(); 88 | if (avformat_open_input(&(av->format), filename, NULL, NULL)) { 89 | return false; 90 | } 91 | if (avformat_find_stream_info(av->format, NULL) < 0) { 92 | return false; 93 | } 94 | 95 | for (int i = 0; i < av->format->nb_streams; i++) { 96 | if (av->format->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { 97 | if (av->audio_stream_index >= 0) { 98 | fprintf(stderr, "open_av_data: found multiple audio tracks?\n"); 99 | return false; 100 | } 101 | av->audio_stream_index = i; 102 | } 103 | } 104 | if (av->audio_stream_index == -1) { 105 | return false; 106 | } 107 | 108 | for (int i = 0; i < av->format->nb_streams; i++) { 109 | if (av->format->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { 110 | if (av->video_stream_index >= 0) { 111 | fprintf(stderr, "open_av_data: found multiple video tracks?\n"); 112 | return false; 113 | } 114 | av->video_stream_index = i; 115 | } 116 | } 117 | 118 | av->audio_stream = av->format->streams[av->audio_stream_index]; 119 | av->video_stream = (av->video_stream_index != -1 ? av->format->streams[av->video_stream_index] : NULL); 120 | av->audio_codec = avcodec_find_decoder(av->audio_stream->codecpar->codec_id); 121 | av->audio_codec_context = avcodec_alloc_context3(av->audio_codec); 122 | if (av->audio_codec_context == NULL) { 123 | return false; 124 | } 125 | if (avcodec_parameters_to_context(av->audio_codec_context, av->audio_stream->codecpar) < 0) { 126 | return false; 127 | } 128 | if (avcodec_open2(av->audio_codec_context, av->audio_codec, NULL) < 0) { 129 | return false; 130 | } 131 | 132 | av->resampler = swr_alloc(); 133 | av_opt_set_int(av->resampler, "in_channel_count", av->audio_codec_context->channels, 0); 134 | av_opt_set_int(av->resampler, "in_channel_layout", av->audio_codec_context->channel_layout, 0); 135 | av_opt_set_int(av->resampler, "in_sample_rate", av->audio_codec_context->sample_rate, 0); 136 | av_opt_set_sample_fmt(av->resampler, "in_sample_fmt", av->audio_codec_context->sample_fmt, 0); 137 | 138 | av->sample_count_mul = settings->stereo ? 2 : 1; 139 | av_opt_set_int(av->resampler, "out_channel_count", settings->stereo ? 2 : 1, 0); 140 | av_opt_set_int(av->resampler, "out_channel_layout", settings->stereo ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO, 0); 141 | av_opt_set_int(av->resampler, "out_sample_rate", settings->frequency, 0); 142 | av_opt_set_sample_fmt(av->resampler, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); 143 | 144 | if (swr_init(av->resampler) < 0) { 145 | return false; 146 | } 147 | 148 | if (av->video_stream != NULL) { 149 | av->video_codec = avcodec_find_decoder(av->video_stream->codecpar->codec_id); 150 | av->video_codec_context = avcodec_alloc_context3(av->video_codec); 151 | if(av->video_codec_context == NULL) { 152 | return false; 153 | } 154 | if (avcodec_parameters_to_context(av->video_codec_context, av->video_stream->codecpar) < 0) { 155 | return false; 156 | } 157 | if (avcodec_open2(av->video_codec_context, av->video_codec, NULL) < 0) { 158 | return false; 159 | } 160 | 161 | av->scaler = sws_getContext( 162 | av->video_codec_context->width, 163 | av->video_codec_context->height, 164 | av->video_codec_context->pix_fmt, 165 | settings->video_width, 166 | settings->video_height, 167 | AV_PIX_FMT_RGBA, 168 | SWS_BICUBIC, 169 | NULL, 170 | NULL, 171 | NULL); 172 | 173 | av->video_frame_src_size = 4*av->video_codec_context->width*av->video_codec_context->height; 174 | av->video_frame_dst_size = 4*settings->video_width*settings->video_height; 175 | } 176 | 177 | av_init_packet(&packet); 178 | av->frame = av_frame_alloc(); 179 | if (av->frame == NULL) { 180 | return false; 181 | } 182 | 183 | settings->audio_samples = NULL; 184 | settings->audio_sample_count = 0; 185 | settings->video_frames = NULL; 186 | settings->video_frame_count = 0; 187 | 188 | return true; 189 | } 190 | 191 | static void poll_av_packet_audio(settings_t *settings, AVPacket *packet) 192 | { 193 | av_decoder_state_t* av = &(settings->decoder_state_av); 194 | 195 | int frame_size, frame_sample_count; 196 | uint8_t *buffer[1]; 197 | 198 | if (decode_audio_frame(av->audio_codec_context, av->frame, &frame_size, packet)) { 199 | size_t buffer_size = sizeof(int16_t) * av->sample_count_mul * swr_get_out_samples(av->resampler, av->frame->nb_samples); 200 | buffer[0] = malloc(buffer_size); 201 | memset(buffer[0], 0, buffer_size); 202 | frame_sample_count = swr_convert(av->resampler, buffer, av->frame->nb_samples, (const uint8_t**)av->frame->data, av->frame->nb_samples); 203 | settings->audio_samples = realloc(settings->audio_samples, (settings->audio_sample_count + ((frame_sample_count + 4032) * av->sample_count_mul)) * sizeof(int16_t)); 204 | memmove(&(settings->audio_samples[settings->audio_sample_count]), buffer[0], sizeof(int16_t) * frame_sample_count * av->sample_count_mul); 205 | settings->audio_sample_count += frame_sample_count * av->sample_count_mul; 206 | free(buffer[0]); 207 | } 208 | } 209 | 210 | static void poll_av_packet_video(settings_t *settings, AVPacket *packet) 211 | { 212 | av_decoder_state_t* av = &(settings->decoder_state_av); 213 | 214 | int frame_size; 215 | 216 | if (decode_video_frame(av->video_codec_context, av->frame, &frame_size, packet)) { 217 | double pts = (((double)av->frame->pts)*(double)av->video_stream->time_base.num)/av->video_stream->time_base.den; 218 | //fprintf(stderr, "%f\n", pts); 219 | // Drop frames with negative PTS values 220 | if(pts < 0.0) { 221 | // do nothing 222 | return; 223 | } 224 | if((settings->video_frame_count) >= 1 && pts < av->video_next_pts) { 225 | // do nothing 226 | return; 227 | } 228 | if((settings->video_frame_count) < 1) { 229 | av->video_next_pts = pts; 230 | } 231 | 232 | double pts_step = ((double)1.0*(double)settings->video_fps_den)/(double)settings->video_fps_num; 233 | //fprintf(stderr, "%d %f %f %f\n", (settings->video_frame_count), pts, av->video_next_pts, pts_step); 234 | av->video_next_pts += pts_step; 235 | // FIXME: increasing framerate doesn't fill it in with duplicate frames! 236 | assert(av->video_next_pts > pts); 237 | //size_t buffer_size = frame_count_mul; 238 | //buffer[0] = malloc(buffer_size); 239 | //memset(buffer[0], 0, buffer_size); 240 | settings->video_frames = realloc(settings->video_frames, (settings->video_frame_count + 1) * av->video_frame_dst_size); 241 | int dst_strides[1] = { 242 | settings->video_width*4, 243 | }; 244 | uint8_t *dst_pointers[1] = { 245 | (settings->video_frames) + av->video_frame_dst_size*(settings->video_frame_count), 246 | }; 247 | sws_scale(av->scaler, av->frame->data, av->frame->linesize, 0, av->frame->height, dst_pointers, dst_strides); 248 | 249 | settings->video_frame_count += 1; 250 | //free(buffer[0]); 251 | } 252 | } 253 | 254 | static void poll_av_packet(settings_t *settings, AVPacket *packet) 255 | { 256 | av_decoder_state_t* av = &(settings->decoder_state_av); 257 | 258 | if (packet->stream_index == av->audio_stream_index) { 259 | poll_av_packet_audio(settings, packet); 260 | } 261 | else if (packet->stream_index == av->video_stream_index) { 262 | poll_av_packet_video(settings, packet); 263 | } 264 | } 265 | 266 | bool poll_av_data(settings_t *settings) 267 | { 268 | av_decoder_state_t* av = &(settings->decoder_state_av); 269 | AVPacket packet; 270 | 271 | if (av_read_frame(av->format, &packet) >= 0) { 272 | poll_av_packet(settings, &packet); 273 | av_packet_unref(&packet); 274 | return true; 275 | } else { 276 | // out is always padded out with 4032 "0" samples, this makes calculations elsewhere easier 277 | memset((settings->audio_samples) + (settings->audio_sample_count), 0, 4032 * av->sample_count_mul * sizeof(int16_t)); 278 | 279 | return false; 280 | } 281 | } 282 | 283 | bool ensure_av_data(settings_t *settings, int needed_audio_samples, int needed_video_frames) 284 | { 285 | // 286 | av_decoder_state_t* av = &(settings->decoder_state_av); 287 | 288 | 289 | while (settings->audio_sample_count < needed_audio_samples || settings->video_frame_count < needed_video_frames) { 290 | //fprintf(stderr, "ensure %d -> %d, %d -> %d\n", settings->audio_sample_count, needed_audio_samples, settings->video_frame_count, needed_video_frames); 291 | if(!poll_av_data(settings)) { 292 | //fprintf(stderr, "cannot ensure\n"); 293 | return false; 294 | } 295 | } 296 | //fprintf(stderr, "ensure %d -> %d, %d -> %d\n", settings->audio_sample_count, needed_audio_samples, settings->video_frame_count, needed_video_frames); 297 | 298 | return true; 299 | } 300 | 301 | void pull_all_av_data(settings_t *settings) 302 | { 303 | while (poll_av_data(settings)) { 304 | // do nothing 305 | } 306 | 307 | fprintf(stderr, "Loaded %d samples.\n", settings->audio_sample_count); 308 | fprintf(stderr, "Loaded %d frames.\n", settings->video_frame_count); 309 | } 310 | 311 | void retire_av_data(settings_t *settings, int retired_audio_samples, int retired_video_frames) 312 | { 313 | av_decoder_state_t* av = &(settings->decoder_state_av); 314 | 315 | //fprintf(stderr, "retire %d -> %d, %d -> %d\n", settings->audio_sample_count, retired_audio_samples, settings->video_frame_count, retired_video_frames); 316 | assert(retired_audio_samples <= settings->audio_sample_count); 317 | assert(retired_video_frames <= settings->video_frame_count); 318 | 319 | int sample_size = sizeof(int16_t); 320 | if (settings->audio_sample_count > retired_audio_samples) { 321 | memmove(settings->audio_samples, settings->audio_samples + retired_audio_samples, (settings->audio_sample_count - retired_audio_samples)*sample_size); 322 | settings->audio_sample_count -= retired_audio_samples; 323 | } 324 | 325 | int frame_size = av->video_frame_dst_size; 326 | if (settings->video_frame_count > retired_video_frames) { 327 | memmove(settings->video_frames, settings->video_frames + retired_video_frames*frame_size, (settings->video_frame_count - retired_video_frames)*frame_size); 328 | settings->video_frame_count -= retired_video_frames; 329 | } 330 | } 331 | 332 | void close_av_data(settings_t *settings) 333 | { 334 | av_decoder_state_t* av = &(settings->decoder_state_av); 335 | 336 | av_frame_free(&(av->frame)); 337 | swr_free(&(av->resampler)); 338 | avcodec_close(av->audio_codec_context); 339 | avcodec_free_context(&(av->audio_codec_context)); 340 | avformat_free_context(av->format); 341 | 342 | if(settings->audio_samples != NULL) { 343 | free(settings->audio_samples); 344 | settings->audio_samples = NULL; 345 | } 346 | if(settings->video_frames != NULL) { 347 | free(settings->video_frames); 348 | settings->video_frames = NULL; 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /src/seedy/seedy.c: -------------------------------------------------------------------------------- 1 | /* 2 | seedy: CD-ROM driver 3 | 4 | Copyright (c) 2017, 2019 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | // 36 | // Maths! 37 | // 38 | 39 | static inline int tobcd8(int v) 40 | { 41 | return (v%10)+((v/10)<<4); 42 | } 43 | 44 | // 45 | // Raw hardware interface 46 | // 47 | 48 | void seedy_wait_until_ready(void) 49 | { 50 | while((PSXREG_CDROM_In_IDXSR & 0x80) != 0) { 51 | // wait for busy flag to clear 52 | } 53 | } 54 | 55 | int seedy_poll_interrupt_blocking(void) 56 | { 57 | PSXREG_CDROM_In_IDXSR = 0x01; 58 | PSXREG_CDROM_I1_INTFLG = 0x1F; 59 | while((PSXREG_CDROM_I1_INTFLG & 0x07) == 0x00) { 60 | // wait for interrupt 61 | } 62 | int result = (PSXREG_CDROM_I1_INTFLG & 0x07); 63 | PSXREG_CDROM_I1_INTFLG = 0x47; 64 | PSXREG_CDROM_In_IDXSR = 0x00; 65 | return result; 66 | } 67 | 68 | void seedy_ack_main_interrupt(void) 69 | { 70 | PSXREG_CDROM_In_IDXSR = 0x01; 71 | PSXREG_CDROM_I1_INTFLG = 0x1F; 72 | PSXREG_CDROM_In_IDXSR = 0x00; 73 | } 74 | 75 | void seedy_write_command(int b) 76 | { 77 | //seedy_wait_until_ready(); 78 | PSXREG_CDROM_In_IDXSR = 0x00; 79 | PSXREG_CDROM_I0_CMD = b; 80 | } 81 | 82 | void seedy_write_param(int b) 83 | { 84 | //seedy_wait_until_ready(); 85 | PSXREG_CDROM_In_IDXSR = 0x00; 86 | PSXREG_CDROM_I0_PARAMS = b; 87 | //seedy_wait_until_ready(); 88 | } 89 | 90 | int seedy_read_response(void) 91 | { 92 | PSXREG_CDROM_In_IDXSR = 0x01; 93 | return PSXREG_CDROM_I1_RESP_R; 94 | } 95 | 96 | void seedy_start_command(void) 97 | { 98 | seedy_wait_until_ready(); 99 | while((PSXREG_CDROM_In_IDXSR & 0x40) != 0) { volatile int k = PSXREG_CDROM_In_DATA_R; } 100 | while((PSXREG_CDROM_In_IDXSR & 0x20) != 0) { volatile int k = seedy_read_response(); } 101 | PSXREG_CDROM_In_IDXSR = 0x01; 102 | PSXREG_CDROM_I1_INTFLG = 0x1F; 103 | PSXREG_CDROM_In_IDXSR = 0x00; 104 | } 105 | 106 | // 107 | // Raw commands 108 | // 109 | 110 | void seedy_send_cmd_getstat(void) 111 | { 112 | seedy_start_command(); 113 | seedy_write_command(CD_CMD_GETSTAT); 114 | } 115 | 116 | void seedy_send_cmd_setloc(int min, int sec, int sub) 117 | { 118 | seedy_start_command(); 119 | seedy_write_param(min); 120 | seedy_write_param(sec); 121 | seedy_write_param(sub); 122 | seedy_write_command(CD_CMD_SETLOC); 123 | } 124 | 125 | void seedy_send_cmd_setloc_lba(int sector) // Helper 126 | { 127 | sector += 75*2; 128 | seedy_send_cmd_setloc(tobcd8(sector/(60*75)), tobcd8((sector/75)%60), tobcd8(sector%75)); 129 | } 130 | 131 | void seedy_send_cmd_play_notrack() 132 | { 133 | seedy_start_command(); 134 | seedy_write_command(CD_CMD_PLAY); 135 | } 136 | 137 | void seedy_send_cmd_play(int track) 138 | { 139 | seedy_start_command(); 140 | seedy_write_param(track); 141 | seedy_write_command(CD_CMD_PLAY); 142 | } 143 | 144 | void seedy_send_cmd_forward(void) 145 | { 146 | seedy_start_command(); 147 | seedy_write_command(CD_CMD_FORWARD); 148 | } 149 | 150 | void seedy_send_cmd_backward(void) 151 | { 152 | seedy_start_command(); 153 | seedy_write_command(CD_CMD_BACKWARD); 154 | } 155 | 156 | void seedy_send_cmd_readn(void) 157 | { 158 | seedy_start_command(); 159 | seedy_write_command(CD_CMD_READN); 160 | } 161 | 162 | void seedy_send_cmd_motoron(void) 163 | { 164 | seedy_start_command(); 165 | seedy_write_command(CD_CMD_MOTORON); 166 | } 167 | 168 | void seedy_send_cmd_stop(void) 169 | { 170 | seedy_start_command(); 171 | seedy_write_command(CD_CMD_STOP); 172 | } 173 | 174 | void seedy_send_cmd_pause(void) 175 | { 176 | seedy_start_command(); 177 | seedy_write_command(CD_CMD_PAUSE); 178 | } 179 | 180 | void seedy_send_cmd_init(void) 181 | { 182 | seedy_start_command(); 183 | seedy_write_command(CD_CMD_INIT); 184 | } 185 | 186 | void seedy_send_cmd_mute(void) 187 | { 188 | seedy_start_command(); 189 | seedy_write_command(CD_CMD_MUTE); 190 | } 191 | 192 | void seedy_send_cmd_demute(void) 193 | { 194 | seedy_start_command(); 195 | seedy_write_command(CD_CMD_DEMUTE); 196 | } 197 | 198 | void seedy_send_cmd_setfilter(int file, int channel) 199 | { 200 | seedy_start_command(); 201 | seedy_write_param(file); 202 | seedy_write_param(channel); 203 | seedy_write_command(CD_CMD_SETFILTER); 204 | } 205 | 206 | void seedy_send_cmd_setmode(int mode) 207 | { 208 | seedy_start_command(); 209 | seedy_write_param(mode); 210 | seedy_write_command(CD_CMD_SETMODE); 211 | } 212 | 213 | void seedy_send_cmd_getparam(void) 214 | { 215 | seedy_start_command(); 216 | seedy_write_command(CD_CMD_GETPARAM); 217 | } 218 | 219 | void seedy_send_cmd_getlocl(void) 220 | { 221 | seedy_start_command(); 222 | seedy_write_command(CD_CMD_GETLOCL); 223 | } 224 | 225 | void seedy_send_cmd_getlocp(void) 226 | { 227 | seedy_start_command(); 228 | seedy_write_command(CD_CMD_GETLOCP); 229 | } 230 | 231 | void seedy_send_cmd_setsession(int session) 232 | { 233 | seedy_start_command(); 234 | seedy_write_param(session); 235 | seedy_write_command(CD_CMD_SETSESSION); 236 | } 237 | 238 | void seedy_send_cmd_gettn(void) 239 | { 240 | seedy_start_command(); 241 | seedy_write_command(CD_CMD_GETTN); 242 | } 243 | 244 | void seedy_send_cmd_gettd(int track_bcd) 245 | { 246 | seedy_start_command(); 247 | seedy_write_command(CD_CMD_GETTD); 248 | } 249 | 250 | void seedy_send_cmd_getid(void) 251 | { 252 | seedy_start_command(); 253 | seedy_write_command(CD_CMD_GETID); 254 | } 255 | 256 | void seedy_send_cmd_reads(void) 257 | { 258 | seedy_start_command(); 259 | seedy_write_command(CD_CMD_READS); 260 | } 261 | 262 | void seedy_send_cmd_reset(void) 263 | { 264 | seedy_start_command(); 265 | seedy_write_command(CD_CMD_RESET); 266 | } 267 | 268 | void seedy_send_cmd_getq(void) 269 | { 270 | seedy_start_command(); 271 | seedy_write_command(CD_CMD_GETQ); 272 | } 273 | 274 | void seedy_send_cmd_readtoc(void) 275 | { 276 | seedy_start_command(); 277 | seedy_write_command(CD_CMD_READTOC); 278 | } 279 | 280 | // 281 | // ISR handlers 282 | // 283 | 284 | void seedy_isr_cdrom(void) 285 | { 286 | // TODO! 287 | } 288 | 289 | // 290 | // Helpers 291 | // 292 | 293 | static int seedy_ack(void) 294 | { 295 | int init_isr1 = seedy_poll_interrupt_blocking(); 296 | int init_val1 = seedy_read_response(); 297 | seedy_ack_main_interrupt(); 298 | return init_val1; 299 | } 300 | 301 | static void seedy_setloc_lba(int lba) 302 | { 303 | { 304 | seedy_send_cmd_setsession(0x01); 305 | int _isr1 = seedy_poll_interrupt_blocking(); 306 | int _val1 = seedy_read_response(); 307 | if(_isr1 == 0x05) { 308 | seedy_read_response(); 309 | seedy_ack_main_interrupt(); 310 | } else { 311 | seedy_ack_main_interrupt(); 312 | int _isr2 = seedy_poll_interrupt_blocking(); 313 | int _val2 = seedy_read_response(); 314 | seedy_ack_main_interrupt(); 315 | } 316 | } 317 | 318 | { 319 | seedy_send_cmd_setloc_lba(lba); 320 | int setloc_isr1 = seedy_poll_interrupt_blocking(); 321 | int setloc_val1 = seedy_read_response(); 322 | if(setloc_isr1 == 0x05) { 323 | seedy_read_response(); 324 | } 325 | seedy_ack_main_interrupt(); 326 | } 327 | } 328 | 329 | void seedy_drive_start(void) 330 | { 331 | { 332 | seedy_send_cmd_getstat(); 333 | int stat = seedy_ack(); 334 | if ((stat & 0x2) != 0) return; 335 | } 336 | 337 | { 338 | seedy_send_cmd_init(); 339 | } 340 | } 341 | 342 | void seedy_drive_stop(void) 343 | { 344 | seedy_send_cmd_stop(); 345 | } 346 | 347 | int seedy_is_xa_playing(void) 348 | { 349 | return ((PSXREG_CDROM_I1_INTFLG >> 2) & 0x01); 350 | } 351 | 352 | void seedy_stop_xa(void) 353 | { 354 | PSXREG_CDROM_In_IDXSR = 0x02; 355 | PSXREG_CDROM_I2_VOL_LL = 0x00; 356 | PSXREG_CDROM_I2_VOL_LR = 0x00; 357 | PSXREG_CDROM_In_IDXSR = 0x03; 358 | PSXREG_CDROM_I3_VOL_RL = 0x00; 359 | PSXREG_CDROM_I3_VOL_RR = 0x00; 360 | PSXREG_CDROM_I3_VOLCTL = 1; 361 | PSXREG_CDROM_In_IDXSR = 0x00; 362 | 363 | seedy_send_cmd_pause(); 364 | seedy_ack(); 365 | } 366 | 367 | void seedy_read_xa(int lba, int flags, int file, int channel) 368 | { 369 | PSXREG_CDROM_In_IDXSR = 0x03; 370 | PSXREG_CDROM_I3_VOLCTL = 1; 371 | PSXREG_CDROM_I3_VOL_RL = 0x00; 372 | PSXREG_CDROM_I3_VOL_RR = 0x80; 373 | PSXREG_CDROM_In_IDXSR = 0x02; 374 | PSXREG_CDROM_I2_SMAP_W = ( 375 | ((flags & SEEDY_PLAY_XA_STEREO) ? 0x01 : 0) 376 | | ((flags & SEEDY_PLAY_XA_18900) ? 0x04 : 0) 377 | | ((flags & SEEDY_PLAY_XA_8BIT) ? 0x10 : 0) 378 | | ((flags & SEEDY_PLAY_XA_EMPHASIS) ? 0x40 : 0) 379 | ); 380 | PSXREG_CDROM_I2_VOL_LL = 0x80; 381 | PSXREG_CDROM_I2_VOL_LR = 0x00; 382 | PSXREG_CDROM_In_IDXSR = 0x00; 383 | 384 | seedy_send_cmd_setfilter(file, channel); 385 | seedy_ack(); 386 | seedy_send_cmd_setmode( 387 | (!(flags & SEEDY_READ_SINGLE_SPEED) ? 0x80 : 0) 388 | | 0x48 389 | ); 390 | seedy_ack(); 391 | seedy_setloc_lba(lba); 392 | seedy_send_cmd_reads(); 393 | seedy_ack(); 394 | 395 | PSXREG_CDROM_In_IDXSR = 0x03; 396 | PSXREG_CDROM_I3_VOLCTL = 0; 397 | PSXREG_CDROM_In_IDXSR = 0x00; 398 | } 399 | 400 | int seedy_read_data_sync(int lba, int flags, uint8_t *buffer, int size) 401 | { 402 | int bufpos = 0; 403 | int secsize = (flags & SEEDY_READ_WHOLE_SECTORS) ? 0x924 : 0x800; 404 | 405 | seedy_send_cmd_setmode( 406 | (!(flags & SEEDY_READ_SINGLE_SPEED) ? 0x80 : 0) 407 | | ((flags & SEEDY_READ_WHOLE_SECTORS) ? 0x20 : 0) 408 | ); 409 | seedy_ack(); 410 | seedy_setloc_lba(lba); 411 | 412 | { 413 | seedy_send_cmd_readn(); 414 | int readn_isr1 = seedy_poll_interrupt_blocking(); 415 | int readn_val1 = seedy_read_response(); 416 | if(readn_isr1 == 0x03) { 417 | seedy_ack_main_interrupt(); 418 | int readn_isr2 = seedy_poll_interrupt_blocking(); 419 | int readn_val2 = seedy_read_response(); 420 | seedy_ack_main_interrupt(); 421 | if(readn_isr2 == 0x01) { 422 | PSXREG_CDROM_In_IDXSR = 0x00; 423 | PSXREG_CDROM_I0_RQST_W = 0x00; 424 | PSXREG_CDROM_I0_RQST_W = 0x80; 425 | while((PSXREG_CDROM_In_IDXSR & 0x40) == 0) { 426 | // wait 427 | } 428 | 429 | // Read as much of the buffer as we can 430 | for(int reps = ((size + secsize - 1) / secsize); reps > 0; reps--) { 431 | PSXREG_CDROM_In_IDXSR = 0x00; 432 | PSXREG_CDROM_I0_RQST_W = 0x80; 433 | for(int i = 0; i < secsize; i++) { 434 | while((PSXREG_CDROM_In_IDXSR & 0x40) == 0) { 435 | // wait 436 | } 437 | buffer[bufpos++] = PSXREG_CDROM_In_DATA_R; 438 | if (bufpos >= size) break; 439 | } 440 | while((PSXREG_CDROM_In_IDXSR & 0x40) != 0) { 441 | volatile int k = PSXREG_CDROM_In_DATA_R; 442 | } 443 | while((PSXREG_CDROM_In_IDXSR & 0x20) != 0) { 444 | volatile int k = seedy_read_response(); 445 | } 446 | 447 | if(reps > 1) { 448 | int readn_isr2 = seedy_poll_interrupt_blocking(); 449 | int readn_val2 = seedy_read_response(); 450 | seedy_ack_main_interrupt(); 451 | } 452 | } 453 | 454 | // Pause the damn thing 455 | seedy_send_cmd_pause(); 456 | int pause_isr1 = seedy_poll_interrupt_blocking(); 457 | int pause_val1 = seedy_read_response(); 458 | seedy_ack_main_interrupt(); 459 | int pause_isr2 = seedy_poll_interrupt_blocking(); 460 | int pause_val2 = seedy_read_response(); 461 | seedy_ack_main_interrupt(); 462 | } 463 | } else { 464 | seedy_read_response(); 465 | seedy_ack_main_interrupt(); 466 | } 467 | } 468 | } 469 | 470 | // 471 | // Initialisation 472 | // 473 | 474 | void seedy_init_cdrom(void) 475 | { 476 | PSXREG_CDROM_In_IDXSR = 0x01; 477 | PSXREG_CDROM_I1_INTE_W = 0x1F; 478 | PSXREG_CDROM_I1_INTFLG = 0x1F; 479 | while((PSXREG_CDROM_In_IDXSR & 0x20) != 0) { volatile int k = seedy_read_response(); } 480 | while((PSXREG_CDROM_In_IDXSR & 0x40) != 0) { volatile int k = PSXREG_CDROM_In_DATA_R; } 481 | 482 | PSXREG_CDROM_In_IDXSR = 0x00; 483 | PSXREG_CDROM_I0_RQST_W = 0x00; 484 | PSXREG_MEM_COM_DELAY = 0x1325; 485 | 486 | for(int i = 0; i < 2; i++) { 487 | seedy_send_cmd_getstat(); 488 | int getstat_isr1 = seedy_poll_interrupt_blocking(); 489 | int getstat_val1 = seedy_read_response(); 490 | seedy_ack_main_interrupt(); 491 | } 492 | 493 | { 494 | seedy_send_cmd_init(); 495 | int init_isr1 = seedy_poll_interrupt_blocking(); 496 | int init_val1 = seedy_read_response(); 497 | seedy_ack_main_interrupt(); 498 | int init_isr2 = seedy_poll_interrupt_blocking(); 499 | int init_val2 = seedy_read_response(); 500 | seedy_ack_main_interrupt(); 501 | } 502 | 503 | { 504 | seedy_send_cmd_demute(); 505 | int demute_isr1 = seedy_poll_interrupt_blocking(); 506 | int demute_val1 = seedy_read_response(); 507 | seedy_ack_main_interrupt(); 508 | } 509 | } 510 | 511 | -------------------------------------------------------------------------------- /include/psxdefs/regs.h: -------------------------------------------------------------------------------- 1 | /* 2 | psxdefs: PS register/constants definitions 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include 25 | 26 | #ifndef PSX_IOBASE 27 | #define PSX_IOBASE 0x1F800000 28 | #endif 29 | 30 | // Most of this is named to match psx-spx terms and definitions, 31 | // with some mild differences for consistency purposes. 32 | 33 | // 34 | // Memory control 35 | // 36 | #define PSXREG_MEM_EXP1_BASE (*(volatile uint32_t *)(PSX_IOBASE + 0x1000)) 37 | #define PSXREG_MEM_EXP2_BASE (*(volatile uint32_t *)(PSX_IOBASE + 0x1004)) 38 | #define PSXREG_MEM_EXP1_CONF (*(volatile uint32_t *)(PSX_IOBASE + 0x1008)) 39 | #define PSXREG_MEM_EXP3_CONF (*(volatile uint32_t *)(PSX_IOBASE + 0x100C)) 40 | #define PSXREG_MEM_BIOS_CONF (*(volatile uint32_t *)(PSX_IOBASE + 0x1010)) 41 | #define PSXREG_MEM_SPU_CONF (*(volatile uint32_t *)(PSX_IOBASE + 0x1014)) 42 | #define PSXREG_MEM_CDROM_CONF (*(volatile uint32_t *)(PSX_IOBASE + 0x1018)) 43 | #define PSXREG_MEM_EXP2_CONF (*(volatile uint32_t *)(PSX_IOBASE + 0x101C)) 44 | #define PSXREG_MEM_COM_DELAY (*(volatile uint32_t *)(PSX_IOBASE + 0x1020)) 45 | 46 | #define PSXREG_MEM_RAM_SIZE (*(volatile uint32_t *)(PSX_IOBASE + 0x1060)) 47 | 48 | // 49 | // Serial/Joypad/Memcard 50 | // 51 | #define PSXREG_JOY_DATA (*(volatile uint32_t *)(PSX_IOBASE + 0x1040)) 52 | #define PSXREG_JOY_DATA0 (*(volatile uint8_t *)(PSX_IOBASE + 0x1040)) 53 | #define PSXREG_JOY_STAT (*(volatile uint32_t *)(PSX_IOBASE + 0x1044)) 54 | #define PSXREG_JOY_MODE (*(volatile uint16_t *)(PSX_IOBASE + 0x1048)) 55 | #define PSXREG_JOY_CTRL (*(volatile uint16_t *)(PSX_IOBASE + 0x104A)) 56 | #define PSXREG_JOY_BAUD (*(volatile uint16_t *)(PSX_IOBASE + 0x104E)) 57 | #define PSXREG_SIO_DATA (*(volatile uint32_t *)(PSX_IOBASE + 0x1050)) 58 | #define PSXREG_SIO_DATA0 (*(volatile uint8_t *)(PSX_IOBASE + 0x1050)) 59 | #define PSXREG_SIO_STAT (*(volatile uint32_t *)(PSX_IOBASE + 0x1054)) 60 | #define PSXREG_SIO_MODE (*(volatile uint16_t *)(PSX_IOBASE + 0x1058)) 61 | #define PSXREG_SIO_CTRL (*(volatile uint16_t *)(PSX_IOBASE + 0x105A)) 62 | #define PSXREG_SIO_MISC (*(volatile uint16_t *)(PSX_IOBASE + 0x105C)) 63 | #define PSXREG_SIO_BAUD (*(volatile uint16_t *)(PSX_IOBASE + 0x105E)) 64 | 65 | // 66 | // Interrupt Controller 67 | // 68 | #define PSXREG_I_STAT (*(volatile uint16_t *)(PSX_IOBASE + 0x1070)) 69 | #define PSXREG_I_MASK (*(volatile uint16_t *)(PSX_IOBASE + 0x1074)) 70 | 71 | // 72 | // DMA Controller 73 | // 74 | #define PSXREG_Dn_MADR(n) (*(volatile uint32_t *)(PSX_IOBASE + 0x1080 + ((n)*0x10))) 75 | #define PSXREG_Dn_BCR(n) (*(volatile uint32_t *)(PSX_IOBASE + 0x1084 + ((n)*0x10))) 76 | #define PSXREG_Dn_CHCR(n) (*(volatile uint32_t *)(PSX_IOBASE + 0x1088 + ((n)*0x10))) 77 | 78 | #define PSXREG_DPCR (*(volatile uint32_t *)(PSX_IOBASE + 0x10F0)) 79 | #define PSXREG_DICR (*(volatile uint32_t *)(PSX_IOBASE + 0x10F4)) 80 | 81 | // 82 | // Timers 83 | // 84 | #define PSXREG_Tn_VALUE(n) (*(volatile uint32_t *)(PSX_IOBASE + 0x1100 + ((n)*0x10))) 85 | #define PSXREG_Tn_MODE(n) (*(volatile uint32_t *)(PSX_IOBASE + 0x1104 + ((n)*0x10))) 86 | #define PSXREG_Tn_TARGET(n) (*(volatile uint32_t *)(PSX_IOBASE + 0x1108 + ((n)*0x10))) 87 | 88 | // 89 | // CD-ROM 90 | // 91 | #define PSXREG_CDROM_In_IDXSR (*(volatile uint8_t *)(PSX_IOBASE + 0x1800)) 92 | #define PSXREG_CDROM_I0_CMD (*(volatile uint8_t *)(PSX_IOBASE + 0x1801)) 93 | #define PSXREG_CDROM_I0_PARAMS (*(volatile uint8_t *)(PSX_IOBASE + 0x1802)) 94 | #define PSXREG_CDROM_I0_RQST_W (*(volatile uint8_t *)(PSX_IOBASE + 0x1803)) 95 | #define PSXREG_CDROM_In_DATA_R (*(volatile uint8_t *)(PSX_IOBASE + 0x1802)) 96 | #define PSXREG_CDROM_I1_RESP_R (*(volatile uint8_t *)(PSX_IOBASE + 0x1801)) 97 | #define PSXREG_CDROM_I1_INTE_W (*(volatile uint8_t *)(PSX_IOBASE + 0x1802)) 98 | #define PSXREG_CDROM_I0_INTE_R (*(volatile uint8_t *)(PSX_IOBASE + 0x1803)) 99 | #define PSXREG_CDROM_I1_INTFLG (*(volatile uint8_t *)(PSX_IOBASE + 0x1803)) 100 | #define PSXREG_CDROM_I2_VOL_LL (*(volatile uint8_t *)(PSX_IOBASE + 0x1802)) 101 | #define PSXREG_CDROM_I2_VOL_LR (*(volatile uint8_t *)(PSX_IOBASE + 0x1803)) 102 | #define PSXREG_CDROM_I3_VOL_RR (*(volatile uint8_t *)(PSX_IOBASE + 0x1801)) 103 | #define PSXREG_CDROM_I3_VOL_RL (*(volatile uint8_t *)(PSX_IOBASE + 0x1802)) 104 | #define PSXREG_CDROM_I3_VOLCTL (*(volatile uint8_t *)(PSX_IOBASE + 0x1803)) 105 | #define PSXREG_CDROM_I2_SMAP_W (*(volatile uint8_t *)(PSX_IOBASE + 0x1801)) 106 | 107 | // 108 | // GPU 109 | // 110 | #define PSXREG_GPU_GP0 (*(volatile uint32_t *)(PSX_IOBASE + 0x1810)) 111 | #define PSXREG_GPU_GP1 (*(volatile uint32_t *)(PSX_IOBASE + 0x1814)) 112 | #define PSXREG_GPU_GPUREAD (*(volatile uint32_t *)(PSX_IOBASE + 0x1810)) 113 | #define PSXREG_GPU_GPUSTAT (*(volatile uint32_t *)(PSX_IOBASE + 0x1814)) 114 | 115 | // 116 | // MDEC 117 | // 118 | #define PSXREG_MDEC_MDEC0 (*(volatile uint32_t *)(PSX_IOBASE + 0x1820)) 119 | #define PSXREG_MDEC_MDEC1 (*(volatile uint32_t *)(PSX_IOBASE + 0x1824)) 120 | #define PSXREG_MDEC_CMD PSXREG_MDEC_MDEC0 121 | #define PSXREG_MDEC_DATA PSXREG_MDEC_MDEC0 122 | #define PSXREG_MDEC_CTRL PSXREG_MDEC_MDEC1 123 | #define PSXREG_MDEC_STAT PSXREG_MDEC_MDEC1 124 | 125 | // 126 | // SPU 127 | // 128 | #define PSXREG_SPU_n_VOL(n) (*(volatile uint32_t *)(PSX_IOBASE + 0x1C00 + (n)*0x10)) 129 | #define PSXREG_SPU_n_VOLL(n) (*(volatile uint16_t *)(PSX_IOBASE + 0x1C00 + (n)*0x10)) 130 | #define PSXREG_SPU_n_VOLR(n) (*(volatile uint16_t *)(PSX_IOBASE + 0x1C02 + (n)*0x10)) 131 | #define PSXREG_SPU_n_PITCH(n) (*(volatile uint16_t *)(PSX_IOBASE + 0x1C04 + (n)*0x10)) 132 | #define PSXREG_SPU_n_ADDR(n) (*(volatile uint16_t *)(PSX_IOBASE + 0x1C06 + (n)*0x10)) 133 | #define PSXREG_SPU_n_ADSR(n) (*(volatile uint32_t *)(PSX_IOBASE + 0x1C08 + (n)*0x10)) 134 | #define PSXREG_SPU_n_ADSR0(n) (*(volatile uint16_t *)(PSX_IOBASE + 0x1C08 + (n)*0x10)) 135 | #define PSXREG_SPU_n_ADSR1(n) (*(volatile uint16_t *)(PSX_IOBASE + 0x1C0A + (n)*0x10)) 136 | #define PSXREG_SPU_n_ADSR_CURR_VOL(n) (*(volatile uint16_t *)(PSX_IOBASE + 0x1C0C + (n)*0x10)) 137 | #define PSXREG_SPU_n_LOOP_ADDR(n) (*(volatile uint16_t *)(PSX_IOBASE + 0x1C0E + (n)*0x10)) 138 | 139 | #define PSXREG_SPU_MVOL (*(volatile uint32_t *)(PSX_IOBASE + 0x1D80)) 140 | #define PSXREG_SPU_MVOLL (*(volatile uint16_t *)(PSX_IOBASE + 0x1D80)) 141 | #define PSXREG_SPU_MVOLR (*(volatile uint16_t *)(PSX_IOBASE + 0x1D82)) 142 | #define PSXREG_SPU_KON (*(volatile uint32_t *)(PSX_IOBASE + 0x1D88)) 143 | #define PSXREG_SPU_KON0 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D88)) 144 | #define PSXREG_SPU_KON1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D8A)) 145 | #define PSXREG_SPU_KOFF (*(volatile uint32_t *)(PSX_IOBASE + 0x1D8C)) 146 | #define PSXREG_SPU_KOFF0 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D8C)) 147 | #define PSXREG_SPU_KOFF1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D8E)) 148 | #define PSXREG_SPU_PMON (*(volatile uint32_t *)(PSX_IOBASE + 0x1D90)) 149 | #define PSXREG_SPU_PMON0 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D90)) 150 | #define PSXREG_SPU_PMON1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D92)) 151 | #define PSXREG_SPU_NON (*(volatile uint32_t *)(PSX_IOBASE + 0x1D94)) 152 | #define PSXREG_SPU_NON0 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D94)) 153 | #define PSXREG_SPU_NON1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D96)) 154 | #define PSXREG_SPU_EON (*(volatile uint32_t *)(PSX_IOBASE + 0x1D98)) 155 | #define PSXREG_SPU_EON0 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D98)) 156 | #define PSXREG_SPU_EON1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D9A)) 157 | #define PSXREG_SPU_ENDX (*(volatile uint32_t *)(PSX_IOBASE + 0x1D9C)) 158 | #define PSXREG_SPU_ENDX0 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D9C)) 159 | #define PSXREG_SPU_ENDX1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1D9E)) 160 | // 0x1DA0 16-bit unknown 161 | #define PSXREG_SPU_IRQ_ADDR (*(volatile uint16_t *)(PSX_IOBASE + 0x1DA4)) 162 | #define PSXREG_SPU_MEM_ADDR (*(volatile uint16_t *)(PSX_IOBASE + 0x1DA6)) 163 | #define PSXREG_SPU_MEM_FIFO (*(volatile uint16_t *)(PSX_IOBASE + 0x1DA8)) 164 | #define PSXREG_SPU_CTRL (*(volatile uint16_t *)(PSX_IOBASE + 0x1DAA)) 165 | #define PSXREG_SPU_MEM_CTRL (*(volatile uint16_t *)(PSX_IOBASE + 0x1DAC)) 166 | #define PSXREG_SPU_STAT (*(volatile uint16_t *)(PSX_IOBASE + 0x1DAE)) 167 | #define PSXREG_SPU_CD_VOL (*(volatile uint32_t *)(PSX_IOBASE + 0x1DB0)) 168 | #define PSXREG_SPU_CD_VOLL (*(volatile uint16_t *)(PSX_IOBASE + 0x1DB0)) 169 | #define PSXREG_SPU_CD_VOLR (*(volatile uint16_t *)(PSX_IOBASE + 0x1DB2)) 170 | #define PSXREG_SPU_EXT_VOL (*(volatile uint32_t *)(PSX_IOBASE + 0x1DB4)) 171 | #define PSXREG_SPU_EXT_VOLL (*(volatile uint16_t *)(PSX_IOBASE + 0x1DB4)) 172 | #define PSXREG_SPU_EXT_VOLR (*(volatile uint16_t *)(PSX_IOBASE + 0x1DB6)) 173 | #define PSXREG_SPU_CURR_VOL (*(volatile uint32_t *)(PSX_IOBASE + 0x1DB8)) 174 | #define PSXREG_SPU_CURR_VOLL (*(volatile uint16_t *)(PSX_IOBASE + 0x1DB8)) 175 | #define PSXREG_SPU_CURR_VOLR (*(volatile uint16_t *)(PSX_IOBASE + 0x1DBA)) 176 | // 0x1DBC 32-bit unknown 177 | 178 | #define PSXREG_SPU_EFFECT_vOUT (*(volatile uint32_t *)(PSX_IOBASE + 0x1D84)) 179 | #define PSXREG_SPU_EFFECT_vLOUT (*(volatile uint16_t *)(PSX_IOBASE + 0x1D84)) 180 | #define PSXREG_SPU_EFFECT_vROUT (*(volatile uint16_t *)(PSX_IOBASE + 0x1D86)) 181 | #define PSXREG_SPU_EFFECT_mBASE (*(volatile uint16_t *)(PSX_IOBASE + 0x1DA2)) 182 | #define PSXREG_SPU_EFFECT_dAPF1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DC0)) 183 | #define PSXREG_SPU_EFFECT_dAPF2 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DC2)) 184 | #define PSXREG_SPU_EFFECT_vIIR (*(volatile uint16_t *)(PSX_IOBASE + 0x1DC4)) 185 | #define PSXREG_SPU_EFFECT_vCOMB1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DC6)) 186 | #define PSXREG_SPU_EFFECT_vCOMB2 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DC8)) 187 | #define PSXREG_SPU_EFFECT_vCOMB3 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DCA)) 188 | #define PSXREG_SPU_EFFECT_vCOMB4 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DCC)) 189 | #define PSXREG_SPU_EFFECT_vWALL (*(volatile uint16_t *)(PSX_IOBASE + 0x1DCE)) 190 | #define PSXREG_SPU_EFFECT_vAPF1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DD0)) 191 | #define PSXREG_SPU_EFFECT_vAPF2 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DD2)) 192 | 193 | #define PSXREG_SPU_EFFECT_mSAME (*(volatile uint32_t *)(PSX_IOBASE + 0x1DD4)) 194 | #define PSXREG_SPU_EFFECT_mCOMB1 (*(volatile uint32_t *)(PSX_IOBASE + 0x1DD8)) 195 | #define PSXREG_SPU_EFFECT_mCOMB2 (*(volatile uint32_t *)(PSX_IOBASE + 0x1DDC)) 196 | #define PSXREG_SPU_EFFECT_dSAME (*(volatile uint32_t *)(PSX_IOBASE + 0x1DE0)) 197 | #define PSXREG_SPU_EFFECT_mDIFF (*(volatile uint32_t *)(PSX_IOBASE + 0x1DE4)) 198 | #define PSXREG_SPU_EFFECT_mCOMB3 (*(volatile uint32_t *)(PSX_IOBASE + 0x1DE8)) 199 | #define PSXREG_SPU_EFFECT_mCOMB4 (*(volatile uint32_t *)(PSX_IOBASE + 0x1DEC)) 200 | #define PSXREG_SPU_EFFECT_dDIFF (*(volatile uint32_t *)(PSX_IOBASE + 0x1DF0)) 201 | #define PSXREG_SPU_EFFECT_mAPF1 (*(volatile uint32_t *)(PSX_IOBASE + 0x1DF4)) 202 | #define PSXREG_SPU_EFFECT_mAPF2 (*(volatile uint32_t *)(PSX_IOBASE + 0x1DF8)) 203 | #define PSXREG_SPU_EFFECT_vIN (*(volatile uint32_t *)(PSX_IOBASE + 0x1DFC)) 204 | 205 | #define PSXREG_SPU_EFFECT_mLSAME (*(volatile uint16_t *)(PSX_IOBASE + 0x1DD4)) 206 | #define PSXREG_SPU_EFFECT_mLCOMB1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DD8)) 207 | #define PSXREG_SPU_EFFECT_mLCOMB2 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DDC)) 208 | #define PSXREG_SPU_EFFECT_dLSAME (*(volatile uint16_t *)(PSX_IOBASE + 0x1DE0)) 209 | #define PSXREG_SPU_EFFECT_mLDIFF (*(volatile uint16_t *)(PSX_IOBASE + 0x1DE4)) 210 | #define PSXREG_SPU_EFFECT_mLCOMB3 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DE8)) 211 | #define PSXREG_SPU_EFFECT_mLCOMB4 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DEC)) 212 | #define PSXREG_SPU_EFFECT_dLDIFF (*(volatile uint16_t *)(PSX_IOBASE + 0x1DF0)) 213 | #define PSXREG_SPU_EFFECT_mLAPF1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DF4)) 214 | #define PSXREG_SPU_EFFECT_mLAPF2 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DF8)) 215 | #define PSXREG_SPU_EFFECT_mRSAME (*(volatile uint16_t *)(PSX_IOBASE + 0x1DD6)) 216 | #define PSXREG_SPU_EFFECT_mRCOMB1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DDA)) 217 | #define PSXREG_SPU_EFFECT_mRCOMB2 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DDE)) 218 | #define PSXREG_SPU_EFFECT_dRSAME (*(volatile uint16_t *)(PSX_IOBASE + 0x1DE2)) 219 | #define PSXREG_SPU_EFFECT_mRDIFF (*(volatile uint16_t *)(PSX_IOBASE + 0x1DE6)) 220 | #define PSXREG_SPU_EFFECT_mRCOMB3 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DEA)) 221 | #define PSXREG_SPU_EFFECT_mRCOMB4 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DEE)) 222 | #define PSXREG_SPU_EFFECT_dRDIFF (*(volatile uint16_t *)(PSX_IOBASE + 0x1DF2)) 223 | #define PSXREG_SPU_EFFECT_mRAPF1 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DF6)) 224 | #define PSXREG_SPU_EFFECT_mRAPF2 (*(volatile uint16_t *)(PSX_IOBASE + 0x1DFA)) 225 | #define PSXREG_SPU_EFFECT_vLIN (*(volatile uint16_t *)(PSX_IOBASE + 0x1DFC)) 226 | #define PSXREG_SPU_EFFECT_vRIN (*(volatile uint16_t *)(PSX_IOBASE + 0x1DFE)) 227 | 228 | #define PSXREG_SPU_n_CURR_VOL(n) (*(volatile uint32_t *)(PSX_IOBASE + 0x1E00 + (n)*0x04)) -------------------------------------------------------------------------------- /toolsrc/libpsxav/adpcm.c: -------------------------------------------------------------------------------- 1 | /* 2 | libpsxav: MDEC video + SPU/XA-ADPCM audio library 3 | 4 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 5 | Copyright (c) 2019 Ben "GreaseMonkey" Russell 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include "libpsxav.h" 27 | 28 | #define ADPCM_FILTER_COUNT 5 29 | #define XA_ADPCM_FILTER_COUNT 4 30 | #define SPU_ADPCM_FILTER_COUNT 5 31 | 32 | static const int16_t filter_k1[ADPCM_FILTER_COUNT] = {0, 60, 115, 98, 122}; 33 | static const int16_t filter_k2[ADPCM_FILTER_COUNT] = {0, 0, -52, -55, -60}; 34 | 35 | static int find_min_shift(const psx_audio_encoder_channel_state_t *state, int16_t *samples, int pitch, int filter) { 36 | // Assumption made: 37 | // 38 | // There is value in shifting right one step further to allow the nibbles to clip. 39 | // However, given a possible shift value, there is no value in shifting one step less. 40 | // 41 | // Having said that, this is not a completely accurate model of the encoder, 42 | // so maybe we will need to shift one step less. 43 | // 44 | int prev1 = state->prev1; 45 | int prev2 = state->prev2; 46 | int k1 = filter_k1[filter]; 47 | int k2 = filter_k2[filter]; 48 | 49 | int right_shift = 0; 50 | 51 | int32_t s_min = 0; 52 | int32_t s_max = 0; 53 | for (int i = 0; i < 28; i++) { 54 | int32_t raw_sample = samples[i * pitch]; 55 | int32_t previous_values = (k1*prev1 + k2*prev2 + (1<<5))>>6; 56 | int32_t sample = raw_sample - previous_values; 57 | if (sample < s_min) { s_min = sample; } 58 | if (sample > s_max) { s_max = sample; } 59 | prev2 = prev1; 60 | prev1 = raw_sample; 61 | } 62 | while(right_shift < 12 && (s_max>>right_shift) > +0x7) { right_shift += 1; }; 63 | while(right_shift < 12 && (s_min>>right_shift) < -0x8) { right_shift += 1; }; 64 | 65 | int min_shift = 12 - right_shift; 66 | assert(0 <= min_shift && min_shift <= 12); 67 | return min_shift; 68 | } 69 | 70 | static uint8_t attempt_to_encode_nibbles(psx_audio_encoder_channel_state_t *outstate, const psx_audio_encoder_channel_state_t *instate, int16_t *samples, int sample_limit, int pitch, uint8_t *data, int data_shift, int data_pitch, int filter, int sample_shift) { 71 | uint8_t nondata_mask = ~(0x0F << data_shift); 72 | int min_shift = sample_shift; 73 | int k1 = filter_k1[filter]; 74 | int k2 = filter_k2[filter]; 75 | 76 | uint8_t hdr = (min_shift & 0x0F) | (filter << 4); 77 | 78 | if (outstate != instate) { 79 | memcpy(outstate, instate, sizeof(psx_audio_encoder_channel_state_t)); 80 | } 81 | 82 | outstate->mse = 0; 83 | 84 | for (int i = 0; i < 28; i++) { 85 | int32_t sample = ((i * pitch) >= sample_limit ? 0 : samples[i * pitch]) + outstate->qerr; 86 | int32_t previous_values = (k1*outstate->prev1 + k2*outstate->prev2 + (1<<5))>>6; 87 | int32_t sample_enc = sample - previous_values; 88 | sample_enc <<= min_shift; 89 | sample_enc += (1<<(12-1)); 90 | sample_enc >>= 12; 91 | if(sample_enc < -8) { sample_enc = -8; } 92 | if(sample_enc > +7) { sample_enc = +7; } 93 | sample_enc &= 0xF; 94 | 95 | int32_t sample_dec = (int16_t) ((sample_enc&0xF) << 12); 96 | sample_dec >>= min_shift; 97 | sample_dec += previous_values; 98 | if (sample_dec > +0x7FFF) { sample_dec = +0x7FFF; } 99 | if (sample_dec < -0x8000) { sample_dec = -0x8000; } 100 | int64_t sample_error = sample_dec - sample; 101 | 102 | assert(sample_error < (1<<30)); 103 | assert(sample_error > -(1<<30)); 104 | 105 | data[i * data_pitch] = (data[i * data_pitch] & nondata_mask) | (sample_enc << data_shift); 106 | // FIXME: dithering is hard to predict 107 | //outstate->qerr += sample_error; 108 | outstate->mse += ((uint64_t)sample_error) * (uint64_t)sample_error; 109 | 110 | outstate->prev2 = outstate->prev1; 111 | outstate->prev1 = sample_dec; 112 | } 113 | 114 | return hdr; 115 | } 116 | 117 | static uint8_t encode_nibbles(psx_audio_encoder_channel_state_t *state, int16_t *samples, int sample_limit, int pitch, uint8_t *data, int data_shift, int data_pitch, int filter_count) { 118 | psx_audio_encoder_channel_state_t proposed; 119 | int64_t best_mse = ((int64_t)1<<(int64_t)50); 120 | int best_filter = 0; 121 | int best_sample_shift = 0; 122 | 123 | for (int filter = 0; filter < filter_count; filter++) { 124 | int true_min_shift = find_min_shift(state, samples, pitch, filter); 125 | 126 | // Testing has shown that the optimal shift can be off the true minimum shift 127 | // by 1 in *either* direction. 128 | // This is NOT the case when dither is used. 129 | int min_shift = true_min_shift - 1; 130 | int max_shift = true_min_shift + 1; 131 | if (min_shift < 0) { min_shift = 0; } 132 | if (max_shift > 12) { max_shift = 12; } 133 | 134 | for (int sample_shift = min_shift; sample_shift <= max_shift; sample_shift++) { 135 | // ignore header here 136 | attempt_to_encode_nibbles( 137 | &proposed, state, 138 | samples, sample_limit, pitch, 139 | data, data_shift, data_pitch, 140 | filter, sample_shift); 141 | 142 | if (best_mse > proposed.mse) { 143 | best_mse = proposed.mse; 144 | best_filter = filter; 145 | best_sample_shift = sample_shift; 146 | } 147 | } 148 | } 149 | 150 | // now go with the encoder 151 | return attempt_to_encode_nibbles( 152 | state, state, 153 | samples, sample_limit, pitch, 154 | data, data_shift, data_pitch, 155 | best_filter, best_sample_shift); 156 | } 157 | 158 | static void encode_block_xa(int16_t *audio_samples, int audio_samples_limit, uint8_t *data, psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state) { 159 | if (settings.bits_per_sample == 4) { 160 | if (settings.stereo) { 161 | data[0] = encode_nibbles(&(state->left), audio_samples, audio_samples_limit, 2, data + 0x10, 0, 4, XA_ADPCM_FILTER_COUNT); 162 | data[1] = encode_nibbles(&(state->right), audio_samples + 1, audio_samples_limit - 1, 2, data + 0x10, 4, 4, XA_ADPCM_FILTER_COUNT); 163 | data[2] = encode_nibbles(&(state->left), audio_samples + 56, audio_samples_limit - 56, 2, data + 0x11, 0, 4, XA_ADPCM_FILTER_COUNT); 164 | data[3] = encode_nibbles(&(state->right), audio_samples + 56 + 1, audio_samples_limit - 56 - 1, 2, data + 0x11, 4, 4, XA_ADPCM_FILTER_COUNT); 165 | data[8] = encode_nibbles(&(state->left), audio_samples + 56*2, audio_samples_limit - 56*2, 2, data + 0x12, 0, 4, XA_ADPCM_FILTER_COUNT); 166 | data[9] = encode_nibbles(&(state->right), audio_samples + 56*2 + 1, audio_samples_limit - 56*2 - 1, 2, data + 0x12, 4, 4, XA_ADPCM_FILTER_COUNT); 167 | data[10] = encode_nibbles(&(state->left), audio_samples + 56*3, audio_samples_limit - 56*3, 2, data + 0x13, 0, 4, XA_ADPCM_FILTER_COUNT); 168 | data[11] = encode_nibbles(&(state->right), audio_samples + 56*3 + 1, audio_samples_limit - 56*3 - 1, 2, data + 0x13, 4, 4, XA_ADPCM_FILTER_COUNT); 169 | } else { 170 | data[0] = encode_nibbles(&(state->left), audio_samples, audio_samples_limit, 1, data + 0x10, 0, 4, XA_ADPCM_FILTER_COUNT); 171 | data[1] = encode_nibbles(&(state->right), audio_samples + 28, audio_samples_limit - 28, 1, data + 0x10, 4, 4, XA_ADPCM_FILTER_COUNT); 172 | data[2] = encode_nibbles(&(state->left), audio_samples + 28*2, audio_samples_limit - 28*2, 1, data + 0x11, 0, 4, XA_ADPCM_FILTER_COUNT); 173 | data[3] = encode_nibbles(&(state->right), audio_samples + 28*3, audio_samples_limit - 28*3, 1, data + 0x11, 4, 4, XA_ADPCM_FILTER_COUNT); 174 | data[8] = encode_nibbles(&(state->left), audio_samples + 28*4, audio_samples_limit - 28*4, 1, data + 0x12, 0, 4, XA_ADPCM_FILTER_COUNT); 175 | data[9] = encode_nibbles(&(state->right), audio_samples + 28*5, audio_samples_limit - 28*5, 1, data + 0x12, 4, 4, XA_ADPCM_FILTER_COUNT); 176 | data[10] = encode_nibbles(&(state->left), audio_samples + 28*6, audio_samples_limit - 28*6, 1, data + 0x13, 0, 4, XA_ADPCM_FILTER_COUNT); 177 | data[11] = encode_nibbles(&(state->right), audio_samples + 28*7, audio_samples_limit - 28*7, 1, data + 0x13, 4, 4, XA_ADPCM_FILTER_COUNT); 178 | } 179 | } else { 180 | /* if (settings->stereo) { 181 | data[0] = encode_bytes(audio_samples, 2, data + 0x10); 182 | data[1] = encode_bytes(audio_samples + 1, 2, data + 0x11); 183 | data[2] = encode_bytes(audio_samples + 56, 2, data + 0x12); 184 | data[3] = encode_bytes(audio_samples + 57, 2, data + 0x13); 185 | } else { 186 | data[0] = encode_bytes(audio_samples, 1, data + 0x10); 187 | data[1] = encode_bytes(audio_samples + 28, 1, data + 0x11); 188 | data[2] = encode_bytes(audio_samples + 56, 1, data + 0x12); 189 | data[3] = encode_bytes(audio_samples + 84, 1, data + 0x13); 190 | } */ 191 | } 192 | } 193 | 194 | uint32_t psx_audio_xa_get_buffer_size(psx_audio_xa_settings_t settings, int sample_count) { 195 | int sample_pitch = psx_audio_xa_get_samples_per_sector(settings); 196 | int xa_sectors = ((sample_count + sample_pitch - 1) / sample_pitch); 197 | int xa_sector_size = psx_audio_xa_get_buffer_size_per_sector(settings); 198 | return xa_sectors * xa_sector_size; 199 | } 200 | 201 | uint32_t psx_audio_spu_get_buffer_size(int sample_count) { 202 | return ((sample_count + 27) / 28) << 4; 203 | } 204 | 205 | uint32_t psx_audio_xa_get_buffer_size_per_sector(psx_audio_xa_settings_t settings) { 206 | return settings.format == PSX_AUDIO_XA_FORMAT_XA ? 2336 : 2352; 207 | } 208 | 209 | uint32_t psx_audio_spu_get_buffer_size_per_block(void) { 210 | return 16; 211 | } 212 | 213 | uint32_t psx_audio_xa_get_samples_per_sector(psx_audio_xa_settings_t settings) { 214 | return (((settings.bits_per_sample == 8) ? 112 : 224) >> (settings.stereo ? 1 : 0)) * 18; 215 | } 216 | 217 | uint32_t psx_audio_spu_get_samples_per_block(void) { 218 | return 28; 219 | } 220 | 221 | static void psx_audio_xa_encode_init_sector(uint8_t *buffer, psx_audio_xa_settings_t settings) { 222 | if (settings.format == PSX_AUDIO_XA_FORMAT_XACD) { 223 | memset(buffer, 0, 2352); 224 | memset(buffer+0x001, 0xFF, 10); 225 | buffer[0x00F] = 0x02; 226 | } else { 227 | memset(buffer + 0x10, 0, 2336); 228 | } 229 | 230 | buffer[0x010] = settings.file_number; 231 | buffer[0x011] = settings.channel_number & 0x1F; 232 | buffer[0x012] = 0x24 | 0x40; 233 | buffer[0x013] = 234 | (settings.stereo ? 1 : 0) 235 | | (settings.frequency >= PSX_AUDIO_XA_FREQ_DOUBLE ? 0 : 4) 236 | | (settings.bits_per_sample >= 8 ? 16 : 0); 237 | memcpy(buffer + 0x014, buffer + 0x010, 4); 238 | } 239 | 240 | int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output) { 241 | int sample_jump = (settings.bits_per_sample == 8) ? 112 : 224; 242 | int i, j; 243 | int xa_sector_size = settings.format == PSX_AUDIO_XA_FORMAT_XA ? 2336 : 2352; 244 | int xa_offset = 2352 - xa_sector_size; 245 | uint8_t init_sector = 1; 246 | 247 | if (settings.stereo) { sample_count <<= 1; } 248 | 249 | for (i = 0, j = 0; i < sample_count || ((j % 18) != 0); i += sample_jump, j++) { 250 | uint8_t *sector_data = output + ((j/18) * xa_sector_size) - xa_offset; 251 | uint8_t *block_data = sector_data + 0x18 + ((j%18) * 0x80); 252 | 253 | if (init_sector) { 254 | psx_audio_xa_encode_init_sector(sector_data, settings); 255 | init_sector = 0; 256 | } 257 | 258 | encode_block_xa(samples + i, sample_count - i, block_data, settings, state); 259 | 260 | memcpy(block_data + 4, block_data, 4); 261 | memcpy(block_data + 12, block_data + 8, 4); 262 | 263 | if ((j+1)%18 == 0) { 264 | psx_cdrom_calculate_checksums(sector_data, PSX_CDROM_SECTOR_TYPE_MODE2_FORM2); 265 | init_sector = 1; 266 | } 267 | } 268 | 269 | return (((j + 17) / 18) * xa_sector_size); 270 | } 271 | 272 | int psx_audio_xa_encode_finalize(psx_audio_xa_settings_t settings, uint8_t *output, int output_length) { 273 | if (output_length >= 2336) { 274 | output[output_length - 2352 + 0x12] |= 0x80; 275 | output[output_length - 2352 + 0x18] |= 0x80; 276 | } 277 | } 278 | 279 | int psx_audio_xa_encode_simple(psx_audio_xa_settings_t settings, int16_t* samples, int sample_count, uint8_t *output) { 280 | psx_audio_encoder_state_t state; 281 | memset(&state, 0, sizeof(psx_audio_encoder_state_t)); 282 | int length = psx_audio_xa_encode(settings, &state, samples, sample_count, output); 283 | psx_audio_xa_encode_finalize(settings, output, length); 284 | return length; 285 | } 286 | 287 | int psx_audio_spu_encode(psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output) { 288 | uint8_t prebuf[28]; 289 | uint8_t *buffer = output; 290 | uint8_t *data; 291 | 292 | for (int i = 0; i < sample_count; i += 28, buffer += 16) { 293 | buffer[0] = encode_nibbles(&(state->left), samples + i, sample_count - i, 1, prebuf, 0, 1, SPU_ADPCM_FILTER_COUNT); 294 | buffer[1] = 0; 295 | 296 | for (int j = 0; j < 28; j+=2) { 297 | buffer[2 + (j>>1)] = (prebuf[j] & 0x0F) | (prebuf[j+1] << 4); 298 | } 299 | } 300 | 301 | return buffer - output; 302 | } 303 | 304 | int psx_audio_spu_encode_simple(int16_t* samples, int sample_count, uint8_t *output, int loop_start) { 305 | psx_audio_encoder_state_t state; 306 | memset(&state, 0, sizeof(psx_audio_encoder_state_t)); 307 | int length = psx_audio_spu_encode(&state, samples, sample_count, output); 308 | 309 | if (length >= 32) { 310 | if (loop_start < 0) { 311 | output[1] = 4; 312 | output[length - 16 + 1] = 1; 313 | } else { 314 | psx_audio_spu_set_flag_at_sample(output, loop_start, 4); 315 | output[length - 16 + 1] = 3; 316 | } 317 | } else if (length >= 16) { 318 | output[1] = loop_start >= 0 ? 7 : 5; 319 | } 320 | 321 | return length; 322 | } 323 | 324 | void psx_audio_spu_set_flag_at_sample(uint8_t* spu_data, int sample_pos, int flag) { 325 | int buffer_pos = (sample_pos / 28) << 4; 326 | spu_data[buffer_pos + 1] = flag; 327 | } 328 | -------------------------------------------------------------------------------- /toolsrc/psxavenc/mdec.c: -------------------------------------------------------------------------------- 1 | /* 2 | psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend 3 | 4 | Copyright (c) 2019, 2020 Adrian "asie" Siekierka 5 | Copyright (c) 2019 Ben "GreaseMonkey" Russell 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | #include "common.h" 25 | 26 | // high 8 bits = bit count 27 | // low 24 bits = value 28 | uint32_t huffman_encoding_map[0x10000]; 29 | bool dct_done_init = false; 30 | 31 | #define MAKE_HUFFMAN_PAIR(zeroes, value) (((zeroes)<<10)|((+(value))&0x3FF)),(((zeroes)<<10)|((-(value))&0x3FF)) 32 | const struct { 33 | int c_bits; 34 | uint32_t c_value; 35 | uint16_t u_hword_pos; 36 | uint16_t u_hword_neg; 37 | } huffman_lookup[] = { 38 | // Fuck this Huffman tree in particular --GM 39 | 2,0x3,MAKE_HUFFMAN_PAIR(0,1), 40 | 3,0x3,MAKE_HUFFMAN_PAIR(1,1), 41 | 4,0x4,MAKE_HUFFMAN_PAIR(0,2), 42 | 4,0x5,MAKE_HUFFMAN_PAIR(2,1), 43 | 5,0x05,MAKE_HUFFMAN_PAIR(0,3), 44 | 5,0x06,MAKE_HUFFMAN_PAIR(4,1), 45 | 5,0x07,MAKE_HUFFMAN_PAIR(3,1), 46 | 6,0x04,MAKE_HUFFMAN_PAIR(7,1), 47 | 6,0x05,MAKE_HUFFMAN_PAIR(6,1), 48 | 6,0x06,MAKE_HUFFMAN_PAIR(1,2), 49 | 6,0x07,MAKE_HUFFMAN_PAIR(5,1), 50 | 7,0x04,MAKE_HUFFMAN_PAIR(2,2), 51 | 7,0x05,MAKE_HUFFMAN_PAIR(9,1), 52 | 7,0x06,MAKE_HUFFMAN_PAIR(0,4), 53 | 7,0x07,MAKE_HUFFMAN_PAIR(8,1), 54 | 8,0x20,MAKE_HUFFMAN_PAIR(13,1), 55 | 8,0x21,MAKE_HUFFMAN_PAIR(0,6), 56 | 8,0x22,MAKE_HUFFMAN_PAIR(12,1), 57 | 8,0x23,MAKE_HUFFMAN_PAIR(11,1), 58 | 8,0x24,MAKE_HUFFMAN_PAIR(3,2), 59 | 8,0x25,MAKE_HUFFMAN_PAIR(1,3), 60 | 8,0x26,MAKE_HUFFMAN_PAIR(0,5), 61 | 8,0x27,MAKE_HUFFMAN_PAIR(10,1), 62 | 10,0x008,MAKE_HUFFMAN_PAIR(16,1), 63 | 10,0x009,MAKE_HUFFMAN_PAIR(5,2), 64 | 10,0x00A,MAKE_HUFFMAN_PAIR(0,7), 65 | 10,0x00B,MAKE_HUFFMAN_PAIR(2,3), 66 | 10,0x00C,MAKE_HUFFMAN_PAIR(1,4), 67 | 10,0x00D,MAKE_HUFFMAN_PAIR(15,1), 68 | 10,0x00E,MAKE_HUFFMAN_PAIR(14,1), 69 | 10,0x00F,MAKE_HUFFMAN_PAIR(4,2), 70 | 12,0x010,MAKE_HUFFMAN_PAIR(0,11), 71 | 12,0x011,MAKE_HUFFMAN_PAIR(8,2), 72 | 12,0x012,MAKE_HUFFMAN_PAIR(4,3), 73 | 12,0x013,MAKE_HUFFMAN_PAIR(0,10), 74 | 12,0x014,MAKE_HUFFMAN_PAIR(2,4), 75 | 12,0x015,MAKE_HUFFMAN_PAIR(7,2), 76 | 12,0x016,MAKE_HUFFMAN_PAIR(21,1), 77 | 12,0x017,MAKE_HUFFMAN_PAIR(20,1), 78 | 12,0x018,MAKE_HUFFMAN_PAIR(0,9), 79 | 12,0x019,MAKE_HUFFMAN_PAIR(19,1), 80 | 12,0x01A,MAKE_HUFFMAN_PAIR(18,1), 81 | 12,0x01B,MAKE_HUFFMAN_PAIR(1,5), 82 | 12,0x01C,MAKE_HUFFMAN_PAIR(3,3), 83 | 12,0x01D,MAKE_HUFFMAN_PAIR(0,8), 84 | 12,0x01E,MAKE_HUFFMAN_PAIR(6,2), 85 | 12,0x01F,MAKE_HUFFMAN_PAIR(17,1), 86 | 13,0x0010,MAKE_HUFFMAN_PAIR(10,2), 87 | 13,0x0011,MAKE_HUFFMAN_PAIR(9,2), 88 | 13,0x0012,MAKE_HUFFMAN_PAIR(5,3), 89 | 13,0x0013,MAKE_HUFFMAN_PAIR(3,4), 90 | 13,0x0014,MAKE_HUFFMAN_PAIR(2,5), 91 | 13,0x0015,MAKE_HUFFMAN_PAIR(1,7), 92 | 13,0x0016,MAKE_HUFFMAN_PAIR(1,6), 93 | 13,0x0017,MAKE_HUFFMAN_PAIR(0,15), 94 | 13,0x0018,MAKE_HUFFMAN_PAIR(0,14), 95 | 13,0x0019,MAKE_HUFFMAN_PAIR(0,13), 96 | 13,0x001A,MAKE_HUFFMAN_PAIR(0,12), 97 | 13,0x001B,MAKE_HUFFMAN_PAIR(26,1), 98 | 13,0x001C,MAKE_HUFFMAN_PAIR(25,1), 99 | 13,0x001D,MAKE_HUFFMAN_PAIR(24,1), 100 | 13,0x001E,MAKE_HUFFMAN_PAIR(23,1), 101 | 13,0x001F,MAKE_HUFFMAN_PAIR(22,1), 102 | 14,0x0010,MAKE_HUFFMAN_PAIR(0,31), 103 | 14,0x0011,MAKE_HUFFMAN_PAIR(0,30), 104 | 14,0x0012,MAKE_HUFFMAN_PAIR(0,29), 105 | 14,0x0013,MAKE_HUFFMAN_PAIR(0,28), 106 | 14,0x0014,MAKE_HUFFMAN_PAIR(0,27), 107 | 14,0x0015,MAKE_HUFFMAN_PAIR(0,26), 108 | 14,0x0016,MAKE_HUFFMAN_PAIR(0,25), 109 | 14,0x0017,MAKE_HUFFMAN_PAIR(0,24), 110 | 14,0x0018,MAKE_HUFFMAN_PAIR(0,23), 111 | 14,0x0019,MAKE_HUFFMAN_PAIR(0,22), 112 | 14,0x001A,MAKE_HUFFMAN_PAIR(0,21), 113 | 14,0x001B,MAKE_HUFFMAN_PAIR(0,20), 114 | 14,0x001C,MAKE_HUFFMAN_PAIR(0,19), 115 | 14,0x001D,MAKE_HUFFMAN_PAIR(0,18), 116 | 14,0x001E,MAKE_HUFFMAN_PAIR(0,17), 117 | 14,0x001F,MAKE_HUFFMAN_PAIR(0,16), 118 | 15,0x0010,MAKE_HUFFMAN_PAIR(0,40), 119 | 15,0x0011,MAKE_HUFFMAN_PAIR(0,39), 120 | 15,0x0012,MAKE_HUFFMAN_PAIR(0,38), 121 | 15,0x0013,MAKE_HUFFMAN_PAIR(0,37), 122 | 15,0x0014,MAKE_HUFFMAN_PAIR(0,36), 123 | 15,0x0015,MAKE_HUFFMAN_PAIR(0,35), 124 | 15,0x0016,MAKE_HUFFMAN_PAIR(0,34), 125 | 15,0x0017,MAKE_HUFFMAN_PAIR(0,33), 126 | 15,0x0018,MAKE_HUFFMAN_PAIR(0,32), 127 | 15,0x0019,MAKE_HUFFMAN_PAIR(1,14), 128 | 15,0x001A,MAKE_HUFFMAN_PAIR(1,13), 129 | 15,0x001B,MAKE_HUFFMAN_PAIR(1,12), 130 | 15,0x001C,MAKE_HUFFMAN_PAIR(1,11), 131 | 15,0x001D,MAKE_HUFFMAN_PAIR(1,10), 132 | 15,0x001E,MAKE_HUFFMAN_PAIR(1,9), 133 | 15,0x001F,MAKE_HUFFMAN_PAIR(1,8), 134 | 16,0x0010,MAKE_HUFFMAN_PAIR(1,18), 135 | 16,0x0011,MAKE_HUFFMAN_PAIR(1,17), 136 | 16,0x0012,MAKE_HUFFMAN_PAIR(1,16), 137 | 16,0x0013,MAKE_HUFFMAN_PAIR(1,15), 138 | 16,0x0014,MAKE_HUFFMAN_PAIR(6,3), 139 | 16,0x0015,MAKE_HUFFMAN_PAIR(16,2), 140 | 16,0x0016,MAKE_HUFFMAN_PAIR(15,2), 141 | 16,0x0017,MAKE_HUFFMAN_PAIR(14,2), 142 | 16,0x0018,MAKE_HUFFMAN_PAIR(13,2), 143 | 16,0x0019,MAKE_HUFFMAN_PAIR(12,2), 144 | 16,0x001A,MAKE_HUFFMAN_PAIR(11,2), 145 | 16,0x001B,MAKE_HUFFMAN_PAIR(31,1), 146 | 16,0x001C,MAKE_HUFFMAN_PAIR(30,1), 147 | 16,0x001D,MAKE_HUFFMAN_PAIR(29,1), 148 | 16,0x001E,MAKE_HUFFMAN_PAIR(28,1), 149 | 16,0x001F,MAKE_HUFFMAN_PAIR(27,1), 150 | }; 151 | #undef MAKE_HUFFMAN_PAIR 152 | 153 | const uint8_t quant_dec[8*8] = { 154 | 2, 16, 19, 22, 26, 27, 29, 34, 155 | 16, 16, 22, 24, 27, 29, 34, 37, 156 | 19, 22, 26, 27, 29, 34, 34, 38, 157 | 22, 22, 26, 27, 29, 34, 37, 40, 158 | 22, 26, 27, 29, 32, 35, 40, 48, 159 | 26, 27, 29, 32, 35, 40, 48, 58, 160 | 26, 27, 29, 34, 38, 46, 56, 69, 161 | 27, 29, 35, 38, 46, 56, 69, 83, 162 | }; 163 | 164 | const uint8_t dct_zigzag_table[8*8] = { 165 | 0x00,0x01,0x05,0x06,0x0E,0x0F,0x1B,0x1C, 166 | 0x02,0x04,0x07,0x0D,0x10,0x1A,0x1D,0x2A, 167 | 0x03,0x08,0x0C,0x11,0x19,0x1E,0x29,0x2B, 168 | 0x09,0x0B,0x12,0x18,0x1F,0x28,0x2C,0x35, 169 | 0x0A,0x13,0x17,0x20,0x27,0x2D,0x34,0x36, 170 | 0x14,0x16,0x21,0x26,0x2E,0x33,0x37,0x3C, 171 | 0x15,0x22,0x25,0x2F,0x32,0x38,0x3B,0x3D, 172 | 0x23,0x24,0x30,0x31,0x39,0x3A,0x3E,0x3F, 173 | }; 174 | 175 | const uint8_t dct_zagzig_table[8*8] = { 176 | 0x00,0x01,0x08,0x10,0x09,0x02,0x03,0x0A, 177 | 0x11,0x18,0x20,0x19,0x12,0x0B,0x04,0x05, 178 | 0x0C,0x13,0x1A,0x21,0x28,0x30,0x29,0x22, 179 | 0x1B,0x14,0x0D,0x06,0x07,0x0E,0x15,0x1C, 180 | 0x23,0x2A,0x31,0x38,0x39,0x32,0x2B,0x24, 181 | 0x1D,0x16,0x0F,0x17,0x1E,0x25,0x2C,0x33, 182 | 0x3A,0x3B,0x34,0x2D,0x26,0x1F,0x27,0x2E, 183 | 0x35,0x3C,0x3D,0x36,0x2F,0x37,0x3E,0x3F, 184 | }; 185 | 186 | const int16_t dct_scale_table[8*8] = { 187 | +0x5A82, +0x5A82, +0x5A82, +0x5A82, +0x5A82, +0x5A82, +0x5A82, +0x5A82, 188 | +0x7D8A, +0x6A6D, +0x471C, +0x18F8, -0x18F9, -0x471D, -0x6A6E, -0x7D8B, 189 | +0x7641, +0x30FB, -0x30FC, -0x7642, -0x7642, -0x30FC, +0x30FB, +0x7641, 190 | +0x6A6D, -0x18F9, -0x7D8B, -0x471D, +0x471C, +0x7D8A, +0x18F8, -0x6A6E, 191 | +0x5A82, -0x5A83, -0x5A83, +0x5A82, +0x5A82, -0x5A83, -0x5A83, +0x5A82, 192 | +0x471C, -0x7D8B, +0x18F8, +0x6A6D, -0x6A6E, -0x18F9, +0x7D8A, -0x471D, 193 | +0x30FB, -0x7642, +0x7641, -0x30FC, -0x30FC, +0x7641, -0x7642, +0x30FB, 194 | +0x18F8, -0x471D, +0x6A6D, -0x7D8B, +0x7D8A, -0x6A6E, +0x471C, -0x18F9, 195 | }; 196 | 197 | static void init_dct_data(void) 198 | { 199 | for(int i = 0; i <= 0xFFFF; i++) { 200 | huffman_encoding_map[i] = ((6+16)<<24)|((0x01<<16)|(i)); 201 | } 202 | 203 | for(int i = 0; i < sizeof(huffman_lookup)/sizeof(huffman_lookup[0]); i++) { 204 | int bits = huffman_lookup[i].c_bits+1; 205 | uint32_t base_value = huffman_lookup[i].c_value; 206 | huffman_encoding_map[huffman_lookup[i].u_hword_pos] = (bits<<24)|(base_value<<1)|0; 207 | huffman_encoding_map[huffman_lookup[i].u_hword_neg] = (bits<<24)|(base_value<<1)|1; 208 | } 209 | 210 | } 211 | 212 | static void flush_bits(vid_encoder_state_t *state) 213 | { 214 | if(state->bits_left < 16) { 215 | assert(state->bytes_used < sizeof(state->unmuxed)); 216 | state->unmuxed[state->bytes_used++] = (uint8_t)state->bits_value; 217 | assert(state->bytes_used < sizeof(state->unmuxed)); 218 | assert(state->bytes_used < 2016*state->frame_block_count); 219 | state->unmuxed[state->bytes_used++] = (uint8_t)(state->bits_value>>8); 220 | } 221 | state->bits_left = 16; 222 | state->bits_value = 0; 223 | } 224 | 225 | static void encode_bits(vid_encoder_state_t *state, int bits, uint32_t val) 226 | { 227 | assert(val < (1< 16 230 | // and I have no idea why, so I have to split this up --GM 231 | if (bits > 16) { 232 | encode_bits(state, bits-16, val>>16); 233 | bits = 16; 234 | val &= 0xFFFF; 235 | } 236 | 237 | if (state->bits_left == 0) { 238 | flush_bits(state); 239 | } 240 | 241 | while (bits > state->bits_left) { 242 | // Bits need truncating 243 | uint32_t outval = val; 244 | outval >>= bits - state->bits_left; 245 | assert(outval < (1<<16)); 246 | uint16_t old_value = state->bits_value; 247 | assert((state->bits_value & outval) == 0); 248 | state->bits_value |= (uint16_t)outval; 249 | //fprintf(stderr, "trunc %2d %2d %08X %04X %04X\n", bits, state->bits_left, val, old_value, state->bits_value); 250 | bits -= state->bits_left; 251 | uint32_t mask = (1<= 1); 254 | assert(val < (1<= 1) { 259 | assert(bits <= 16); 260 | // Bits may need shifting into place 261 | uint32_t outval = val; 262 | outval <<= state->bits_left - bits; 263 | assert(outval < (1<<16)); 264 | uint16_t old_value = state->bits_value; 265 | assert((state->bits_value & outval) == 0); 266 | state->bits_value |= (uint16_t)outval; 267 | //fprintf(stderr, "plop %2d %2d %08X %04X %04X\n", bits, state->bits_left, val, state->bits_value); 268 | state->bits_left -= bits; 269 | } 270 | } 271 | 272 | static void encode_ac_value(vid_encoder_state_t *state, uint16_t value) 273 | { 274 | assert(0 <= value && value <= 0xFFFF); 275 | 276 | #if 0 277 | for(int i = 0; i < sizeof(huffman_lookup)/sizeof(huffman_lookup[0]); i++) { 278 | if(value == huffman_lookup[i].u_hword_pos) { 279 | encode_bits(state, huffman_lookup[i].c_bits+1, (((uint32_t)huffman_lookup[i].c_value)<<1)|0); 280 | return; 281 | } 282 | else if(value == huffman_lookup[i].u_hword_neg) { 283 | encode_bits(state, huffman_lookup[i].c_bits+1, (((uint32_t)huffman_lookup[i].c_value)<<1)|1); 284 | return; 285 | } 286 | } 287 | 288 | // Use an escape 289 | encode_bits(state, 6+16, (0x01<<16)|(0xFFFF&(uint32_t)value)); 290 | #else 291 | uint32_t outword = huffman_encoding_map[value]; 292 | encode_bits(state, outword>>24, outword&0xFFFFFF); 293 | #endif 294 | } 295 | 296 | static void transform_dct_block(vid_encoder_state_t *state, int32_t *block) 297 | { 298 | // Apply DCT to block 299 | int32_t midblock[8*8]; 300 | 301 | for (int reps = 0; reps < 2; reps++) { 302 | for (int i = 0; i < 8; i++) { 303 | for (int j = 0; j < 8; j++) { 304 | int32_t v = 0; 305 | for(int k = 0; k < 8; k++) { 306 | v += block[8*j+k]*dct_scale_table[8*i+k]; 307 | } 308 | midblock[8*i+j] = (v + (1<<((14)-1)))>>(14); 309 | } 310 | } 311 | memcpy(block, midblock, sizeof(midblock)); 312 | } 313 | 314 | // FIXME: Work out why the math has to go this way 315 | block[0] /= 8; 316 | for (int i = 0; i < 64; i++) { 317 | // Finish reducing it 318 | block[i] /= 4; 319 | 320 | // If it's below the quantisation threshold, zero it 321 | if(abs(block[i]) < quant_dec[i]) { 322 | block[i] = 0; 323 | } 324 | } 325 | 326 | } 327 | 328 | static void encode_dct_block(vid_encoder_state_t *state, int32_t *block) 329 | { 330 | int dc_value = 0; 331 | 332 | for (int i = 0; i < 64; i++) { 333 | // Quantise it 334 | block[i] = (block[i])/quant_dec[i]; 335 | 336 | // Clamp it 337 | if (block[i] < -0x200) { block[i] = -0x200; } 338 | if (block[i] > +0x1FF) { block[i] = +0x1FF; } 339 | } 340 | 341 | // Get DC value 342 | dc_value = block[0]; 343 | //dc_value = 0; 344 | encode_bits(state, 10, dc_value&0x3FF); 345 | 346 | // Build RLE output 347 | uint16_t zero_rle_data[8*8]; 348 | int zero_rle_words = 0; 349 | for (int i = 1, zeroes = 0; i < 64; i++) { 350 | int ri = dct_zagzig_table[i]; 351 | //int ri = dct_zigzag_table[i]; 352 | if (block[ri] == 0) { 353 | zeroes++; 354 | } else { 355 | zero_rle_data[zero_rle_words++] = (zeroes<<10)|(block[ri]&0x3FF); 356 | zeroes = 0; 357 | state->uncomp_hwords_used += 1; 358 | } 359 | } 360 | 361 | // Now Huffman-code the data 362 | for (int i = 0; i < zero_rle_words; i++) { 363 | encode_ac_value(state, zero_rle_data[i]); 364 | } 365 | 366 | //fprintf(stderr, "dc %08X rles %2d\n", dc_value, zero_rle_words); 367 | //assert(dc_value >= -0x200); assert(dc_value < +0x200); 368 | 369 | // Store end of block 370 | encode_bits(state, 2, 0x2); 371 | state->uncomp_hwords_used += 2; 372 | 373 | state->uncomp_hwords_used = (state->uncomp_hwords_used+0xF)&~0xF; 374 | } 375 | 376 | static int reduce_dct_block(vid_encoder_state_t *state, int32_t *block, int32_t min_val, int *values_to_shed) 377 | { 378 | // Reduce so it can all fit 379 | int nonzeroes = 0; 380 | 381 | for (int i = 1; i < 64; i++) { 382 | //int ri = dct_zigzag_table[i]; 383 | if (block[i] != 0) { 384 | //if (abs(block[i])+(ri>>3) < min_val+(64>>3)) { 385 | if ((*values_to_shed) > 0 && abs(block[i]) < min_val*1) { 386 | block[i] = 0; 387 | (*values_to_shed)--; 388 | } else { 389 | nonzeroes++; 390 | } 391 | } 392 | } 393 | 394 | // Factor in DC + EOF values 395 | return nonzeroes+2; 396 | } 397 | 398 | static void encode_frame_str(uint8_t *video_frames, int video_frame_count, uint8_t *output, settings_t *settings) 399 | { 400 | int pitch = settings->video_width*4; 401 | int real_index = (settings->state_vid.frame_index-1); 402 | if (real_index > video_frame_count-1) { 403 | real_index = video_frame_count-1; 404 | } 405 | //uint8_t *video_frame = video_frames + settings->video_width*settings->video_height*4*real_index; 406 | uint8_t *video_frame = video_frames; 407 | 408 | if (!dct_done_init) { 409 | init_dct_data(); 410 | dct_done_init = true; 411 | } 412 | 413 | if (settings->state_vid.dct_block_lists[0] == NULL) { 414 | int dct_block_count_x = (settings->video_width+15)/16; 415 | int dct_block_count_y = (settings->video_height+15)/16; 416 | int dct_block_size = dct_block_count_x*dct_block_count_y*sizeof(int32_t)*8*8; 417 | for (int i = 0; i < 6; i++) { 418 | settings->state_vid.dct_block_lists[i] = malloc(dct_block_size); 419 | } 420 | } 421 | 422 | memset(settings->state_vid.unmuxed, 0, sizeof(settings->state_vid.unmuxed)); 423 | 424 | settings->state_vid.quant_scale = 1; 425 | settings->state_vid.uncomp_hwords_used = 0; 426 | settings->state_vid.bytes_used = 8; 427 | settings->state_vid.blocks_used = 0; 428 | 429 | // TODO: non-16x16-aligned videos 430 | assert((settings->video_width % 16) == 0); 431 | assert((settings->video_height % 16) == 0); 432 | 433 | // Do the initial transform 434 | for(int fx = 0; fx < settings->video_width; fx += 16) { 435 | for(int fy = 0; fy < settings->video_height; fy += 16) { 436 | // Order: Cr Cb [Y1|Y2\nY3|Y4] 437 | int block_offs = 8*8*((fy>>4)*((settings->video_width+15)/16)+(fx>>4)); 438 | int32_t *blocks[6] = { 439 | settings->state_vid.dct_block_lists[0] + block_offs, 440 | settings->state_vid.dct_block_lists[1] + block_offs, 441 | settings->state_vid.dct_block_lists[2] + block_offs, 442 | settings->state_vid.dct_block_lists[3] + block_offs, 443 | settings->state_vid.dct_block_lists[4] + block_offs, 444 | settings->state_vid.dct_block_lists[5] + block_offs, 445 | }; 446 | 447 | for(int y = 0; y < 8; y++) { 448 | for(int x = 0; x < 8; x++) { 449 | int k = y*8+x; 450 | 451 | int cr = 0; 452 | int cg = 0; 453 | int cb = 0; 454 | for(int cy = 0; cy < 2; cy++) { 455 | for(int cx = 0; cx < 2; cx++) { 456 | int coffs = pitch*(fy+y*2+cy) + 4*(fx+x*2+cx); 457 | cr += video_frame[coffs+0]; 458 | cg += video_frame[coffs+1]; 459 | cb += video_frame[coffs+2]; 460 | } 461 | } 462 | 463 | // TODO: Get the real math for this 464 | int cluma = cr+cg*2+cb; 465 | #if 1 466 | blocks[0][k] = ((cr<<2) - cluma + (1<<(4-1)))>>4; 467 | blocks[1][k] = ((cb<<2) - cluma + (1<<(4-1)))>>4; 468 | #else 469 | blocks[0][k] = 0; 470 | blocks[1][k] = 0; 471 | #endif 472 | 473 | for(int ly = 0; ly < 2; ly++) { 474 | for(int lx = 0; lx < 2; lx++) { 475 | int loffs = pitch*(fy+ly*8+y) + 4*(fx+lx*8+x); 476 | int lr = video_frame[loffs+0]; 477 | int lg = video_frame[loffs+1]; 478 | int lb = video_frame[loffs+2]; 479 | 480 | // TODO: Get the real math for this 481 | int lluma = (lr+lg*2+lb+2)-0x200; 482 | if(lluma < -0x200) { lluma = -0x200; } 483 | if(lluma > +0x1FF) { lluma = +0x1FF; } 484 | lluma >>= 1; 485 | blocks[2+2*ly+lx][k] = lluma; 486 | } 487 | } 488 | } 489 | } 490 | for(int i = 0; i < 6; i++) { 491 | transform_dct_block(&(settings->state_vid), blocks[i]); 492 | } 493 | } 494 | } 495 | 496 | // Now reduce all the blocks 497 | // TODO: Base this on actual bit count 498 | //const int accum_threshold = 6500; 499 | const int accum_threshold = 1025*settings->state_vid.frame_block_count; 500 | //const int accum_threshold = 900*settings->state_vid.frame_block_count; 501 | int values_to_shed = 0; 502 | for(int min_val = 0;; min_val += 1) { 503 | int accum = 0; 504 | for(int fx = 0; fx < settings->video_width; fx += 16) { 505 | for(int fy = 0; fy < settings->video_height; fy += 16) { 506 | // Order: Cr Cb [Y1|Y2\nY3|Y4] 507 | int block_offs = 8*8*((fy>>4)*((settings->video_width+15)/16)+(fx>>4)); 508 | int32_t *blocks[6] = { 509 | settings->state_vid.dct_block_lists[0] + block_offs, 510 | settings->state_vid.dct_block_lists[1] + block_offs, 511 | settings->state_vid.dct_block_lists[2] + block_offs, 512 | settings->state_vid.dct_block_lists[3] + block_offs, 513 | settings->state_vid.dct_block_lists[4] + block_offs, 514 | settings->state_vid.dct_block_lists[5] + block_offs, 515 | }; 516 | const int luma_reduce_mul = 8; 517 | const int chroma_reduce_mul = 8; 518 | for(int i = 6-1; i >= 0; i--) { 519 | accum += reduce_dct_block(&(settings->state_vid), blocks[i], (i < 2 ? min_val*luma_reduce_mul+1 : min_val*chroma_reduce_mul+1), &values_to_shed); 520 | } 521 | } 522 | } 523 | 524 | if(accum <= accum_threshold) { 525 | break; 526 | } 527 | 528 | values_to_shed = accum - accum_threshold; 529 | } 530 | 531 | // Now encode all the blocks 532 | for(int fx = 0; fx < settings->video_width; fx += 16) { 533 | for(int fy = 0; fy < settings->video_height; fy += 16) { 534 | // Order: Cr Cb [Y1|Y2\nY3|Y4] 535 | int block_offs = 8*8*((fy>>4)*((settings->video_width+15)/16)+(fx>>4)); 536 | int32_t *blocks[6] = { 537 | settings->state_vid.dct_block_lists[0] + block_offs, 538 | settings->state_vid.dct_block_lists[1] + block_offs, 539 | settings->state_vid.dct_block_lists[2] + block_offs, 540 | settings->state_vid.dct_block_lists[3] + block_offs, 541 | settings->state_vid.dct_block_lists[4] + block_offs, 542 | settings->state_vid.dct_block_lists[5] + block_offs, 543 | }; 544 | for(int i = 0; i < 6; i++) { 545 | encode_dct_block(&(settings->state_vid), blocks[i]); 546 | } 547 | } 548 | } 549 | 550 | encode_bits(&(settings->state_vid), 10, 0x1FF); 551 | encode_bits(&(settings->state_vid), 2, 0x2); 552 | settings->state_vid.uncomp_hwords_used += 2; 553 | settings->state_vid.uncomp_hwords_used = (settings->state_vid.uncomp_hwords_used+0xF)&~0xF; 554 | 555 | flush_bits(&(settings->state_vid)); 556 | 557 | settings->state_vid.blocks_used = ((settings->state_vid.uncomp_hwords_used+0xF)&~0xF)>>4; 558 | 559 | // We need a multiple of 4 560 | settings->state_vid.bytes_used = (settings->state_vid.bytes_used+0x3)&~0x3; 561 | 562 | // Build the demuxed header 563 | settings->state_vid.unmuxed[0x000] = (uint8_t)settings->state_vid.blocks_used; 564 | settings->state_vid.unmuxed[0x001] = (uint8_t)(settings->state_vid.blocks_used>>8); 565 | settings->state_vid.unmuxed[0x002] = (uint8_t)0x00; 566 | settings->state_vid.unmuxed[0x003] = (uint8_t)0x38; 567 | settings->state_vid.unmuxed[0x004] = (uint8_t)settings->state_vid.quant_scale; 568 | settings->state_vid.unmuxed[0x005] = (uint8_t)(settings->state_vid.quant_scale>>8); 569 | settings->state_vid.unmuxed[0x006] = 0x02; // Version 2 570 | settings->state_vid.unmuxed[0x007] = 0x00; 571 | 572 | retire_av_data(settings, 0, 1); 573 | } 574 | 575 | void encode_block_str(uint8_t *video_frames, int video_frame_count, uint8_t *output, settings_t *settings) 576 | { 577 | uint8_t header[32]; 578 | memset(header, 0, sizeof(header)); 579 | 580 | for(int i = 0; i < 7; i++) { 581 | while(settings->state_vid.frame_block_index >= settings->state_vid.frame_block_count) { 582 | settings->state_vid.frame_index++; 583 | // TODO: work out an optimal block count for this 584 | // TODO: calculate this all based on FPS 585 | settings->state_vid.frame_block_overflow_num += settings->state_vid.frame_block_base_overflow; 586 | settings->state_vid.frame_block_count = settings->state_vid.frame_block_overflow_num / settings->state_vid.frame_block_overflow_den; 587 | settings->state_vid.frame_block_overflow_num %= settings->state_vid.frame_block_overflow_den; 588 | settings->state_vid.frame_block_index = 0; 589 | encode_frame_str(video_frames, video_frame_count, output, settings); 590 | } 591 | // Header: MDEC0 register 592 | header[0x000] = 0x60; 593 | header[0x001] = 0x01; 594 | header[0x002] = 0x01; 595 | header[0x003] = 0x80; 596 | 597 | // Muxed chunk index/count 598 | int chunk_index = settings->state_vid.frame_block_index; 599 | int chunk_count = settings->state_vid.frame_block_count; 600 | header[0x004] = (uint8_t)chunk_index; 601 | header[0x005] = (uint8_t)(chunk_index>>8); 602 | header[0x006] = (uint8_t)chunk_count; 603 | header[0x007] = (uint8_t)(chunk_count>>8); 604 | 605 | // Frame index 606 | header[0x008] = (uint8_t)settings->state_vid.frame_index; 607 | header[0x009] = (uint8_t)(settings->state_vid.frame_index>>8); 608 | header[0x00A] = (uint8_t)(settings->state_vid.frame_index>>16); 609 | header[0x00B] = (uint8_t)(settings->state_vid.frame_index>>24); 610 | 611 | // Video frame size 612 | header[0x010] = (uint8_t)settings->video_width; 613 | header[0x011] = (uint8_t)(settings->video_width>>8); 614 | header[0x012] = (uint8_t)settings->video_height; 615 | header[0x013] = (uint8_t)(settings->video_height>>8); 616 | 617 | // 32-byte blocks required for MDEC data 618 | header[0x014] = (uint8_t)settings->state_vid.blocks_used; 619 | header[0x015] = (uint8_t)(settings->state_vid.blocks_used>>8); 620 | 621 | // Some weird thing 622 | header[0x016] = 0x00; 623 | header[0x017] = 0x38; 624 | 625 | // Quantization scale 626 | header[0x018] = (uint8_t)settings->state_vid.quant_scale; 627 | header[0x019] = (uint8_t)(settings->state_vid.quant_scale>>8); 628 | 629 | // Version 630 | header[0x01A] = 0x02; // Version 2 631 | header[0x01B] = 0x00; 632 | 633 | // Demuxed bytes used as a multiple of 4 634 | header[0x00C] = (uint8_t)settings->state_vid.bytes_used; 635 | header[0x00D] = (uint8_t)(settings->state_vid.bytes_used>>8); 636 | header[0x00E] = (uint8_t)(settings->state_vid.bytes_used>>16); 637 | header[0x00F] = (uint8_t)(settings->state_vid.bytes_used>>24); 638 | 639 | memcpy(output + 2352*i + 0x018, header, sizeof(header)); 640 | memcpy(output + 2352*i + 0x018 + 0x020, settings->state_vid.unmuxed + 2016*settings->state_vid.frame_block_index, 2016); 641 | 642 | settings->state_vid.frame_block_index++; 643 | } 644 | } 645 | -------------------------------------------------------------------------------- /toolsrc/pscd-new/pscd-new.c: -------------------------------------------------------------------------------- 1 | /* 2 | pscd-new: PS .BIN/.CUE image building tool 3 | 4 | Copyright (c) 2017 Ben "GreaseMonkey" Russell 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | //define BSWAP16(v) ((((v)>>8)&0xFF)|(((v)<<8)&0xFF00)) 34 | //define BSWAP32(v) (((BSWAP16(v)>>16)&0xFFFF)|((BSWAP16(v)<<16)&0xFFFF0000)) 35 | uint32_t BSWAP16(uint32_t v) { 36 | v = ((v & 0x00FF)<<8) | ((v & 0xFF00)>>8); 37 | return v; 38 | } 39 | uint32_t BSWAP32(uint32_t v) { 40 | v = ((v & 0x00FF00FF)<< 8) | ((v & 0xFF00FF00)>> 8); 41 | v = ((v & 0x0000FFFF)<<16) | ((v & 0xFFFF0000)>>16); 42 | return v; 43 | } 44 | #define TOLE16(v) (v) 45 | #define TOBE16(v) BSWAP16(v) 46 | #define TOLE32(v) (v) 47 | #define TOBE32(v) BSWAP32(v) 48 | 49 | /* 50 | from nocash's psx-spx ( http://problemkaputt.de/psx-spx.htm ): 51 | 52 | 00h 1 Length of Directory Record (LEN_DR) (33+LEN_FI+pad+LEN_SU) 53 | 01h 1 Extended Attribute Record Length (usually 00h) 54 | 02h 8 Data Logical Block Number (2x32bit) 55 | 0Ah 8 Data Size in Bytes (2x32bit) 56 | 12h 7 Recording Timestamp (yy-1900,mm,dd,hh,mm,ss,timezone) 57 | 19h 1 File Flags 8 bits (usually 00h=File, or 02h=Directory) 58 | 1Ah 1 File Unit Size (usually 00h) 59 | 1Bh 1 Interleave Gap Size (usually 00h) 60 | 1Ch 4 Volume Sequence Number (2x16bit, usually 0001h) 61 | 20h 1 Length of Name (LEN_FI) 62 | 21h LEN_FI File/Directory Name ("FILENAME.EXT;1" or "DIR_NAME" or 00h or 01h) 63 | xxh 0..1 Padding Field (00h) (only if LEN_FI is even) 64 | xxh LEN_SU System Use (LEN_SU bytes) (see below for CD-XA disks) 65 | 66 | LEN_SU can be calculated as "LEN_DR-(33+LEN_FI+Padding)". For CD-XA disks (as used in the PSX), LEN_SU is 14 bytes: 67 | 68 | 00h 2 Owner ID Group (whatever, usually 0000h, big endian) 69 | 02h 2 Owner ID User (whatever, usually 0000h, big endian) 70 | 04h 2 File Attributes (big endian): 71 | 0 Owner Read (usually 1) 72 | 1 Reserved (0) 73 | 2 Owner Execute (usually 1) 74 | 3 Reserved (0) 75 | 4 Group Read (usually 1) 76 | 5 Reserved (0) 77 | 6 Group Execute (usually 1) 78 | 7 Reserved (0) 79 | 8 World Read (usually 1) 80 | 9 Reserved (0) 81 | 10 World Execute (usually 1) 82 | 11 IS_MODE2 (0=MODE1 or CD-DA, 1=MODE2) 83 | 12 IS_MODE2_FORM2 (0=FORM1, 1=FORM2) 84 | 13 IS_INTERLEAVED (0=No, 1=Yes...?) (by file and/or channel?) 85 | 14 IS_CDDA (0=Data or ADPCM, 1=CD-DA Audio Track) 86 | 15 IS_DIRECTORY (0=File or CD-DA, 1=Directory Record) 87 | Commonly used Attributes are: 88 | 0D55h=Normal Binary File (with 800h-byte sectors) 89 | 2555h=Unknown (wipeout .AV files) (MODE1 ??) 90 | 4555h=CD-DA Audio Track (wipeout .SWP files, alone .WAV file) 91 | 3D55h=Streaming File (ADPCM and/or MDEC or so) 92 | 8D55h=Directory Record (parent-, current-, or sub-directory) 93 | 06h 2 Signature ("XA") 94 | 08h 1 File Number (Must match Subheader's File Number) 95 | 09h 5 Reserved (00h-filled) 96 | */ 97 | 98 | typedef enum dentmode 99 | { 100 | DENT_DAT, // Normal data 101 | DENT_RAW, // Raw sectors 102 | DENT_DIR, // Directory 103 | } dentmode_t; 104 | 105 | #define FNAME_MAX_LEN_ISO 32 106 | #define FNAME_MAX_LEN_LOC 128 107 | typedef struct isodent { 108 | uint8_t len_dr; 109 | uint8_t earl; // 0x00 110 | uint32_t dblk_le, dblk_be; 111 | uint32_t dlen_le, dlen_be; 112 | uint8_t ts_year; // year = ts_year+1900 113 | uint8_t ts_month; 114 | uint8_t ts_day; 115 | uint8_t ts_hour; 116 | uint8_t ts_minute; 117 | uint8_t ts_second; 118 | uint8_t ts_timezone; 119 | uint8_t flags; // 0x00 == file, 0x02 == directory 120 | uint8_t unitsz; // 0x00 121 | uint8_t ilgapsz; // 0x00 122 | uint16_t vsnum_le, vsnum_be; // 0x0001 123 | uint8_t len_fi; 124 | char fname[FNAME_MAX_LEN_ISO]; 125 | } __attribute__((__packed__)) isodent_t; 126 | 127 | typedef struct xadent { 128 | uint16_t gid; // big-endian, usually 0x0000 129 | uint16_t uid; // big-endian, usually 0x0000 130 | uint16_t fattr; // big-endian 131 | // usual fattr vals (BE/LE): 132 | // * 0x0D55/0x550D normal file 133 | // * 0x3D55/0x553D XA/STR m2f2 file 134 | // * 0x8D55/0x558D directory 135 | uint8_t magic[2]; // "XA" 136 | uint8_t filenum; // equal to the sector subheader file number 137 | uint8_t reserved1[5]; // all 0x00 138 | } __attribute__((__packed__)) xadent_t; 139 | 140 | typedef struct locdent { 141 | isodent_t isodent; 142 | xadent_t xadent; 143 | char loc_fname[FNAME_MAX_LEN_LOC]; 144 | char dir_fname[FNAME_MAX_LEN_LOC]; 145 | dentmode_t dmode; 146 | int path_idx; // -1 == not a dir 147 | int parent_dir; // -1 == no parent 148 | int parent_path; // -1 == no parent 149 | int sector; 150 | } locdent_t; 151 | 152 | /* 153 | Primary Volume Descriptor (sector 16 on PSX disks) 154 | 155 | 000h 1 Volume Descriptor Type (01h=Primary Volume Descriptor) 156 | 001h 5 Standard Identifier ("CD001") 157 | 006h 1 Volume Descriptor Version (01h=Standard) 158 | 007h 1 Reserved (00h) 159 | 008h 32 System Identifier (a-characters) ("PLAYSTATION") 160 | 028h 32 Volume Identifier (d-characters) (max 8 chars for PSX?) 161 | 048h 8 Reserved (00h) 162 | 050h 8 Volume Space Size (2x32bit, number of logical blocks) 163 | 058h 32 Reserved (00h) 164 | 078h 4 Volume Set Size (2x16bit) (usually 0001h) 165 | 07Ch 4 Volume Sequence Number (2x16bit) (usually 0001h) 166 | 080h 4 Logical Block Size in Bytes (2x16bit) (usually 0800h) (1 sector) 167 | 084h 8 Path Table Size in Bytes (2x32bit) (max 800h for PSX) 168 | 08Ch 4 Path Table 1 Block Number (32bit little-endian) 169 | 090h 4 Path Table 2 Block Number (32bit little-endian) (or 0=None) 170 | 094h 4 Path Table 3 Block Number (32bit big-endian) 171 | 098h 4 Path Table 4 Block Number (32bit big-endian) (or 0=None) 172 | 09Ch 34 Root Directory Record (see next chapter) 173 | 0BEh 128 Volume Set Identifier (d-characters) (usually empty) 174 | 13Eh 128 Publisher Identifier (a-characters) (company name) 175 | 1BEh 128 Data Preparer Identifier (a-characters) (empty or other) 176 | 23Eh 128 Application Identifier (a-characters) ("PLAYSTATION") 177 | 2BEh 37 Copyright Filename ("FILENAME.EXT;VER") (empty or text) 178 | 2E3h 37 Abstract Filename ("FILENAME.EXT;VER") (empty) 179 | 308h 37 Bibliographic Filename ("FILENAME.EXT;VER") (empty) 180 | 32Dh 17 Volume Creation Timestamp ("YYYYMMDDHHMMSSFF",timezone) 181 | 33Eh 17 Volume Modification Timestamp ("0000000000000000",00h) 182 | 34Fh 17 Volume Expiration Timestamp ("0000000000000000",00h) 183 | 360h 17 Volume Effective Timestamp ("0000000000000000",00h) 184 | 371h 1 File Structure Version (01h=Standard) 185 | 372h 1 Reserved for future (00h-filled) 186 | 373h 141 Application Use Area (00h-filled for PSX) 187 | 400h 8 CD-XA Identifying Signature ("CD-XA001" for PSX) 188 | 408h 2 CD-XA Flags (unknown purpose) (00h-filled for PSX) 189 | 40Ah 8 CD-XA Startup Directory (00h-filled for PSX) 190 | 412h 8 CD-XA Reserved (00h-filled for PSX) 191 | 41Ah 345 Application Use Area (00h-filled for PSX) 192 | 573h 653 Reserved for future (00h-filled) 193 | 194 | Volume Descriptor Set Terminator (sector 17 on PSX disks) 195 | 196 | 000h 1 Volume Descriptor Type (FFh=Terminator) 197 | 001h 5 Standard Identifier ("CD001") 198 | 006h 1 Terminator Version (01h=Standard) 199 | 007h 2041 Reserved (00h-filled) 200 | */ 201 | 202 | typedef struct pvd { 203 | uint8_t vdtype; // 0x01 = PVD 204 | uint8_t magic1[5]; // "CD001" 205 | uint8_t vdver; // 0x01 206 | uint8_t reserved2[1]; // 0x00 207 | uint8_t iden_sys[32]; // "PLAYSTATION" 208 | uint8_t iden_vol[32]; // max 8 chars for some reason 209 | uint8_t reserved4[8]; 210 | uint32_t lbn_count_le, lbn_count_be; 211 | uint8_t reserved3[32]; 212 | uint16_t vset_size_le, vset_size_be; // 0x0001 213 | uint16_t vset_seqn_le, vset_seqn_be; // 0x0001 214 | uint16_t block_size_le, block_size_be; // 0x0800 215 | uint32_t ptsize_size_le, ptsize_size_be; // max 0x0800 216 | uint32_t ptent1_le; 217 | uint32_t ptent2_le; // often 0 218 | uint32_t ptent3_be; 219 | uint32_t ptent4_be; // often 0 220 | uint8_t rootdir_record[34]; 221 | uint8_t iden_volset[128]; // "" 222 | uint8_t iden_publisher[128]; // "company name" 223 | uint8_t iden_datprep[128]; // "" 224 | uint8_t iden_app[128]; // "PLAYSTATION" 225 | uint8_t fname_copyright[37]; // "" 226 | uint8_t fname_abstract[37]; // "" 227 | uint8_t fname_biblio[37]; // "" 228 | uint8_t ts_creation[37]; // "YYYYMMDDHHMMSSFF" + timezone 229 | uint8_t ts_modification[37]; // "0000000000000000" + 0x00 230 | uint8_t ts_expiration[37]; // "0000000000000000" + 0x00 231 | uint8_t ts_effective[37]; // "0000000000000000" + 0x00 232 | uint8_t fsver; // 0x01 233 | uint8_t reserved1[1]; // 0x00 234 | uint8_t appuse1[141]; // 0x00 235 | uint8_t xamagic1[8]; // "CD-XA001" 236 | uint8_t xapad1[18]; // 0x00 237 | uint8_t appuse2[345]; // 0x00 238 | uint8_t pad1[653]; 239 | }__attribute__((__packed__)) pvd_t; 240 | 241 | typedef struct vdst { 242 | uint8_t vdtype; // 0xFF = Terminator 243 | uint8_t magic1[5]; // "CD001" 244 | uint8_t vdver; // 0x01 245 | uint8_t pad1[2041]; 246 | } __attribute__((__packed__)) vdst_t; 247 | 248 | #define MAX_DENTS 2048 249 | locdent_t dent_list[MAX_DENTS]; 250 | int dent_remap[MAX_DENTS]; 251 | int dent_path_remap[MAX_DENTS]; 252 | int dent_count = 0; 253 | int dent_path_count = 0; 254 | uint32_t sector_count = 0; 255 | 256 | typedef enum secmode 257 | { 258 | SEC_EMPTY = 0, 259 | SEC_AUDIO, // Raw 260 | SEC_MODE1, // Normal data 261 | SEC_MODE2_FORM1, // XA data 262 | SEC_MODE2_FORM2, // XA ADPCM 263 | 264 | SEC_RAW, // Provide raw sector and fix it up 265 | } secmode_t; 266 | 267 | void free_whole_file(char **bufptr, size_t *buf_len) 268 | { 269 | if(*bufptr != NULL) { 270 | free(*bufptr); 271 | } 272 | *bufptr = NULL; 273 | *buf_len = 0; 274 | } 275 | 276 | int load_whole_file(char **bufptr, size_t *buf_len, const char *fname) 277 | { 278 | printf("Loading \"%s\"\n", fname); 279 | FILE *fp = fopen(fname, "rb"); 280 | assert(fp != NULL); 281 | 282 | free_whole_file(bufptr, buf_len); 283 | *buf_len = 0; 284 | for(;;) { 285 | size_t step_size = (1<<20); // 1MB at a time 286 | *bufptr = realloc(*bufptr, *buf_len+step_size); 287 | size_t amt = fread(*bufptr + *buf_len, 1, step_size, fp); 288 | if(amt == 0) { 289 | assert(feof(fp)); 290 | break; 291 | } 292 | *buf_len += amt; 293 | } 294 | *bufptr = realloc(*bufptr, *buf_len); 295 | 296 | fclose(fp); 297 | 298 | return 0; 299 | } 300 | 301 | uint32_t edc_table[256]; 302 | int GF8_LOG[256]; 303 | int GF8_ILOG[256]; 304 | int GF8_PRODUCT[43][256]; 305 | 306 | int subfunc(int a, int b) 307 | { 308 | if(a>0) 309 | { 310 | a=GF8_LOG[a]-b; 311 | if(a<0) 312 | a += 255; 313 | 314 | a=GF8_ILOG[a]; 315 | } 316 | 317 | return(a); 318 | } 319 | 320 | void init_tables(void) 321 | { 322 | int i, j; 323 | 324 | // standard "fast-CRC" LUT except with a different polynomial 325 | for(i=0; i <= 0xFF; i++) 326 | { 327 | uint32_t x = i; 328 | for(j = 0; j <= 7; j++) 329 | { 330 | uint32_t carry = x&1; 331 | x >>= 1; 332 | 333 | if(carry) 334 | x ^= 0xD8018001; 335 | } 336 | 337 | edc_table[i]=x; 338 | } 339 | 340 | GF8_LOG[0x00]=0x00; 341 | GF8_ILOG[0xFF]=0x00; 342 | int x=0x01; 343 | for(i=0x00; i <= 0xFE; i++) 344 | { 345 | GF8_LOG[x]=i; 346 | GF8_ILOG[i]=x; 347 | 348 | int carry8bit = x&0x80; 349 | x <<= 1; 350 | if(carry8bit) 351 | x ^= 0x1D; 352 | 353 | x &= 0xFF; 354 | } 355 | 356 | for(j=0; j <= 42; j++) 357 | { 358 | int xx = GF8_ILOG[44-j]; 359 | int yy = subfunc(xx ^ 1,0x19); 360 | 361 | xx = subfunc(xx,0x01); 362 | xx = subfunc(xx ^ 1,0x18); 363 | xx = GF8_LOG[xx]; 364 | yy = GF8_LOG[yy]; 365 | GF8_PRODUCT[j][0]=0x0000; 366 | for(i=0x01; i <= 0xFF; i++) 367 | { 368 | int x=xx+GF8_LOG[i]; 369 | int y=yy+GF8_LOG[i]; 370 | 371 | if(x>=255) x -= 255; 372 | if(y>=255) y -= 255; 373 | 374 | GF8_PRODUCT[j][i]=GF8_ILOG[x]+(GF8_ILOG[y] << 8); 375 | } 376 | } 377 | } 378 | 379 | void calc_parity(uint8_t *sector, int offs, int len, int j0, int step1, int step2) 380 | { 381 | int i, j; 382 | 383 | int src=0x00c; 384 | int dst=0x81c+offs; 385 | int srcmax=dst; 386 | 387 | for(i = 0; i <= len-1; i++) 388 | { 389 | int base=src, x=0x0000, y=0x0000; 390 | for(j=j0; j <= 42; j++) 391 | { 392 | x ^= GF8_PRODUCT[j][sector[src+0]]; 393 | y ^= GF8_PRODUCT[j][sector[src+1]]; 394 | src += step1; 395 | if((step1 == 2*44) && (src>=srcmax)) 396 | src -= 2*1118; 397 | } 398 | 399 | sector[dst+2*len+0]=x & 0x0FF; sector[dst+0]=x >> 8; 400 | sector[dst+2*len+1]=y & 0x0FF; sector[dst+1]=y >> 8; 401 | dst += 2; 402 | src = base + step2; 403 | } 404 | } 405 | 406 | void calc_p_parity(uint8_t *sector) 407 | { 408 | calc_parity(sector,0,43,19,2*43,2); 409 | } 410 | 411 | void calc_q_parity(uint8_t *sector) 412 | { 413 | calc_parity(sector,43*4,26,0,2*44,2*43); 414 | } 415 | 416 | void adjust_edc(uint8_t *addr, int len) 417 | { 418 | int i; 419 | uint32_t x=0x00000000; 420 | 421 | for(i=0; i <= len-1; i++) 422 | { 423 | x ^= (uint32_t)(uint8_t)addr[i]; 424 | x = (x>>8) ^ edc_table[x & 0xFF]; 425 | } 426 | 427 | //append EDC value (little endian) 428 | addr[0*4+len+0] = x >> 0; 429 | addr[0*4+len+1] = x >> 8; 430 | addr[0*4+len+2] = x >> 16; 431 | addr[0*4+len+3] = x >> 24; 432 | } 433 | 434 | int tobcd8(int v) 435 | { 436 | return (v%10)+((v/10)<<4); 437 | } 438 | 439 | void encode_sector(uint8_t *rawsec, const uint8_t *srcsec, int lba, secmode_t secmode, uint8_t submode) 440 | { 441 | // Sync 442 | memset(rawsec+0x000, 0x00, 0x930-0x000); 443 | memset(rawsec+0x001, 0xFF, 0x00B-0x001); 444 | 445 | // Time 446 | lba += 75*2; 447 | rawsec[0x00C] = tobcd8((lba/75)/60); 448 | rawsec[0x00D] = tobcd8((lba/75)%60); 449 | rawsec[0x00E] = tobcd8(lba%75); 450 | 451 | //bool is_raw = (secmode == SEC_RAW); 452 | 453 | if(secmode == SEC_RAW) { 454 | // Adjust it so it works 455 | memcpy(rawsec+0x010, srcsec+0x010, 0x930-0x010); 456 | if(srcsec[0x00F] == 0) { 457 | secmode = SEC_EMPTY; 458 | 459 | } else if(srcsec[0x00F] == 1) { 460 | secmode = SEC_MODE1; 461 | srcsec += 0x010; 462 | 463 | } else if(srcsec[0x00F] == 2) { 464 | if((srcsec[0x012] & 0x20) == 0) { 465 | secmode = SEC_MODE2_FORM1; 466 | srcsec += 0x018; 467 | } else { 468 | secmode = SEC_MODE2_FORM2; 469 | } 470 | } else { 471 | assert(!"invalid raw sector type"); 472 | abort(); 473 | } 474 | } 475 | 476 | switch(secmode) 477 | { 478 | case SEC_EMPTY: 479 | rawsec[0x00F] = 0x00; 480 | break; 481 | 482 | case SEC_MODE1: 483 | rawsec[0x00F] = 0x01; 484 | memcpy(rawsec+0x010, srcsec, 0x800); 485 | //*(uint32_t *)(&rawsec[0x810]) = calculate_edc(rawsec, 0x000, 0x810); 486 | adjust_edc(rawsec+0x000, 0x810); 487 | calc_p_parity(rawsec); 488 | calc_q_parity(rawsec); 489 | break; 490 | 491 | case SEC_MODE2_FORM1: 492 | rawsec[0x00F] = 0x02; 493 | rawsec[0x010] = rawsec[0x014] = 0x00; 494 | rawsec[0x011] = rawsec[0x015] = 0x00; 495 | rawsec[0x012] = rawsec[0x016] = submode&~0x20; 496 | rawsec[0x013] = rawsec[0x017] = 0x00; 497 | memcpy(rawsec+0x018, srcsec, 0x800); 498 | adjust_edc(rawsec+0x010, 0x808); 499 | { 500 | uint32_t bakhed = *(uint32_t *)(&rawsec[0x00C]); 501 | calc_p_parity(rawsec); 502 | calc_q_parity(rawsec); 503 | *(uint32_t *)(&rawsec[0x00C]) = bakhed; 504 | } 505 | break; 506 | 507 | case SEC_MODE2_FORM2: 508 | // Use raw sectors and patch the result 509 | memcpy(rawsec+0x010, srcsec+0x010, 0x930-0x010); 510 | rawsec[0x00F] = 0x02; 511 | rawsec[0x010] = rawsec[0x014]; 512 | rawsec[0x011] = rawsec[0x015]; 513 | rawsec[0x012] = (rawsec[0x016] |= 0x20); 514 | rawsec[0x013] = rawsec[0x017]; 515 | if(*(uint32_t *)(&rawsec[0x92C]) != 0x00000000) { 516 | adjust_edc(rawsec+0x010, 0x91C); 517 | } 518 | break; 519 | 520 | default: 521 | assert(!"rip"); 522 | abort(); 523 | break; 524 | } 525 | } 526 | 527 | int find_dent(const char *fname) 528 | { 529 | for(int i = 0; i < dent_count; i++) { 530 | if(!strcmp(dent_list[i].loc_fname, fname)) { 531 | return i; 532 | } 533 | } 534 | 535 | return -1; 536 | } 537 | 538 | int assign_dent(const char *fname_in, dentmode_t dmode) 539 | { 540 | // See if we need to split the string 541 | char fname_buf[FNAME_MAX_LEN_LOC]; 542 | strncpy(fname_buf, fname_in, sizeof(fname_buf)); 543 | char *c_sep = strchr(fname_buf, '/'); 544 | int parent_idx = -1; 545 | if(c_sep != NULL) { 546 | for(;;) { 547 | char *newsep = strchr(c_sep+1, '/'); 548 | if(newsep == NULL) { 549 | break; 550 | } 551 | c_sep = newsep; 552 | } 553 | *c_sep = '\x00'; 554 | parent_idx = assign_dent(fname_buf, DENT_DIR); 555 | } else { 556 | c_sep = fname_buf-1; 557 | } 558 | 559 | // See if we have it already 560 | int ent_idx = find_dent(fname_in); 561 | if(ent_idx != -1) { 562 | return ent_idx; 563 | } 564 | 565 | // Allocate an entry 566 | ent_idx = (dent_count++); 567 | locdent_t *D = &dent_list[ent_idx]; 568 | 569 | if(ent_idx == 0) { // assuming '.' 570 | parent_idx = ent_idx; 571 | } 572 | 573 | // Fill it in 574 | strncpy(D->loc_fname, fname_in, sizeof(D->loc_fname)); 575 | strncpy(D->isodent.fname, c_sep+1, sizeof(D->isodent.fname)); 576 | for(int i = 0; D->isodent.fname[i] != '\x00'; i++) { 577 | D->isodent.fname[i] = toupper(D->isodent.fname[i]); 578 | } 579 | strncpy(D->dir_fname, D->isodent.fname, sizeof(D->dir_fname)); 580 | if(dmode != DENT_DIR) { 581 | strncat(D->isodent.fname, ";1", sizeof(D->isodent.fname)); 582 | } 583 | D->dmode = dmode; 584 | D->path_idx = (dmode != DENT_DIR ? -1 : dent_path_count++); 585 | D->sector = (dmode != DENT_DIR ? 0 : D->path_idx + 22); 586 | D->parent_dir = parent_idx; 587 | D->parent_path = (parent_idx == -1 ? -1 : dent_list[parent_idx].path_idx); 588 | printf("%d %d %d %d \"%s\" \"%s\"\n", ent_idx, dmode, parent_idx, D->parent_path, fname_in, c_sep+1); 589 | 590 | D->isodent.ts_year = 70; 591 | D->isodent.ts_month = 1; 592 | D->isodent.ts_day = 1; 593 | D->isodent.ts_hour = 0; 594 | D->isodent.ts_minute = 0; 595 | D->isodent.ts_second = 0; 596 | D->isodent.ts_timezone = 0; 597 | D->isodent.len_fi = strlen(D->isodent.fname); 598 | D->isodent.len_dr = 14+((33+D->isodent.len_fi+1)&~1); 599 | D->isodent.flags = (dmode == DENT_DIR ? 0x02 : 0x00); 600 | D->isodent.vsnum_le = TOLE16(0x0001); 601 | D->isodent.vsnum_be = TOBE16(0x0001); 602 | D->xadent.fattr = (dmode == DENT_RAW ? TOBE16(0x3D55) 603 | : dmode == DENT_DAT ? TOBE16(0x0D55) 604 | : TOBE16(0x8D55)); 605 | D->xadent.magic[0] = 'X'; 606 | D->xadent.magic[1] = 'A'; 607 | D->xadent.filenum = 0x00; // TODO: make use of this 608 | 609 | // Return! 610 | return ent_idx; 611 | } 612 | 613 | int main(int argc, char *argv[]) 614 | { 615 | init_tables(); 616 | 617 | if(argc <= 1) { 618 | printf("usage:\n\t%s manifest.txt\n", argv[0]); 619 | } 620 | 621 | // 622 | // Parse manifest 623 | // 624 | 625 | FILE *manifestfp = fopen(argv[1], "r"); 626 | #define LINEBUF_MAX 1024 627 | char linebuf[LINEBUF_MAX]; 628 | 629 | char *fname_bin = NULL; 630 | char *fname_cue = NULL; 631 | char *fname_lic = NULL; 632 | 633 | //assign_dent(".", DENT_DIR); 634 | 635 | for(;;) 636 | { 637 | // Get line 638 | char *ret_fgets = fgets(linebuf, sizeof(linebuf), manifestfp); 639 | if(ret_fgets == NULL) { 640 | assert(feof(manifestfp)); 641 | break; 642 | } 643 | 644 | // Strip comment/newlines 645 | char *c_comnl = strpbrk(linebuf, "\r\n#"); 646 | if(c_comnl != NULL) { 647 | *c_comnl = '\x00'; 648 | } 649 | 650 | // Strip leading whitespace 651 | size_t wsbeg = strspn(linebuf, " \t\r\n"); 652 | if(wsbeg != 0) { 653 | size_t l = strlen(linebuf); 654 | memmove(linebuf, linebuf+wsbeg, l-wsbeg+1); 655 | } 656 | 657 | // Skip empty lines 658 | if(linebuf[0] == '\x00') { 659 | continue; 660 | } 661 | 662 | // Do a split against '=' 663 | char *c_eq = strchr(linebuf, '='); 664 | if(c_eq == NULL) { 665 | printf("ERROR: expected '=' in line: \"%s\"\n", linebuf); 666 | return 1; 667 | } 668 | assert(*c_eq == '='); 669 | *c_eq = '\x00'; 670 | 671 | char *arg1 = c_eq+1; 672 | if(!strcmp(linebuf, "bin")) { 673 | assert(fname_bin == NULL); 674 | fname_bin = strdup(arg1); 675 | } else if(!strcmp(linebuf, "cue")) { 676 | assert(fname_cue == NULL); 677 | fname_cue = strdup(arg1); 678 | } else if(!strcmp(linebuf, "lic")) { 679 | assert(fname_lic == NULL); 680 | fname_lic = strdup(arg1); 681 | 682 | } else if(!strcmp(linebuf, "dat")) { 683 | assign_dent(arg1, DENT_DAT); 684 | } else if(!strcmp(linebuf, "raw")) { 685 | assign_dent(arg1, DENT_RAW); 686 | 687 | } else { 688 | printf("ERROR: unhandled: [%s] = [%s]\n", linebuf, arg1); 689 | return 1; 690 | } 691 | } 692 | 693 | // Close manifest 694 | fclose(manifestfp); 695 | 696 | // Sort directories in 1. parent node order, 2. alphabetical order 697 | { 698 | // Create identity mapping 699 | for(int i = 0; i < dent_count; i++) { 700 | dent_remap[i] = i; 701 | } 702 | 703 | // Find first non-root dir 704 | int i0 = 0; 705 | for(; i0 < dent_count; i0++) { 706 | locdent_t *D0 = &dent_list[dent_remap[i0]]; 707 | //if(D0->dmode != DENT_DIR) { continue; } 708 | if(!strcmp(D0->isodent.fname, ".")) { continue; } 709 | if(!strcmp(D0->isodent.fname, "..")) { continue; } 710 | break; 711 | } 712 | 713 | // Bubblesort directories 714 | for(; i0 < dent_count; i0++) { 715 | locdent_t *D0 = &dent_list[dent_remap[i0]]; 716 | //if(D0->dmode != DENT_DIR) { continue; } 717 | 718 | for(int i1 = i0+1; i1 < dent_count; i1++) { 719 | int tmp; 720 | locdent_t *D1 = &dent_list[dent_remap[i1]]; 721 | //if(D1->dmode != DENT_DIR) { continue; } 722 | if(D0->parent_dir > D1->parent_dir || 723 | (D0->parent_dir == D1->parent_dir && strcmp(D0->isodent.fname, D1->isodent.fname) > 0)) { 724 | //printf("swap: \"%s\" \"%s\"\n", D0->isodent.fname, D1->isodent.fname); 725 | memcpy(&tmp, &dent_remap[i0], sizeof(tmp)); 726 | memcpy(&dent_remap[i0], &dent_remap[i1], sizeof(tmp)); 727 | memcpy(&dent_remap[i1], &tmp, sizeof(tmp)); 728 | D0 = &dent_list[dent_remap[i0]]; 729 | } 730 | } 731 | 732 | printf("new dir: %d %d %d \"%s\"\n", i0, D0->path_idx, D0->sector, D0->isodent.fname); 733 | } 734 | } 735 | 736 | // Ensure that we are ready to make an image 737 | assert(fname_bin != NULL); 738 | assert(fname_cue != NULL); 739 | assert(fname_lic != NULL); 740 | 741 | printf("Building CD image...\n"); 742 | 743 | // Load licence file 744 | char *licence_buf = NULL; 745 | size_t licence_len = 0; 746 | load_whole_file(&licence_buf, &licence_len, fname_lic); 747 | assert(licence_len == 0x930*16); 748 | 749 | // Start producing bin file 750 | FILE *binfp = fopen(fname_bin, "w+b"); 751 | uint8_t secdata_out[0x930]; 752 | uint8_t secdata_in_data[0x800]; 753 | uint8_t secdata_in_raw[0x930]; 754 | assert(binfp != NULL); 755 | fwrite(licence_buf, licence_len, 1, binfp); 756 | sector_count = 22 + dent_path_count; 757 | 758 | // Generate path table 759 | // We have to do a little-endian ver and a big-endian ver 760 | for(int j = 0; j < dent_count; j++) { 761 | dent_path_remap[j] = -1; 762 | } 763 | 764 | int ptsize = 0; 765 | for(int i = 0; i < 4; i+=2) { 766 | ptsize = 0; 767 | fseek(binfp, 0x930*(18+i), SEEK_SET); 768 | memset(secdata_in_data, 0, sizeof(secdata_in_data)); 769 | 770 | // XXX: Does this require everything to be ordered by depth? 771 | // If so, this will need a rework. 772 | // FIXME: Yes, it does. 773 | 774 | int pent_idx = 0; 775 | for(int j = 0; j < dent_count; j++) { 776 | locdent_t *D = &dent_list[dent_remap[j]]; 777 | 778 | if(D->dmode != DENT_DIR) { continue; } 779 | 780 | /* 781 | 00h 1 Length of Directory Name (LEN_DI) (01h..08h for PSX) 782 | 01h 1 Extended Attribute Record Length (usually 00h) 783 | 02h 4 Directory Logical Block Number 784 | 06h 2 Parent Directory Number (0001h and up) 785 | 08h LEN_DI Directory Name (d-characters, d1-characters) (or 00h for Root) 786 | xxh 0..1 Padding Field (00h) (only if LEN_FI is odd) 787 | */ 788 | 789 | int len_di = strlen(D->dir_fname); 790 | //printf("%d %d \"%s\" \"%s\" %d\n" , D->path_idx, D->parent_path , D->loc_fname , D->dir_fname , len_di); 791 | secdata_in_data[ptsize++] = len_di; 792 | secdata_in_data[ptsize++] = 0x00; 793 | *(uint32_t *)(secdata_in_data+ptsize) = ( 794 | i == 0 795 | ? TOLE32(D->sector) 796 | : TOBE32(D->sector) 797 | ); ptsize += 4; 798 | 799 | if(pent_idx != 0) { 800 | assert(dent_path_remap[D->parent_dir] != -1); 801 | } 802 | dent_path_remap[dent_remap[j]] = pent_idx; 803 | 804 | *(uint16_t *)(secdata_in_data+ptsize) = ( 805 | i == 0 806 | ? TOLE16(dent_path_remap[D->parent_dir]+1) 807 | : TOBE16(dent_path_remap[D->parent_dir]+1) 808 | ); ptsize += 2; 809 | strncpy((char *)secdata_in_data+ptsize, D->dir_fname, len_di); 810 | if(D->dir_fname[0] == '.') { 811 | secdata_in_data[ptsize] = '\x00'; 812 | } 813 | ptsize += (len_di+1)&~1; 814 | assert(ptsize <= 0x800); 815 | pent_idx++; 816 | } 817 | 818 | encode_sector(secdata_out, secdata_in_data, (18+i), SEC_MODE2_FORM1, 0x89); 819 | fwrite(secdata_out, 0x930, 1, binfp); 820 | encode_sector(secdata_out, secdata_in_data, (19+i), SEC_MODE2_FORM1, 0x89); 821 | fwrite(secdata_out, 0x930, 1, binfp); 822 | } 823 | printf("ptsize = %d\n", ptsize); 824 | 825 | // Put files everywhere 826 | for(int i = 0; i < dent_count; i++) { 827 | locdent_t *D = &dent_list[i]; 828 | //if(D->dmode == DENT_DIR) { continue; } 829 | printf("file \"%s\"\n", D->loc_fname); 830 | 831 | switch(D->dmode) { 832 | case DENT_DAT: { 833 | char *dat_buf = NULL; 834 | size_t dat_len = 0; 835 | load_whole_file(&dat_buf, &dat_len, D->loc_fname); 836 | int dat_sectors = (dat_len+0x7FF)/0x800; 837 | 838 | D->sector = sector_count; 839 | D->isodent.dblk_le = TOLE32(D->sector); 840 | D->isodent.dblk_be = TOBE32(D->sector); 841 | D->isodent.dlen_le = TOLE32(dat_len); 842 | D->isodent.dlen_be = TOBE32(dat_len); 843 | sector_count += dat_sectors; 844 | 845 | //fseek(binfp, 0x930*(D->sector+j), SEEK_SET); 846 | fseek(binfp, 0x930*(D->sector), SEEK_SET); 847 | for(int j = 0; j < dat_sectors; j++) { 848 | memset(secdata_in_data, 0, sizeof(secdata_in_data)); 849 | memcpy(secdata_in_data, dat_buf+0x800*j, 850 | (j < dat_sectors-1 ? 0x800: dat_len-0x800*j)); 851 | encode_sector(secdata_out, secdata_in_data, (D->sector+j), SEC_MODE2_FORM1, (j+1 == dat_sectors ? 0x89 : 0x08)); 852 | fwrite(secdata_out, 0x930, 1, binfp); 853 | } 854 | 855 | free_whole_file(&dat_buf, &dat_len); 856 | } break; 857 | 858 | case DENT_RAW: { 859 | char *raw_buf = NULL; 860 | size_t raw_len = 0; 861 | load_whole_file(&raw_buf, &raw_len, D->loc_fname); 862 | int raw_sectors = (raw_len+0x92F)/0x930; 863 | int raw_normlen = raw_sectors*0x800; 864 | 865 | D->sector = sector_count; 866 | D->isodent.dblk_le = TOLE32(D->sector); 867 | D->isodent.dblk_be = TOBE32(D->sector); 868 | D->isodent.dlen_le = TOLE32(raw_normlen); 869 | D->isodent.dlen_be = TOBE32(raw_normlen); 870 | sector_count += raw_sectors; 871 | 872 | fseek(binfp, 0x930*(D->sector), SEEK_SET); 873 | for(int j = 0; j < raw_sectors; j++) { 874 | memset(secdata_in_raw, 0, sizeof(secdata_in_raw)); 875 | memcpy(secdata_in_raw, raw_buf+0x930*j, 876 | (j < raw_sectors-1 ? 0x930: raw_len-0x930*j)); 877 | //fseek(binfp, 0x930*(D->sector+j), SEEK_SET); 878 | encode_sector(secdata_out, secdata_in_raw, (D->sector+j), SEC_RAW, 0x00); 879 | fwrite(secdata_out, 0x930, 1, binfp); 880 | } 881 | 882 | free_whole_file(&raw_buf, &raw_len); 883 | } break; 884 | 885 | case DENT_DIR: 886 | // Do nothing 887 | D->isodent.dblk_le = TOLE32(D->sector); 888 | D->isodent.dblk_be = TOBE32(D->sector); 889 | D->isodent.dlen_le = TOLE32(0x800); 890 | D->isodent.dlen_be = TOBE32(0x800); 891 | break; 892 | 893 | default: 894 | assert(!"halp"); 895 | abort(); 896 | return 99; 897 | } 898 | } 899 | 900 | // Generate directories 901 | for(int i = 0; i < dent_count; i++) { 902 | locdent_t *D = &dent_list[i]; 903 | if(D->dmode != DENT_DIR) { continue; } 904 | fseek(binfp, 0x930*(22+D->path_idx), SEEK_SET); 905 | 906 | memset(secdata_in_data, 0, sizeof(secdata_in_data)); 907 | 908 | printf("Directory! %d %d %d %d \"%s\"\n", i, D->path_idx, D->sector, D->parent_dir, D->isodent.fname); 909 | uint8_t *p = secdata_in_data; 910 | 911 | // Generate main link 912 | memcpy(p, &D->isodent, sizeof(D->isodent)-FNAME_MAX_LEN_ISO); 913 | ((isodent_t *)p)->len_fi = 1; 914 | ((isodent_t *)p)->len_dr = 33+1+14; 915 | p += sizeof(D->isodent)-FNAME_MAX_LEN_ISO; 916 | *(p++) = '\x00'; 917 | memcpy(p, &D->xadent, sizeof(D->xadent)); 918 | p += sizeof(D->xadent); 919 | 920 | // Generate back link 921 | assert(D->parent_dir >= 0); 922 | locdent_t *E = &dent_list[D->parent_dir]; 923 | memcpy(p, &E->isodent, sizeof(E->isodent)-FNAME_MAX_LEN_ISO); 924 | ((isodent_t *)p)->len_fi = 1; 925 | ((isodent_t *)p)->len_dr = 33+1+14; 926 | p += sizeof(E->isodent)-FNAME_MAX_LEN_ISO; 927 | *(p++) = '\x01'; 928 | memcpy(p, &E->xadent, sizeof(E->xadent)); 929 | p += sizeof(E->xadent); 930 | 931 | // Generate file stuff 932 | for(int j = 0; j < dent_count; j++) { 933 | locdent_t *F = &dent_list[dent_remap[j]]; 934 | if(j == i) { continue; } 935 | if(F->parent_dir != i) { continue; } 936 | printf("- %d %d \"%s\"\n", j, F->sector, F->isodent.fname); 937 | memcpy(p, &F->isodent, sizeof(F->isodent)-FNAME_MAX_LEN_ISO+F->isodent.len_fi); 938 | //p += sizeof(F->isodent)-FNAME_MAX_LEN_ISO+((F->isodent.len_fi+1)&~1); 939 | p += F->isodent.len_dr-sizeof(F->xadent); 940 | memcpy(p, &F->xadent, sizeof(F->xadent)); 941 | p += sizeof(F->xadent); 942 | } 943 | 944 | // TODO! 945 | 946 | encode_sector(secdata_out, secdata_in_data, (22+D->path_idx), SEC_MODE2_FORM1, 0x89); 947 | fwrite(secdata_out, 0x930, 1, binfp); 948 | } 949 | 950 | printf("sector_count = %d\n", sector_count); 951 | 952 | // Generate PVD 953 | fseek(binfp, 0x930*16, SEEK_SET); 954 | pvd_t pvd = { 955 | .vdtype = 0x01, // 0x01 = PVD 956 | .magic1 = "CD001", // "CD001" 957 | .vdver = 0x01, // 0x01 958 | .iden_sys = "PLAYSTATION", 959 | .iden_vol = "", // max 8 chars for some reason 960 | .lbn_count_le = TOLE32(sector_count), 961 | .lbn_count_be = TOBE32(sector_count), 962 | .vset_size_le = TOLE16(0x0001), 963 | .vset_size_be = TOBE16(0x0001), 964 | .vset_seqn_le = TOLE16(0x0001), 965 | .vset_seqn_be = TOBE16(0x0001), 966 | .block_size_le = TOLE16(0x0800), 967 | .block_size_be = TOBE16(0x0800), 968 | .ptsize_size_le = TOLE16(ptsize), 969 | .ptsize_size_be = TOBE16(ptsize), 970 | .ptent1_le = TOLE32(18), 971 | .ptent2_le = TOLE32(19), 972 | .ptent3_be = TOBE32(20), 973 | .ptent4_be = TOBE32(21), 974 | .rootdir_record = { 975 | 34, 0, 976 | 22,0,0,0, 0,0,0,22, 977 | 0x00,0x08,0,0, 0,0,0x08,0x00, // length. do we patch this? 978 | 70,1,1,0,0,0,0, 979 | 0x02, 980 | 0,0, 981 | 1,0,0,1, 982 | 1,0x00, 983 | }, 984 | .iden_volset = "", 985 | .iden_publisher = "", 986 | .iden_datprep = "CHEN THREAD PSCD TOOLS", 987 | .iden_app = "PLAYSTATION", 988 | .fname_copyright = "", 989 | .fname_abstract = "", 990 | .fname_biblio = "", 991 | .ts_creation = "0000000000000000\x00", // "YYYYMMDDHHMMSSFF" + timezone 992 | .ts_modification = "0000000000000000\x00", 993 | .ts_expiration = "0000000000000000\x00", 994 | .ts_effective = "0000000000000000\x00", 995 | .fsver = 0x01, 996 | .xamagic1 = "CD-XA001", 997 | }; 998 | encode_sector(secdata_out, (uint8_t *)&pvd, 16, SEC_MODE2_FORM1, 0x09); 999 | fwrite(secdata_out, 0x930, 1, binfp); 1000 | 1001 | // Generate VDST 1002 | vdst_t vdst = { 1003 | .vdtype = 0xFF, // 0xFF = terminator 1004 | .magic1 = "CD001", // "CD001" 1005 | .vdver = 0x01, // 0x01 1006 | }; 1007 | encode_sector(secdata_out, (uint8_t *)&vdst, 17, SEC_MODE2_FORM1, 0x89); 1008 | fwrite(secdata_out, 0x930, 1, binfp); 1009 | 1010 | // Close bin file 1011 | fclose(binfp); 1012 | 1013 | // Do cue file 1014 | { 1015 | // Open the file 1016 | FILE *cuefp = fopen(fname_cue, "wb"); 1017 | assert(cuefp != NULL); 1018 | 1019 | // Get the base filename 1020 | char *fnbase = fname_bin; 1021 | for(;;) { 1022 | char *c_psep = strchr(fnbase, '/'); 1023 | if(c_psep == NULL) { break; } 1024 | fnbase = c_psep+1; 1025 | } 1026 | 1027 | // Dump a header 1028 | fprintf(cuefp, "FILE \"%s\" BINARY\n", fnbase); 1029 | fprintf(cuefp, " TRACK 01 MODE2/2352\n"); 1030 | fprintf(cuefp, " INDEX 01 00:00:00\n"); 1031 | 1032 | // We are done here. 1033 | fclose(cuefp); 1034 | } 1035 | 1036 | return 0; 1037 | } 1038 | 1039 | --------------------------------------------------------------------------------