From 8a3053c0279a7f0a18e1027e248f2fd7409d0470 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Thu, 8 May 2025 20:27:52 +0200 Subject: [PATCH 01/23] 2.0.0 Pre alpha Code --- .gitmodules | 6 +- .vscode/launch.json | 21 ++ .vscode/settings.json | 3 +- CMakeLists.txt | 159 +++++++++ D7-Menu-Core | 1 - Makefile | 309 ----------------- README.md | 14 +- ctrff | 1 + palladium | 2 +- romfs/fonts/ComicNeue.ttf | Bin 0 -> 53384 bytes romfs/license/ComicNeue.txt | 93 +++++ source/bcstm.hpp | 87 ----- source/{bcstm.cpp => bcstm/bcstmv2.cpp} | 195 ++++++----- source/bcstm/bcstmv2.hpp | 129 +++++++ source/bcstm_ctrl.hpp | 32 ++ source/bcstm_player.cpp | 193 +++++++++++ source/bcstm_player.hpp | 44 +++ source/common.cpp | 80 ----- source/common.hpp | 34 -- source/config.cpp | 56 --- source/config.hpp | 68 ---- source/cursor.hpp | 66 ++++ source/filebrowser.cpp | 169 +++++++++ source/filebrowser.hpp | 37 ++ source/flex/container.hpp | 16 + romfs/gfx/.gitkeep => source/flex/flex.cpp | 0 source/flex/flex.hpp | 77 +++++ source/flex/objects.cpp | 29 ++ source/flex/objects.hpp | 169 +++++++++ source/inspector_view.cpp | 381 +++++++++++++++++++++ source/inspector_view.hpp | 39 +++ source/main.cpp | 181 ++++++++-- source/pd_ctr_ext.cpp | 132 +++++++ source/pd_ctr_ext.hpp | 26 ++ source/scenes/Filemanager.cpp | 127 ------- source/scenes/Filemanager.hpp | 23 -- source/scenes/MainMenu.cpp | 89 ----- source/scenes/MainMenu.hpp | 18 - source/scenes/Settings.cpp | 188 ---------- source/scenes/Settings.hpp | 24 -- source/scenes/Titles.cpp | 64 ---- source/scenes/Titles.hpp | 16 - source/scenes/scenes.hpp | 6 - source/stagemgr.cpp | 3 + source/stagemgr.hpp | 39 +++ source/stages.hpp | 7 + 46 files changed, 2135 insertions(+), 1318 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 CMakeLists.txt delete mode 160000 D7-Menu-Core delete mode 100644 Makefile create mode 160000 ctrff create mode 100644 romfs/fonts/ComicNeue.ttf create mode 100644 romfs/license/ComicNeue.txt delete mode 100644 source/bcstm.hpp rename source/{bcstm.cpp => bcstm/bcstmv2.cpp} (53%) create mode 100644 source/bcstm/bcstmv2.hpp create mode 100644 source/bcstm_ctrl.hpp create mode 100644 source/bcstm_player.cpp create mode 100644 source/bcstm_player.hpp delete mode 100644 source/common.cpp delete mode 100644 source/common.hpp delete mode 100644 source/config.cpp delete mode 100644 source/config.hpp create mode 100644 source/cursor.hpp create mode 100644 source/filebrowser.cpp create mode 100644 source/filebrowser.hpp create mode 100644 source/flex/container.hpp rename romfs/gfx/.gitkeep => source/flex/flex.cpp (100%) create mode 100644 source/flex/flex.hpp create mode 100644 source/flex/objects.cpp create mode 100644 source/flex/objects.hpp create mode 100644 source/inspector_view.cpp create mode 100644 source/inspector_view.hpp create mode 100644 source/pd_ctr_ext.cpp create mode 100644 source/pd_ctr_ext.hpp delete mode 100644 source/scenes/Filemanager.cpp delete mode 100644 source/scenes/Filemanager.hpp delete mode 100644 source/scenes/MainMenu.cpp delete mode 100644 source/scenes/MainMenu.hpp delete mode 100644 source/scenes/Settings.cpp delete mode 100644 source/scenes/Settings.hpp delete mode 100644 source/scenes/Titles.cpp delete mode 100644 source/scenes/Titles.hpp delete mode 100644 source/scenes/scenes.hpp create mode 100644 source/stagemgr.cpp create mode 100644 source/stagemgr.hpp create mode 100644 source/stages.hpp diff --git a/.gitmodules b/.gitmodules index 8718e5f..34a96e3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "D7-Menu-Core"] - path = D7-Menu-Core - url = https://github.com/NPI-D7/D7-Menu-Core.git [submodule "palladium"] path = palladium url = https://github.com/tobid7/palladium +[submodule "ctrff"] + path = ctrff + url = https://dev.npid7.de/tobid7/ctrff-pub diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8234252 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "name": "3DS GDB", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/BCSTM-Player.elf", + "miDebuggerServerAddress": "localhost:4003", + "miDebuggerPath": "C:\\devkitPro\\devkitARM\\bin\\arm-none-eabi-gdb.exe", + "cwd": "${workspaceFolder}", + "externalConsole": true, + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 17d6863..b8ec699 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -98,6 +98,7 @@ "xlocmes": "cpp", "xlocmon": "cpp", "xloctime": "cpp", - "xtree": "cpp" + "xtree": "cpp", + "any": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9cb9f9e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,159 @@ +cmake_minimum_required(VERSION 3.18) + +# Setup Toolchain if not specified +# Could propably avoided by using arm-none-eabi-cmake +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + if(DEFINED ENV{DEVKITPRO}) + set(CMAKE_TOOLCHAIN_FILE "$ENV{DEVKITPRO}/cmake/3DS.cmake" CACHE PATH "toolchain file") + else() + message(FATAL_ERROR "Please define DEVKITPRO to point to your SDK path!") + endif() +endif() + +find_program(MAKEROM makerom) +find_program(BANNERTOOL bannertool) + +execute_process( + COMMAND git rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_SHORT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# Set Result specific Data +set(APP_NAME "BCSTM-Player") +set(APP_DESC "BCSTM Player for the 3ds") +set(APP_AUTHOR "tobid7") +set(APP_ICON "${CMAKE_SOURCE_DIR}/app/icon.png") +set(APP_ROMFS "${CMAKE_SOURCE_DIR}/romfs") + +set(APP_BANNER "${CMAKE_SOURCE_DIR}/app/banner.png") +set(APP_BANNERAUDIO "${CMAKE_SOURCE_DIR}/app/BannerAudio.wav") +set(APP_RSF "${CMAKE_SOURCE_DIR}/app/build-cia.rsf") +set(APP_LOGO "${CMAKE_SOURCE_DIR}/app/splash.lz") + +# Set 3ds IP for make send +set(3DS_IP "192.168.2.224" CACHE STRING "3ds IP (make send)") + +# Set Project +project(BCSTM-Player LANGUAGES C CXX VERSION 2.0.0) + +# Enable Compile Command Export +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Force C++ 20 +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) + +# Set Special C and CXX flags +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-psabi -O3") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} -fno-rtti") + +add_subdirectory(palladium) +add_subdirectory(palladium/backends/3ds) +add_subdirectory(ctrff) + +# Set Executable and its sources +add_executable(BCSTM-Player + source/main.cpp + source/pd_ctr_ext.cpp + source/filebrowser.cpp + source/inspector_view.cpp + source/stagemgr.cpp + #ource/app.cpp + #source/config.cpp + + source/bcstm/bcstmv2.cpp + #source/bcstm_player.cpp + + source/flex/objects.cpp +) + +# Set dependencies, include dirs and definitions +target_link_libraries(BCSTM-Player PUBLIC m ctrff pd-backend-3ds palladium citro3d ctru) +target_include_directories(BCSTM-Player PUBLIC + source + ${DEVKITPRO}/portlibs/3ds/include +) +target_compile_definitions(BCSTM-Player PUBLIC + -D_GNU_SOURCE=1 + -DVERSION="${PROJECT_VERSION}" + -DGIT_COMMIT="${GIT_SHORT_HASH}" +) + +# Command to send to console (make send) +add_custom_target( + send + COMMAND 3dslink -a "${3DS_IP}" "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.3dsx" +) + +## Graphics +file(GLOB TEX3DS_DEF "${CMAKE_SOURCE_DIR}/gfx/*.t3s") + +foreach(FILE_ ${TEX3DS_DEF}) + get_filename_component(TNAME ${FILE_} NAME_WE) + set(T3X_RES "${CMAKE_SOURCE_DIR}/romfs/gfx/${TNAME}.t3x") + add_custom_command( + OUTPUT ${T3X_RES} + COMMAND tex3ds.exe -i ${FILE_} -o ${T3X_RES} + DEPENDS ${FILE_} + COMMENT "Converting ${TNAME}.t3x" + VERBATIM + ) + list(APPEND T3X ${T3X_RES}) +endforeach() + +add_custom_target(create_resources ALL DEPENDS ${T3X}) + +# Generate 3DSX +ctr_generate_smdh( + ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh + NAME "${APP_NAME}" + DESCRIPTION "${APP_DESC}" + AUTHOR "${APP_AUTHOR}" + ICON "${APP_ICON}" +) +ctr_create_3dsx( + BCSTM-Player + OUTPUT "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.3dsx" + SMDH "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh" + ROMFS "${APP_ROMFS}" + DEPENDS create_resources +) + + +add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr + COMMAND ${BANNERTOOL} makebanner + -i ${APP_BANNER} + -a ${APP_BANNERAUDIO} + -o ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr + DEPENDS ${APP_BANNER} ${APP_BANNERAUDIO} + COMMENT "Creating Banner for cia file..." + VERBATIM +) + +add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.cia + COMMAND ${MAKEROM} + -f cia -target t -exefslogo + -o "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.cia" + -elf "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.elf" + -rsf ${APP_RSF} + -banner "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr" + -icon "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh" + -logo ${APP_LOGO} -DAPP_ROMFS="${APP_ROMFS}" + -major 1 -minor 0 -micro 0 + -DAPP_VERSION_MAJOR=1 + DEPENDS ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.elf ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh + COMMENT "Creating Cia file..." + VERBATIM +) + +add_custom_target(make_banner ALL + DEPENDS ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr +) + +add_custom_target(make_cia ALL + DEPENDS ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.elf ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.cia +) diff --git a/D7-Menu-Core b/D7-Menu-Core deleted file mode 160000 index 9fc05f4..0000000 --- a/D7-Menu-Core +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9fc05f4b5e34ac4d5c45e3a40396b01ee8bd208a diff --git a/Makefile b/Makefile deleted file mode 100644 index 4e00db7..0000000 --- a/Makefile +++ /dev/null @@ -1,309 +0,0 @@ -#--------------------------------------------------------------------------------- -.SUFFIXES: -#nicetest -#--------------------------------------------------------------------------------- - -ifeq ($(strip $(DEVKITARM)),) -$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") -endif - -TOPDIR ?= $(CURDIR) -include $(DEVKITARM)/3ds_rules - -#--------------------------------------------------------------------------------- -# TARGET is the name of the output -# BUILD is the directory where object files & intermediate files will be placed -# SOURCES is a list of directories containing source code -# DATA is a list of directories containing data files -# INCLUDES is a list of directories containing header files -# GRAPHICS is a list of directories containing graphics files -# GFXBUILD is the directory where converted graphics files will be placed -# If set to $(BUILD), it will statically link in the converted -# files as if they were data files. -# -# NO_SMDH: if set to anything, no SMDH file is generated. -# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) -# APP_TITLE is the name of the app stored in the SMDH file (Optional) -# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) -# APP_AUTHOR is the author of the app stored in the SMDH file (Optional) -# ICON is the filename of the icon (.png), relative to the project folder. -# If not set, it attempts to use one of the following (in this order): -# - .png -# - icon.png -# - /default_icon.png - -#--------------------------------------------------------------------------------- -# External tools -#--------------------------------------------------------------------------------- -ifeq ($(OS),Windows_NT) -MAKEROM ?= C:/devkitpro/tools/bin/makerom.exe -BANNERTOOL ?= C:/devkitpro/tools/bin/bannertool.exe - -else -MAKEROM ?= makerom -BANNERTOOL ?= bannertool - -endif - -# If on a tagged commit, use the tag instead of the commit -ifneq ($(shell echo $(shell git tag -l --points-at HEAD) | head -c 1),) -GIT_VER := $(shell git tag -l --points-at HEAD) -else -GIT_VER := $(shell git rev-parse --short HEAD) -endif - -#--------------------------------------------------------------------------------- -# Version number -#--------------------------------------------------------------------------------- - -VERSION_MAJOR := 2 -VERSION_MINOR := 0 -VERSION_MICRO := 0 - -VERSION_STRING := $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_MICRO) - -#--------------------------------------------------------------------------------- -TARGET := BCSTM-Player -BUILD := build -SOURCES := source palladium/source palladium/source/base D7-Menu-Core source/scenes -DATA := data -INCLUDES := source palladium/include D7-Menu-Core source/scenes -GRAPHICS := gfx -#GFXBUILD := $(BUILD) -ROMFS := romfs -GFXBUILD := $(ROMFS)/gfx -APP_AUTHOR := NPI-D7 -APP_DESCRIPTION := BCSTM MusicPlayer for the 3ds. -ICON := app/icon.png -BNR_IMAGE := app/banner.png -BNR_AUDIO := app/BannerAudio.wav -RSF_FILE := app/build-cia.rsf - -#--------------------------------------------------------------------------------- -IP := 0.0.0.0 -#--------------------------------------------------------------------------------- -# options for code generation -#--------------------------------------------------------------------------------- -ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft - -CFLAGS := -g -Wall -Wno-psabi -O2 -mword-relocations \ - -DN_STRING=\"$(GIT_VER)\" \ - -DV_STRING=\"$(VERSION_STRING)\" \ - -fomit-frame-pointer -ffunction-sections \ - $(ARCH) - -CFLAGS += $(INCLUDE) -D__3DS__ -D_GNU_SOURCE=1 - -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++20 - -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) - -LIBS := -lstdc++ -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lz -lm -lcitro2d -lcitro3d -lctru - -#--------------------------------------------------------------------------------- -# list of directories containing libraries, this must be the top level containing -# include and lib -#--------------------------------------------------------------------------------- -LIBDIRS := $(PORTLIBS) $(CTRULIB) - - -#--------------------------------------------------------------------------------- -# no real need to edit anything past this point unless you need to add additional -# rules for different file extensions -#--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) -#--------------------------------------------------------------------------------- - -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) - -export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) -CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) -SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) -PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) -SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) -GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s))) -BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) - -#--------------------------------------------------------------------------------- -# use CXX for linking C++ projects, CC for standard C -#--------------------------------------------------------------------------------- -ifeq ($(strip $(CPPFILES)),) -#--------------------------------------------------------------------------------- - export LD := $(CC) -#--------------------------------------------------------------------------------- -else -#--------------------------------------------------------------------------------- - export LD := $(CXX) -#--------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------- - -#--------------------------------------------------------------------------------- -ifeq ($(GFXBUILD),$(BUILD)) -#--------------------------------------------------------------------------------- -export T3XFILES := $(GFXFILES:.t3s=.t3x) -#--------------------------------------------------------------------------------- -else -#--------------------------------------------------------------------------------- -export ROMFS_T3XFILES := $(patsubst %.t3s, $(GFXBUILD)/%.t3x, $(GFXFILES)) -export T3XHFILES := $(patsubst %.t3s, $(BUILD)/%.h, $(GFXFILES)) -#--------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------- - -export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) - -export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \ - $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) - -export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) - -export HFILES := $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) \ - $(addsuffix .h,$(subst .,_,$(BINFILES))) - -export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ - $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ - -I$(CURDIR)/$(BUILD) - -export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) - -export _3DSXDEPS := $(if $(NO_SMDH),,$(OUTPUT).smdh) - -ifeq ($(strip $(ICON)),) - icons := $(wildcard *.png) - ifneq (,$(findstring $(TARGET).png,$(icons))) - export APP_ICON := $(TOPDIR)/$(TARGET).png - else - ifneq (,$(findstring icon.png,$(icons))) - export APP_ICON := $(TOPDIR)/icon.png - endif - endif -else - export APP_ICON := $(TOPDIR)/$(ICON) -endif - -ifeq ($(strip $(NO_SMDH)),) - export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh -endif - -ifneq ($(ROMFS),) - export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) -endif - -.PHONY: all clean - -#--------------------------------------------------------------------------------- -all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES) - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -#------------------------------------------------------------------------------ -clean: - @echo clean ... - @rm -fr $(BUILD) $(TARGET).elf $(TARGET).3dsx $(TARGET).cia $(TARGET).smdh app/*.bin - @rm -fr $(OUTDIR) - -#--------------------------------------------------------------------------------- -send: - @3dslink -a $(IP) $(TARGET).3dsx -#--------------------------------------------------------------------------------- -run: - @flatpak run org.citra_emu.citra $(TARGET).3dsx -#--------------------------------------------------------------------------------- -andsend: - @make all - @3dslink -a $(IP) $(TARGET).3dsx -#--------------------------------------------------------------------------------- -cia: $(BUILD) - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile cia - -#--------------------------------------------------------------------------------- -3dsx: $(BUILD) - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 3dsx - -#--------------------------------------------------------------------------------- -$(GFXBUILD)/%.t3x $(BUILD)/%.h : %.t3s -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - $(DEVKITPRO)/tools/bin/tex3ds -i $< -H $(BUILD)/$*.h -d $(DEPSDIR)/$*.d -o $(GFXBUILD)/$*.t3x - -#--------------------------------------------------------------------------------- -$(BUILD): - @[ -d $@ ] || mkdir -p $@ - -#--------------------------------------------------------------------------------- -else - -#--------------------------------------------------------------------------------- -# main targets -#--------------------------------------------------------------------------------- -all: $(OUTPUT).cia $(OUTPUT).elf $(OUTPUT).3dsx - -$(OUTPUT).elf : $(OFILES) - -$(OUTPUT).cia : $(OUTPUT).elf $(OUTPUT).smdh - $(BANNERTOOL) makebanner -i "../app/banner.png" -a "../app/BannerAudio.wav" -o "../app/banner.bin" - - $(BANNERTOOL) makesmdh -i "../app/icon.png" -s "$(TARGET)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -o "../app/icon.bin" - - $(MAKEROM) -f cia -target t -exefslogo -o "../BCSTM-Player.cia" -elf "../BCSTM-Player.elf" -rsf "../app/build-cia.rsf" -banner "../app/banner.bin" -icon "../app/icon.bin" -logo "../app/splash.lz" -DAPP_ROMFS="$(TOPDIR)/$(ROMFS)" -major $(VERSION_MAJOR) -minor $(VERSION_MINOR) -micro $(VERSION_MICRO) -DAPP_VERSION_MAJOR="$(VERSION_MAJOR)" -#--------------------------------------------------------------------------------- -# you need a rule like this for each extension you use as binary data -#--------------------------------------------------------------------------------- -%.bin.o %_bin.h : %.bin -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - -#--------------------------------------------------------------------------------- -.PRECIOUS : %.t3x -#--------------------------------------------------------------------------------- -%.t3x.o %_t3x.h : %.t3x -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - -#--------------------------------------------------------------------------------- -# rules for assembling GPU shaders -#--------------------------------------------------------------------------------- -define shader-as - $(eval CURBIN := $*.shbin) - $(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d) - echo "$(CURBIN).o: $< $1" > $(DEPSFILE) - echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h - echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h - echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h - picasso -o $(CURBIN) $1 - bin2s $(CURBIN) | $(AS) -o $*.shbin.o -endef - -%.shbin.o %_shbin.h : %.v.pica %.g.pica - @echo $(notdir $^) - @$(call shader-as,$^) - -%.shbin.o %_shbin.h : %.v.pica - @echo $(notdir $<) - @$(call shader-as,$<) - -%.shbin.o %_shbin.h : %.shlist - @echo $(notdir $<) - @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)$(file))) - -#--------------------------------------------------------------------------------- -%.t3x %.h : %.t3s -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @tex3ds -i $< -H $*.h -d $*.d -o $*.t3x - --include $(DEPSDIR)/*.d - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/README.md b/README.md index 507c92e..cfea097 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ -# BCSTM-Player +# BCSTM-Player License: GPLv3   GitHub repo size +### if you find any bug, report it in an issue + +## Todo - ### if you find any bug, report it in an issue! -# Todo: -- support more channels - mount romfs from installed Titles / Gamecard using D7-Menu-Core. (works in citra but not on real hardware for some reason) -# Nightly builds +## Credits - +- [tobid7](https://github.com/tobid7): Lead Developer, author of palladium, ctrff +- [devkitpro](https://github.com/devkitpro): libctru, citro3d +- [3dbrew](https://www.3dbrew.org/wiki/BCSTM): BCSTM Documentation diff --git a/ctrff b/ctrff new file mode 160000 index 0000000..9969987 --- /dev/null +++ b/ctrff @@ -0,0 +1 @@ +Subproject commit 996998785cf10394338f7a54ce54e324e1229e61 diff --git a/palladium b/palladium index eac36bc..f75f706 160000 --- a/palladium +++ b/palladium @@ -1 +1 @@ -Subproject commit eac36bcc6e2eab515cb65a6918b832f8b33d6e04 +Subproject commit f75f7067ffe44ffafc852691b1bb56867842bb6f diff --git a/romfs/fonts/ComicNeue.ttf b/romfs/fonts/ComicNeue.ttf new file mode 100644 index 0000000000000000000000000000000000000000..dc9a6d5522364181c258abf8b2b074185012d396 GIT binary patch literal 53384 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 literal 0 HcmV?d00001 diff --git a/romfs/license/ComicNeue.txt b/romfs/license/ComicNeue.txt new file mode 100644 index 0000000..80062cf --- /dev/null +++ b/romfs/license/ComicNeue.txt @@ -0,0 +1,93 @@ +Copyright 2014 The Comic Neue Project Authors (https://github.com/crozynski/comicneue) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/source/bcstm.hpp b/source/bcstm.hpp deleted file mode 100644 index 9473d20..0000000 --- a/source/bcstm.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include -#include - -namespace D7 { -class BCSTM { - public: - BCSTM() {} - ~BCSTM() { Stop(); } - - bool LoadFile(const std::string& path); - void Update(); - void Play(); - void Pause(); - void Stop(); - - inline bool IsLooping() { return this->is_looping; } - inline bool IsLoaded() { return this->is_loaded; } - inline unsigned int GetLoopStart() { return this->loop_start; } - inline unsigned int GetLoopEnd() { return this->loop_end; } - inline unsigned int GetChannelCount() { return this->channel_count; } - inline unsigned int GetTotal() { return this->num_blocks; } - inline unsigned int GetCurrent() { return this->current_block; } - inline unsigned int GetSampleRate() { return this->sample_rate; } - inline std::string GetErrorMessage() { return this->err_msg; } - - private: - unsigned int read32(); - unsigned short read16(); - unsigned char read8(); - bool file_advance(unsigned long long bytes); - void stream(); - void fill_buffers(); - - enum RefType : uint16_t { - ByteTable = 0x0100, - ReferenceTable = 0x0101, - SampleData = 0x1F00, - DSPADPCMInfo = 0x0300, - InfoBlock = 0x4000, - SeekBlock = 0x4001, - DataBlock = 0x4002, - StreamInfo = 0x4100, - TrackInfo = 0x4101, - ChannelInfo = 0x4102, - }; - - static const int buffer_count = 20; - - std::string err_msg = ""; - - std::fstream file; - unsigned int current_time = 0; - unsigned int last_time = 0; - - bool is_loaded = false; - bool is_paused = false; - bool is_looping = false; - bool is_streaming = false; - bool is_little_endian; - - unsigned int channel_count = 0; - unsigned int sample_rate = 0; - - unsigned int loop_start = 0; - unsigned int loop_end = 0; - unsigned int num_blocks = 0; - unsigned int block_size = 0; - unsigned int block_samples = 0; - unsigned int last_block_size = 0; - unsigned int last_block_samples = 0; - unsigned short adpcm_coefs[2][16]; - - unsigned int current_block = 0; - unsigned int info_offset = 0; - unsigned int data_offset = 0; - - unsigned int active_channels = 0; - unsigned short channel[2]; - - ndspWaveBuf wave_buf[2][buffer_count]; - ndspAdpcmData adpcm_data[2][2]; - std::vector> - buffer_data[2][buffer_count]; -}; -} // namespace D7 \ No newline at end of file diff --git a/source/bcstm.cpp b/source/bcstm/bcstmv2.cpp similarity index 53% rename from source/bcstm.cpp rename to source/bcstm/bcstmv2.cpp index f5101c6..38b179f 100644 --- a/source/bcstm.cpp +++ b/source/bcstm/bcstmv2.cpp @@ -1,97 +1,101 @@ #include -#include +#include -bool D7::BCSTM::LoadFile(const std::string &path) { +bool D7::BCSTM2::LoadFile(const std::string &path) { Stop(); - file.open(path, std::ios::in | std::ios::binary); - if (!file) { - err_msg = "Unable to load File!"; + pFile.open(path, std::ios::in | std::ios::binary); + if (!pFile) { + std::cout << "BCSTM [ERROR]: Unable to load File!" << std::endl; return false; } + pBigEndian = false; is_little_endian = true; // default to true - auto magic = read32(); - if (!(is_little_endian = (read16() == 0xFEFF))) { + auto magic = Read(); + if ((pBigEndian = (Read() != 0xFEFF))) { magic = htonl(magic); } if (magic != 0x4D545343) { // CSTM - file.close(); - err_msg = "File is invalid!"; + pFile.close(); + std::cout << "BCSTM [ERROR]: File is invalid!" << std::endl; return false; } - file.seekg(0x10); - auto sbc = read16(); - read16(); + pFile.seekg(0x10); + auto sbc = Read(); + Read(); for (unsigned short i = 0; i < sbc; i++) { - auto sec = read16(); - read16(); // padding - auto off = read32(); - read32(); // size + auto sec = Read(); + Read(); // padding + auto off = Read(); + Read(); // size if (sec == InfoBlock) info_offset = off; else if (sec == DataBlock) data_offset = off; } if (!info_offset || !data_offset) { - err_msg = "Data/Info Section not found!"; + std::cout << "BCSTM [ERROR]: Data/Info Section not found" << std::endl; return false; } - file.seekg((info_offset + 0x20)); - if (read8() != 2) { - err_msg = "Unknown Error!"; + pFile.seekg((info_offset + 0x20)); + if (Read() != 2) { + std::cout << "BCSTM [ERROR]: Unknown Error!" << std::endl; return false; } - is_looping = read8(); - channel_count = read8(); - if (channel_count > 2) { - err_msg = "File has not 2 channels!"; + is_looping = Read(); + channel_count = Read(); + /*if (channel_count > 2) { + std::cout << "BCSTM [ERROR]: File has not 2 channels!" << std::endl; return false; - } - file.seekg((info_offset + 0x24)); - sample_rate = read32(); - auto _loop_pos = read32(); - auto _loop_end = read32(); - num_blocks = read32(); - block_size = read32(); - block_samples = read32(); - read32(); // last block used bytes - last_block_samples = read32(); - last_block_size = read32(); + }*/ + pFile.seekg((info_offset + 0x24)); + sample_rate = Read(); + auto _loop_pos = Read(); + auto _loop_end = Read(); + num_blocks = Read(); + block_size = Read(); + block_samples = Read(); + Read(); // last block used bytes + last_block_samples = Read(); + last_block_size = Read(); loop_start = _loop_pos / block_samples; loop_end = (_loop_end % block_samples ? num_blocks : _loop_end / block_samples); - while (read32() != ChannelInfo) { + while (Read() != ChannelInfo) { // Find Channel Info } - file_advance(read32() + channel_count * 8 - 12); - + std::ofstream off("test.dump.txt"); + off << pFile.tellg() << std::endl; + file_advance(Read() + channel_count * 8 - 12); + off << pFile.tellg() << std::endl; + off.close(); // get adpcm data for (unsigned int i = 0; i < channel_count; i++) { - file.read(reinterpret_cast(adpcm_coefs[i]), - sizeof(unsigned short) * 16); + pFile.read(reinterpret_cast(adpcm_coefs[i]), + sizeof(unsigned short) * 16); // beginning context - file.read(reinterpret_cast(&adpcm_data[i][0]), - sizeof(ndspAdpcmData)); + pFile.read(reinterpret_cast(&adpcm_data[i][0]), + sizeof(ndspAdpcmData)); // loop context - file.read(reinterpret_cast(&adpcm_data[i][1]), - sizeof(ndspAdpcmData)); + pFile.read(reinterpret_cast(&adpcm_data[i][1]), + sizeof(ndspAdpcmData)); // skip padding - read16(); + Read(); } - file.seekg((data_offset + 0x20)); + pFile.seekg((data_offset + 0x20)); is_loaded = true; return true; } -void D7::BCSTM::Update() { this->stream(); } +void D7::BCSTM2::Update() { this->stream(); } -void D7::BCSTM::Play() { +void D7::BCSTM2::Play() { if (is_paused) { for (unsigned int i = 0; i < channel_count; i++) { ndspChnSetPaused(channel[i], false); @@ -107,45 +111,60 @@ void D7::BCSTM::Play() { channel[i]++; } if (channel[i] == 24) { - err_msg = "Current chennel equals 24!"; + std::cout << "BCSTM [ERROR]: Current chennel equals 24!" << std::endl; return; } active_channels |= 1 << channel[i]; ndspChnWaveBufClear(channel[i]); } - static float mix[16]; + static float mix[12]; ndspChnSetFormat(channel[i], NDSP_FORMAT_ADPCM | NDSP_3D_SURROUND_PREPROCESSED); ndspChnSetRate(channel[i], sample_rate); + /** Make sure they are 0 */ + for (int j = 0; j < 12; j++) { + mix[j] = 0.f; + } + /** Leave 1 and 2 Channels as they are */ if (channel_count == 1) { mix[0] = mix[1] = 0.5f; } else if (channel_count == 2) { if (i == 0) { mix[0] = 0.8f; - mix[1] = 0.0f; mix[2] = 0.2f; - mix[3] = 0.0f; } else { - mix[0] = 0.0f; mix[1] = 0.8f; - mix[2] = 0.0f; mix[3] = 0.2f; } + /** Dont know if this is the finished code here */ + } else if (channel_count == 4) { + mix[i] = 0.8f; + if (i > 0) mix[i - 1] += 0.2f; + if (i < 3) mix[i + 1] += 0.2f; + } else if (channel_count == 6) { + mix[i] = 0.7f; + if (i > 0) mix[i - 1] += 0.15f; + if (i < 5) mix[i + 1] += 0.15f; + } else if (channel_count == 8) { + mix[i] = 0.6f; + if (i > 0) mix[i - 1] += 0.1f; + if (i < 7) mix[i + 1] += 0.1f; } + ndspChnSetMix(channel[i], mix); ndspChnSetAdpcmCoefs(channel[i], adpcm_coefs[i]); for (unsigned int j = 0; j < buffer_count; j++) { memset(&wave_buf[i][j], 0, sizeof(ndspWaveBuf)); wave_buf[i][j].status = NDSP_WBUF_DONE; - buffer_data[i][j].resize(block_size); + buffer_data[i][j].Resize(block_size); } } is_streaming = true; } -void D7::BCSTM::Pause() { +void D7::BCSTM2::Pause() { if (!is_streaming) return; is_paused = true; for (unsigned int i = 0; i < channel_count; i++) { @@ -153,8 +172,13 @@ void D7::BCSTM::Pause() { } } -void D7::BCSTM::Stop() { - if (file) file.close(); +void D7::BCSTM2::Stop() { + if (pFile) pFile.close(); + /** Move this loop up (logical issue in < v2.0.0) */ + for (unsigned int i = 0; i < channel_count; i++) { + ndspChnWaveBufClear(channel[i]); + active_channels &= ~(1 << channel[i]); + } channel_count = 0; sample_rate = 0; loop_start = 0; @@ -170,17 +194,13 @@ void D7::BCSTM::Stop() { active_channels = 0; is_paused = false; is_looping = false; - err_msg = "None"; + std::cout << "BCSTM [ERROR]: None"; is_loaded = false; if (!is_streaming) return; is_streaming = false; - for (unsigned int i = 0; i < channel_count; i++) { - ndspChnWaveBufClear(channel[i]); - active_channels &= ~(1 << channel[i]); - } } -void D7::BCSTM::stream() { +void D7::BCSTM2::stream() { current_time = svcGetSystemTick(); if (current_time - last_time >= 100000000 && is_loaded) { if (!is_streaming) return; @@ -189,15 +209,24 @@ void D7::BCSTM::stream() { } } -void D7::BCSTM::fill_buffers() { +void D7::BCSTM2::fill_buffers() { for (unsigned int bufIndex = 0; bufIndex < buffer_count; ++bufIndex) { - if (wave_buf[0][bufIndex].status != NDSP_WBUF_DONE) continue; + /*if (wave_buf[0][bufIndex].status != NDSP_WBUF_DONE) continue; if (channel_count == 2 && wave_buf[1][bufIndex].status != NDSP_WBUF_DONE) - continue; + continue;*/ + + bool all_ready = true; + for (unsigned int ch = 0; ch < channel_count; ++ch) { + if (wave_buf[ch][bufIndex].status != NDSP_WBUF_DONE) { + all_ready = false; + break; + } + } + if (!all_ready) continue; if (is_looping && current_block == loop_end) { current_block = loop_start; - file.seekg( + pFile.seekg( (data_offset + 0x20 + block_size * channel_count * loop_start)); } if (!is_looping && current_block == loop_end) { @@ -209,8 +238,8 @@ void D7::BCSTM::fill_buffers() { ndspWaveBuf *buf = &wave_buf[channelIndex][bufIndex]; memset(buf, 0, sizeof(ndspWaveBuf)); - buf->data_adpcm = buffer_data[channelIndex][bufIndex].data(); - file.read( + buf->data_adpcm = buffer_data[channelIndex][bufIndex].Data(); + pFile.read( reinterpret_cast(buf->data_adpcm), (current_block == num_blocks - 1) ? last_block_size : block_size); DSP_FlushDataCache(buf->data_adpcm, block_size); @@ -232,26 +261,8 @@ void D7::BCSTM::fill_buffers() { } } -unsigned int D7::BCSTM::read32() { - unsigned int ret; - file.read(reinterpret_cast(&ret), sizeof(ret)); - return (is_little_endian ? ret : htonl(ret)); -} - -unsigned short D7::BCSTM::read16() { - unsigned short ret; - file.read(reinterpret_cast(&ret), sizeof(ret)); - return (is_little_endian ? ret : htons(ret)); -} - -unsigned char D7::BCSTM::read8() { - unsigned char ret; - file.read(reinterpret_cast(&ret), sizeof(ret)); - return ret; -} - -bool D7::BCSTM::file_advance(unsigned long long bytes) { - size_t seek_pos = (size_t)file.tellg() + bytes; - file.seekg(seek_pos); - return (!file.fail() && file.tellg() == seek_pos); +bool D7::BCSTM2::file_advance(unsigned long long bytes) { + size_t seek_pos = (size_t)pFile.tellg() + bytes; + pFile.seekg(seek_pos); + return (!pFile.fail() && pFile.tellg() == seek_pos); } \ No newline at end of file diff --git a/source/bcstm/bcstmv2.hpp b/source/bcstm/bcstmv2.hpp new file mode 100644 index 0000000..a9055e8 --- /dev/null +++ b/source/bcstm/bcstmv2.hpp @@ -0,0 +1,129 @@ +#pragma once + +#include <3ds.h> + +#include +#include +#include +#include + +namespace D7 { +class BCSTM2 { + public: + BCSTM2() {} + ~BCSTM2() { Stop(); } + + template + void Read(T& v) { + // Check if Value could be Read + static_assert(std::is_integral::value, "Cannot Read type T"); + v = 0; // Set value to 0 (most cases a windows problem) + std::vector buf(sizeof(T), 0); // declare buffer + // Read data into buffer + pFile.read(reinterpret_cast(buf.data()), sizeof(T)); + // Loop or in be reverse loop and chift the values + for (size_t i = 0; i < sizeof(T); i++) { + v |= static_cast(buf[pBigEndian ? sizeof(T) - 1 - i : i]) << (8 * i); + } + } + template + void Write(const T& v) { + // Check if Value could Write + static_assert(std::is_integral::value, "Cannot Write type T"); + std::vector buf(sizeof(T), 0); // declare buffer + // Loop or in be reverse loop and write the values + for (size_t i = 0; i < sizeof(T); i++) { + buf[(pBigEndian ? sizeof(T) - 1 - i : i)] = + buf[pBigEndian ? sizeof(T) - 1 - i : i] = + static_cast((v >> (8 * i)) & 0xFF); + } + // Write buffer into file + pFile.write(reinterpret_cast(buf.data()), sizeof(T)); + } + template + T Read() { + T o; + Read(o); + return o; + } + + bool LoadFile(const std::string& path); + void Update(); + void Play(); + void Pause(); + void Stop(); + + inline bool IsLooping() { return this->is_looping; } + inline bool IsLoaded() { return this->is_loaded; } + inline unsigned int GetLoopStart() { return this->loop_start; } + inline unsigned int GetLoopEnd() { return this->loop_end; } + inline unsigned int GetChannelCount() { return this->channel_count; } + inline unsigned int GetTotal() { return this->num_blocks; } + inline unsigned int GetCurrent() { return this->current_block; } + inline unsigned int GetSampleRate() { return this->sample_rate; } + inline unsigned int GetSamples() const { + return (block_size * num_blocks) / sample_rate; + } + inline unsigned int GetBlcokSize() const { return block_size; } + inline unsigned int GetBlockSamples() const { return block_samples; } + + bool pBigEndian = false; + std::fstream pFile; + + private: + unsigned int read32(); + unsigned short read16(); + unsigned char read8(); + bool file_advance(unsigned long long bytes); + void stream(); + void fill_buffers(); + + enum RefType : uint16_t { + ByteTable = 0x0100, + ReferenceTable = 0x0101, + SampleData = 0x1F00, + DSPADPCMInfo = 0x0300, + InfoBlock = 0x4000, + SeekBlock = 0x4001, + DataBlock = 0x4002, + StreamInfo = 0x4100, + TrackInfo = 0x4101, + ChannelInfo = 0x4102, + }; + + static const int buffer_count = 20; + static const int max_channels = 8; + + unsigned int current_time = 0; + unsigned int last_time = 0; + + bool is_loaded = false; + bool is_paused = false; + bool is_looping = false; + bool is_streaming = false; + bool is_little_endian; + + unsigned int channel_count = 0; + unsigned int sample_rate = 0; + + unsigned int loop_start = 0; + unsigned int loop_end = 0; + unsigned int num_blocks = 0; + unsigned int block_size = 0; + unsigned int block_samples = 0; + unsigned int last_block_size = 0; + unsigned int last_block_samples = 0; + unsigned short adpcm_coefs[max_channels][16]; + + unsigned int current_block = 0; + unsigned int info_offset = 0; + unsigned int data_offset = 0; + + unsigned int active_channels = 0; + unsigned short channel[max_channels]; + + ndspWaveBuf wave_buf[max_channels][buffer_count]; + ndspAdpcmData adpcm_data[max_channels][2]; + PD::Vec> buffer_data[max_channels][buffer_count]; +}; +} // namespace D7 \ No newline at end of file diff --git a/source/bcstm_ctrl.hpp b/source/bcstm_ctrl.hpp new file mode 100644 index 0000000..44b6d18 --- /dev/null +++ b/source/bcstm_ctrl.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +struct BCSTM_Ctrl { + enum ReqType { + OpenFile, /** Big Overhead so should be handled in a separate thread */ + CloseFile, /** No Overhead */ + Play, /** No visible overhead */ + Pause, /** Can be direct accessded due to no overhead */ + Stop, /** No Overhead */ + KillThread, /** Should be called on close */ + }; + struct Request { + Request(ReqType t, const std::string& d) { + req = t; + req_dat = d; + } + ReqType req; + std::string req_dat; + }; + + void DoRequest(ReqType t, const std::string& dat = "") { + pRequests.PushBack(Request(t, dat)); + } + + D7::BCSTM2 plr; + PD::List pRequests; + bool pFileLoaded = false; +}; + +extern BCSTM_Ctrl bcstm_ctrl; \ No newline at end of file diff --git a/source/bcstm_player.cpp b/source/bcstm_player.cpp new file mode 100644 index 0000000..14abd13 --- /dev/null +++ b/source/bcstm_player.cpp @@ -0,0 +1,193 @@ +#include +#include /** std::memset :( */ + +namespace D7 { +void BcstmPlayer::LoadFile(const std::string& path) { + CleanUp(); + try { + pCurrentFile.LoadFile(path); + } catch (const std::exception& e) { + throw std::runtime_error(e.what()); + } + /** Resize the Player Internal Data holders */ + /** Using this allows to not have to much memory allocated */ + pChannels.Resize(pCurrentFile.GetNumChannels()); + pWaveBuf.Resize(pCurrentFile.GetNumChannels()); + // pBufferData.Resize(pCurrentFile.GetNumChannels()); + for (PD::u32 i = 0; i < pCurrentFile.GetNumChannels(); i++) { + pWaveBuf[i].Resize(BufferCount); + // pBufferData[i].Resize(BufferCount); + } + pIsLoaded = true; +} + +void BcstmPlayer::Play() { + if (pIsPaused) { + for (PD::u32 i = 0; i < pCurrentFile.GetNumChannels(); i++) { + ndspChnSetPaused(pChannels[i], false); + } + pIsPaused = false; + return; + } + if (pIsStreaming) { + return; + } + for (PD::u32 i = 0; i < pCurrentFile.GetNumChannels(); i++) { + { + pChannels[i] = 0; + while (pChannels[i] < 24 && ((pActiveChannels >> pChannels[i]) & 1)) { + pChannels[i]++; + } + if (pChannels[i] == 24) { + throw std::range_error( + "BCSTM Player: Out of Range (channel == 24) detected!"); + } + pActiveChannels |= 1 << pChannels[i]; + ndspChnWaveBufClear(pChannels[i]); + } + static float mix[12]; + ndspChnSetFormat(pChannels[i], + NDSP_FORMAT_ADPCM | NDSP_3D_SURROUND_PREPROCESSED); + ndspChnSetRate(pChannels[i], pCurrentFile.GetSampleRate()); + + /** Make sure they are 0 */ + for (int j = 0; j < 12; j++) { + mix[j] = 0.f; + } + /** Leave 1 and 2 Channels as they are */ + if (pCurrentFile.GetNumChannels() == 1) { + mix[0] = mix[1] = 0.5f; + } else if (pCurrentFile.GetNumChannels() == 2) { + if (i == 0) { + mix[0] = 0.8f; + mix[2] = 0.2f; + } else { + mix[1] = 0.8f; + mix[3] = 0.2f; + } + /** Dont know if this is the finished code here */ + } else if (pCurrentFile.GetNumChannels() == 4) { + mix[i] = 0.8f; + if (i > 0) mix[i - 1] += 0.2f; + if (i < 3) mix[i + 1] += 0.2f; + } else if (pCurrentFile.GetNumChannels() == 6) { + mix[i] = 0.7f; + if (i > 0) mix[i - 1] += 0.15f; + if (i < 5) mix[i + 1] += 0.15f; + } else if (pCurrentFile.GetNumChannels() == 8) { + mix[i] = 0.6f; + if (i > 0) mix[i - 1] += 0.1f; + if (i < 7) mix[i + 1] += 0.1f; + } + + ndspChnSetMix(pChannels[i], mix); + ndspChnSetAdpcmCoefs(pChannels[i], + pCurrentFile.pDSP_ADPCM_Info[i].Param.Coefficients); + + for (unsigned int j = 0; j < BufferCount; j++) { + /** still Prefer fill_n over memset */ + std::memset(&pWaveBuf[i][j], 0, sizeof(ndspWaveBuf)); + pWaveBuf[i][j].status = NDSP_WBUF_DONE; + pBufferData[i][j].Resize(pCurrentFile.GetBlockSize()); + } + } + pIsStreaming = true; +} + +void BcstmPlayer::Pause() { + if (!pIsStreaming) { + return; + } + pIsPaused = true; + for (PD::u32 i = 0; i < pCurrentFile.GetNumChannels(); i++) { + ndspChnSetPaused(pChannels[i], true); + } +} + +void BcstmPlayer::Stop() { + if (!pIsStreaming) { + return; + } + for (unsigned int i = 0; i < pCurrentFile.GetNumChannels(); i++) { + ndspChnWaveBufClear(pChannels[i]); + pActiveChannels &= ~(1 << pChannels[i]); + } + pIsStreaming = false; +} + +void BcstmPlayer::Stream() { + pCurrentTime = svcGetSystemTick(); + if (pCurrentTime - pLastTime >= 100000000 && pIsLoaded) { + if (!pIsStreaming) return; + if (!pIsPaused) pFillBuffers(); + pLastTime = pCurrentTime; + } +} + +void BcstmPlayer::pFillBuffers() { + for (PD::u32 buf_idx = 0; buf_idx < BufferCount; buf_idx++) { + bool all_ready = true; + for (PD::u32 ch = 0; ch < pCurrentFile.GetNumChannels(); ch++) { + if (pWaveBuf[ch][buf_idx].status != NDSP_WBUF_DONE) { + all_ready = false; + break; + } + } + if (!all_ready) continue; + + if (pCurrentFile.IsLooping() && + pCurrentBlock == pCurrentFile.GetLoopEnd()) { + pCurrentBlock = pCurrentFile.GetLoopStart(); + pCurrentFile.ReadGotoBeginning(true); + } + if (!pCurrentFile.IsLooping() && + pCurrentBlock == pCurrentFile.GetLoopEnd()) { + this->Stop(); + } + + for (PD::u32 chn_idx = 0; chn_idx < pCurrentFile.GetNumChannels(); + ++chn_idx) { + ndspWaveBuf* buf = &pWaveBuf[chn_idx][buf_idx]; + + memset(buf, 0, sizeof(ndspWaveBuf)); + buf->data_adpcm = pBufferData[chn_idx][buf_idx].Data(); + pCurrentFile.ReadBlock(pCurrentBlock, (PD::u8*)buf->adpcm_data); + DSP_FlushDataCache(buf->data_adpcm, pCurrentFile.GetBlockSize()); + + if (pCurrentBlock == 0) { + buf->adpcm_data = + (ndspAdpcmData*)&pCurrentFile.pDSP_ADPCM_Info[chn_idx].Context; + } else if (pCurrentBlock == pCurrentFile.GetLoopStart()) { + buf->adpcm_data = + (ndspAdpcmData*)&pCurrentFile.pDSP_ADPCM_Info[chn_idx].LoopContext; + } + + if (pCurrentBlock == pCurrentFile.GetNumBlocks() - 1) { + buf->nsamples = pCurrentFile.GetLastBlockSamples(); + } else { + buf->nsamples = pCurrentFile.GetBlockSamples(); + } + + ndspChnWaveBufAdd(pChannels[chn_idx], buf); + } + pCurrentBlock++; + } +} + +void BcstmPlayer::CleanUp() { + Stop(); + if (pIsLoaded) { + try { + pCurrentFile.CleanUp(); + } catch (const std::exception& e) { + throw std::runtime_error(e.what()); + } + } + pIsLoaded = false; + pIsStreaming = false; + pIsPaused = false; + pActiveChannels = 0; + pChannels.Clear(); + pWaveBuf.Clear(); +} +} // namespace D7 \ No newline at end of file diff --git a/source/bcstm_player.hpp b/source/bcstm_player.hpp new file mode 100644 index 0000000..1be6111 --- /dev/null +++ b/source/bcstm_player.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include <3ds.h> + +#include +#include + +namespace D7 { +class BcstmPlayer { + public: + BcstmPlayer() = default; + ~BcstmPlayer() { CleanUp(); } + + void LoadFile(const std::string& path); + void Stream(); + + void CleanUp(); + + void Play(); + void Stop(); + void Pause(); + + void pFillBuffers(); + + /** Probably unused */ + static constexpr PD::u8 MaxChannels = 8; + static constexpr int BufferCount = 20; + ctrff::BCSTM pCurrentFile; + bool pIsLoaded = false; + bool pIsStreaming = false; + bool pIsPaused = false; + + PD::u64 pCurrentTime = 0; + PD::u64 pLastTime = 0; + + PD::u32 pCurrentBlock = 0; + PD::u32 pActiveChannels = 0; + PD::Vec pChannels; + PD::Vec> pWaveBuf; + PD::Vec> pBufferData[8][20]; + // PD::Vec> pBufferData; + // PD::Vec>> pBufferData; +}; +} // namespace D7 \ No newline at end of file diff --git a/source/common.cpp b/source/common.cpp deleted file mode 100644 index bb46b13..0000000 --- a/source/common.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include - -namespace BP { -D7::BCSTM player; -bool playing = false; -std::string now_playing = ""; -bool romfs_is_mount = false; -Config config; -Update update_info; -bool checking_for_update = false; -std::string thiz_path; -bool hb_mode; -std::string Clock() { - const time_t ut = time(0); - bool h24 = config.is24h(); - bool ds = config.disp_seconds(); - auto ts = localtime(&ut); - if (!h24) { - int hr = ts->tm_hour % 12; - if (hr == 0) hr = 12; - if (!ds) { - return std::format("{}:{:02} {}", hr, ts->tm_min, - ts->tm_hour >= 12 ? "PM" : "AM"); - } - return std::format("{}:{:02}:{:02} {}", hr, ts->tm_min, ts->tm_sec, - ts->tm_hour >= 12 ? "PM" : "AM"); - } - if (!ds) { - return std::format("{}:{:02}", ts->tm_hour, ts->tm_min); - } - return std::format("{}:{:02}:{:02}", ts->tm_hour, ts->tm_min, ts->tm_sec); -} - -// TODO: Maybe include some safety checks... -void CheckForUpdate() { - // Dont create infinite tasks - if (checking_for_update) return; - PD::Tasks::Create([&]() { - checking_for_update = true; - update_info = Update(); - update_info.nightly = config.GetBool("use_nightly"); - std::string url; - if (update_info.nightly) { - url = "https://api.github.com/repos/NPI-D7/BCSTM-Player/commits"; - } else { - url = "https://api.github.com/repos/NPI-D7/BCSTM-Player/releases/latest"; - } - nlohmann::json js; - auto ret = PD::Net::JsonApiRequest(url, js); - if (ret) { - update_info = Update(); - checking_for_update = false; - return; - } - if (update_info.nightly) { - update_info.version = - js[0]["sha"].get_ref().substr(0, 7); - update_info.text = js[0]["commit"]["message"]; - update_info.valid = true; - if (update_info.version.compare(N_STRING) != 0) { - PD::PushMessage(PD::Lang::Get("UPDATER"), PD::Lang::Get("UPTFND")); - } - } else { - update_info.version = js["tag_name"]; - if (update_info.version[0] != 'v') { - update_info = Update(); - checking_for_update = false; - return; - } - update_info.text = js["body"]; - update_info.valid = true; - if (update_info.version.substr(1).compare(V_STRING) > 0) { - PD::PushMessage(PD::Lang::Get("UPDATER"), PD::Lang::Get("UPTFND")); - } - } - checking_for_update = false; - }); -} -} // namespace BP \ No newline at end of file diff --git a/source/common.hpp b/source/common.hpp deleted file mode 100644 index b781a0a..0000000 --- a/source/common.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#ifndef N_STRING -#define N_STRING "unknown" -#endif - -#ifndef V_STRING -#define V_STRING "unknown" -#endif - -namespace BP { -struct Update { - std::string version = ""; - bool nightly = false; - std::string text = ""; - bool valid = false; -}; -extern Update update_info; -extern D7::BCSTM player; -extern bool playing; -extern std::string now_playing; -extern bool romfs_is_mount; -extern Config config; -extern std::string thiz_path; -extern bool hb_mode; -std::string Clock(); -void CheckForUpdate(); -} // namespace BP \ No newline at end of file diff --git a/source/config.cpp b/source/config.cpp deleted file mode 100644 index 31b033f..0000000 --- a/source/config.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include - -void BP::Config::Load() { - if (!PD::FS::FileExist(PD::GetAppDirectory() + "/config.json")) { - make_new(); - return; - } - std::ifstream iff(PD::GetAppDirectory() + "/config.json", std::ios::in); - if (!iff.is_open()) { - make_new(); - return; - } - iff >> config; - iff.close(); - if (config.find("cfg_ver") == config.end()) { - make_new(); - return; - } - if (config["cfg_ver"].get() != config_version) { - make_new(); - return; - } - reload(); -} - -void BP::Config::Save() { - std::ofstream off(PD::GetAppDirectory() + "/config.json", std::ios::out); - off << config.dump(4); - off.close(); -} - -void BP::Config::make_new() { - config.clear(); - config["cfg_ver"] = config_version; - config["lang"] = PD::Lang::GetSys(); - config["clock"] = true; - config["24h"] = false; - config["disp_seconds"] = true; - config["romfs_browse"] = false; - config["search_updates"] = true; - config["use_nightly"] = false; - Save(); - reload(); -} - -void BP::Config::reload() { - m_cfg_ver = GetInt("cfg_ver"); - m_lang = GetString("lang"); - m_clock = GetBool("clock"); - m_24h = GetBool("24h"); - m_disp_seconds = GetBool("disp_seconds"); - m_romfs_browse = GetBool("romfs_browse"); - m_search_updates = GetBool("search_updates"); - m_use_nightly = GetBool("use_nightly"); -} \ No newline at end of file diff --git a/source/config.hpp b/source/config.hpp deleted file mode 100644 index d89008b..0000000 --- a/source/config.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include - -namespace BP { -class Config { - public: - Config() {} - ~Config() {} - void Load(); - void Save(); - - template - void Set(const std::string& key, const T& value) { - config[key] = value; - reload(); - } - - std::string GetString(const std::string& key) { - if (!config[key].is_string()) { - return ""; - } - return config[key].get(); - } - - bool GetBool(const std::string& key) { - if (!config[key].is_boolean()) { - return false; - } - return config[key].get(); - } - - int GetInt(const std::string& key) { - if (!config[key].is_number_integer()) { - return 0; - } - return config[key].get(); - } - - void Reset() { make_new(); } - - int cfg_ver() { return m_cfg_ver; } - std::string lang() { return m_lang; } - bool clock() { return m_clock; } - bool is24h() { return m_24h; } - bool disp_seconds() { return m_disp_seconds; } - bool romfs_browse() { return m_romfs_browse; } - bool search_updates() { return m_search_updates; } - bool use_nightly() { return m_use_nightly; } - - void reload(); - - private: - const int config_version = 1; - void make_new(); - nlohmann::json config; - int m_cfg_ver; - std::string m_lang; - bool m_fade; - bool m_rd7tf_theme; - bool m_clock; - bool m_24h; - bool m_disp_seconds; - bool m_romfs_browse; - bool m_search_updates; - bool m_use_nightly; -}; -} // namespace BP \ No newline at end of file diff --git a/source/cursor.hpp b/source/cursor.hpp new file mode 100644 index 0000000..6cfbd5f --- /dev/null +++ b/source/cursor.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include + +constexpr int elms_onscreen = 12; +constexpr PD::Color DesignerHeader = PD::Color(0xff111111); + +class Cursor { + public: + Cursor(fvec2 list_start, float entry_height) { + pListStart = list_start; + pEntryHeight = entry_height; + pCursor.From(pListStart).To(pListStart).As(pCursor.Linear).In(0.5f); + } + ~Cursor() = default; + + PD_SMART_CTOR(Cursor); + + int GetIndex() const { return pIndex; } + + void SetIndex(int idx) { + pIndex = idx; + UpdatePos(); + } + + void UpdatePos() { + pCursor.From(pCursor) + .To(fvec2(pListStart.x, pListStart.y + pEntryHeight * pIndex)) + .In(0.1f) + .As(pCursor.EaseInOutSine); + } + + void Update(float delta) { pCursor.Update(delta); } + + Cursor& operator++(int) { + pIndex++; + UpdatePos(); + return *this; + } + + Cursor& operator--(int) { + pIndex--; + UpdatePos(); + return *this; + } + + Cursor& operator+=(int num) { + pIndex += num; + UpdatePos(); + return *this; + } + + Cursor& operator-=(int num) { + pIndex -= num; + UpdatePos(); + return *this; + } + + operator fvec2() const { return pCursor.Get(); } + + PD::Tween pCursor; + fvec2 pListStart; + fvec2 pCursorSize; + float pEntryHeight; + int pIndex = 0; +}; \ No newline at end of file diff --git a/source/filebrowser.cpp b/source/filebrowser.cpp new file mode 100644 index 0000000..545e59d --- /dev/null +++ b/source/filebrowser.cpp @@ -0,0 +1,169 @@ +#include +#include +#include + +void FileMgr::Update() { + delta.Update(); + cursor.Update(delta.GetSeconds() * 1000.f); + delta.Reset(); + + for (int i = 0; i < 12; i++) { + Top->Rect() + .SetPos(fvec2(0, 18 + 17 * i)) + .SetSize(fvec2(400, 17)) + .SetColor(((i % 2) == 0) ? PD::Color("#222222aa") + : PD::Color("#333333aa")); + } + if (pShowHelp) { + Top->Text( + "Controls:\nUp -> Go 1 Entry Up\nDown -> Go 1 Entry Sown\nLeft -> " + "Go 5 " + "Entries Up\nRight -> Go 5 Entries Down\nA -> Go into Directory / " + "Play " + "BCSTM File\nB -> Go Back\nX -> Open .bcstm File in File " + "Inspector\nStart -> Exit App") + .SetPos(fvec2(5, 18)) + .SetColor(PD::Colors::White); + } else { + Top->Rect() + .SetPos(cursor) + .SetSize(fvec2(400, 17)) + .SetColor(PD::Color("#222222cc")); + for (int i = 0; i < int(list.size() > 12 ? 12 : list.size()); i++) { + Top->Text(list[sp + i].Name) + .SetPos(fvec2(5, 18 + 17 * i)) + .SetColor(PD::Colors::White); + } + } + Top->Rect().SetColor(DesignerHeader).SetPos(0).SetSize(fvec2(400, 18)); + Top->Text("BCSTM-Player -> Filebrowser") + .SetPos(fvec2(5, 1)) + .SetColor(PD::Colors::White); + Top->Rect() + .SetPos(fvec2(0, 222)) + .SetSize(fvec2(400, 18)) + .SetColor(DesignerHeader); + Top->Text(cPath).SetPos(fvec2(5, 223)).SetColor(PD::Colors::White); + if (list.size() > 12) { + float rect_h = (12.f / (float)list.size()) * 204.f; + /** Make sure the rect is still visible */ + rect_h = std::clamp(rect_h, 10.f, 204.f); + float rect_pos = + 18.f + ((float)sp / (float)(list.size() - 12)) * (204.f - rect_h); + Top->Rect() + .SetPos(fvec2(396, rect_pos)) + .SetSize(fvec2(4, rect_h)) + .SetColor(PD::Colors::DarkGray); + } + if (!pShowHelp) { + if (Inp->IsUp(Inp->Down) && sp + cursor.pIndex < (int)list.size() - 1) { + if (cursor.pIndex == 11) { + sp++; + } else { + cursor++; + } + } + if (Inp->IsUp(Inp->Right) && sp + cursor.pIndex + 5 < (int)list.size()) { + if (cursor.pIndex == 11) { + sp += 5; + } else { + if (cursor.pIndex + 5 > 11) { + sp += (cursor.pIndex - 11 + 5); + cursor.SetIndex(11); + } else { + cursor += 5; + } + } + } + if (Inp->IsUp(Inp->Up) && cursor.pIndex + sp > 0) { + if (cursor.pIndex == 0) { + sp--; + } else { + cursor--; + } + } + if (Inp->IsUp(Inp->Left) && sp + cursor.pIndex - 5 >= 0) { + if (cursor.pIndex == 0) { + sp -= 5; + } else { + if (cursor.pIndex - 5 < 0) { + sp -= 5 - cursor.pIndex; + cursor.SetIndex(0); + } else { + cursor -= 5; + } + } + } + + if (Inp->IsDown(Inp->A)) { + auto FSE = list[cursor.pIndex + sp]; + if (FSE.Dir) { + cursor.SetIndex(0); + sp = 0; + ScanDir(FSE.Path); + } else if (FSE.Name.find(".bcstm") != FSE.Name.npos) { + bcstm_ctrl.DoRequest(bcstm_ctrl.Stop); + bcstm_ctrl.DoRequest(bcstm_ctrl.OpenFile, FSE.Path); + bcstm_ctrl.DoRequest(bcstm_ctrl.Play); + } + } + + if (Inp->IsDown(Inp->B)) { + if (cPath != "sdmc:/") { + cPath = std::filesystem::path(cPath).parent_path().string(); + if (cPath == "sdmc:") { + cPath += "/"; + } + cursor.SetIndex(0); + sp = 0; + ScanDir(cPath); + } + } + + if (Inp->IsDown(Inp->X)) { + auto FSE = list[cursor.pIndex + sp]; + if (FSE.Name.find(".bcstm") != FSE.Name.npos) { + FileInspector->ReadFile(FSE.Path); + Goto(FileInspector); + } + } + } + + pShowHelp = Inp->IsHeld(Inp->Select); +} + +void FileMgr::SortList(std::vector& l) { + std::sort(l.begin(), l.end(), [](FSEntry& a, FSEntry& b) -> bool { + if (!a.Dir && b.Dir) { + return false; + } + if (a.Dir && !b.Dir) { + return true; + } + std::string la = a.Name; + std::string lb = b.Name; + std::transform(la.begin(), la.end(), la.begin(), + [](char c) { return std::tolower(c); }); + std::transform(lb.begin(), lb.end(), lb.begin(), + [](char c) { return std::tolower(c); }); + return la.compare(lb) < 0; + }); +} + +void FileMgr::BackgroundListUpdate(std::vector& l, + const std::string& path) { + l.clear(); + for (auto& it : std::filesystem::directory_iterator(path)) { + l.push_back( + {it.is_directory(), it.path().string(), it.path().filename().string()}); + std::this_thread::sleep_for(std::chrono::microseconds(5)); + } + SortList(l); +} + +void FileMgr::ScanDir(const std::string& path) { + cPath = path; + BackgroundListUpdate(list, path); + // std::thread tmp([&]() { BackgroundListUpdate(list, path); }); + // tmp.detach(); +} \ No newline at end of file diff --git a/source/filebrowser.hpp b/source/filebrowser.hpp new file mode 100644 index 0000000..f63b01a --- /dev/null +++ b/source/filebrowser.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include +#include + +class FileMgr : public Stage { + public: + FileMgr(PD::Hid::Ref inp, PD::LI::Texture::Ref wp, PD::LI::Font::Ref f) + : Stage(inp, wp, f) { + ScanDir("sdmc:/"); + } + ~FileMgr() = default; + PD_SMART_CTOR(FileMgr) + + Cursor cursor = Cursor(fvec2(0.f, 18.f), 17.f); + int sp = 0; + PD::Timer delta; + + void Update() override; + + struct FSEntry { + bool Dir; + std::string Path; + std::string Name; + }; + std::vector list; + std::string cPath; + bool pShowHelp = false; + + static void SortList(std::vector& l); + static void BackgroundListUpdate(std::vector& l, + const std::string& path); + void ScanDir(const std::string& path); +}; \ No newline at end of file diff --git a/source/flex/container.hpp b/source/flex/container.hpp new file mode 100644 index 0000000..6bfc0fe --- /dev/null +++ b/source/flex/container.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace PD { +namespace Flex { +class Container { + public: + Container() {} + ~Container() = default; + PD_SMART_CTOR(Container) + + virtual void Draw(PD::LI::DrawList::Ref l) const {} +}; +} // namespace Flex +} // namespace PD \ No newline at end of file diff --git a/romfs/gfx/.gitkeep b/source/flex/flex.cpp similarity index 100% rename from romfs/gfx/.gitkeep rename to source/flex/flex.cpp diff --git a/source/flex/flex.hpp b/source/flex/flex.hpp new file mode 100644 index 0000000..86c99a9 --- /dev/null +++ b/source/flex/flex.hpp @@ -0,0 +1,77 @@ +#pragma once +#include +#include +#include + +/** Another UI Library (worst one ive created) */ + +namespace PD { +namespace Flex { +class Context { + public: + Context(PD::LI::Texture::Ref white, PD::LI::Font::Ref font, + PD::Hid::Ref inp) { + pDrawList = PD::LI::DrawList::New(white); + pFont = font; + pInp = inp; + pDrawList->SetFont(pFont); + } + ~Context() {} + PD_SMART_CTOR(Context) + + void DirectAccessDraw(std::function f) { + if (f) { + f(pDrawList); + } + } + + Flex::Context& operator<<(Rect::Ref r) { + pObjects.Add(r); + return *this; + } + + Flex::Button& Button(const std::string& name) { + auto btn = Button::New(); + btn->pName = name; + pObjects.Add(btn); + return *(btn.get()); + } + + Flex::Rect& Rect() { + auto o = Rect::New(); + pObjects.Add(o); + return *(o.get()); + } + + Flex::Triangle& Triangle() { + auto o = Triangle::New(); + pObjects.Add(o); + return *(o.get()); + } + + Flex::Image& Image(PD::LI::Texture::Ref tex = nullptr) { + auto o = Image::New(); + pObjects.Add(o); + return o->SetImage(tex); + } + + Flex::Text& Text(const std::string& text = "") { + auto o = Text::New(); + pObjects.Add(o); + return o->SetText(text); + } + + void Update() { + for (auto it : pObjects) { + it->Draw(pDrawList); + } + pObjects.Clear(); + } + + PD::LI::DrawList::Ref pDrawList; + PD::Hid::Ref pInp; + PD::LI::Font::Ref pFont; + PD::Vec pObjects; +}; +} // namespace Flex +} // namespace PD \ No newline at end of file diff --git a/source/flex/objects.cpp b/source/flex/objects.cpp new file mode 100644 index 0000000..329c210 --- /dev/null +++ b/source/flex/objects.cpp @@ -0,0 +1,29 @@ +#include + +namespace PD { +namespace Flex { +void Rect::Draw(PD::LI::DrawList::Ref l) const { + l->DrawSolid(); + l->DrawRectFilled(pPos, pSize, pColor); +} +void Triangle::Draw(PD::LI::DrawList::Ref l) const { + l->DrawSolid(); + l->DrawTriangleFilled(pPosA, pPosB, pPosC, pColor); +} +void Text::Draw(PD::LI::DrawList::Ref l) const { + l->DrawText(pPos, pText, pColor); +} +void Image::Draw(PD::LI::DrawList::Ref l) const { + ivec2 size = pTex->GetSize(); + if (pSize != fvec2(-1.f, -1.f)) { + size = ivec2(pSize.x, pSize.y); + } + l->DrawTexture(pTex); + l->DrawRectFilled(pPos, fvec2(size.x, size.y), pColor); +} +void Button::Draw(PD::LI::DrawList::Ref l) const { + l->DrawRectFilled(pPos, 40, PD::Colors::Gray); + l->DrawText(pPos + 2, pName, PD::Colors::White); +} +} // namespace Flex +} // namespace PD \ No newline at end of file diff --git a/source/flex/objects.hpp b/source/flex/objects.hpp new file mode 100644 index 0000000..6d1d19c --- /dev/null +++ b/source/flex/objects.hpp @@ -0,0 +1,169 @@ +#pragma once + +#include + +/** Header Containing Simple Objects */ + +namespace PD { +namespace Flex { +class Rect : public Container { + public: + Rect() {} + Rect(fvec2 p, fvec2 s, u32 clr) { + pPos = p; + pSize = s; + pColor = clr; + } + ~Rect() = default; + PD_SMART_CTOR(Rect) + + Rect& SetPos(fvec2 p) { + pPos = p; + return *this; + } + Rect& SetSize(fvec2 s) { + pSize = s; + return *this; + } + Rect& SetColor(u32 c) { + pColor = c; + return *this; + } + + fvec2 GetPos() const { return pPos; } + fvec2 GetSize() const { return pSize; } + u32 GetColor() const { return pColor; } + + void Draw(PD::LI::DrawList::Ref l) const override; + + fvec2 pPos; + fvec2 pSize; + u32 pColor; +}; + +class Triangle : public Container { + public: + Triangle() {} + ~Triangle() = default; + PD_SMART_CTOR(Triangle) + + Triangle& SetPosA(fvec2 p) { + pPosA = p; + return *this; + } + Triangle& SetPosB(fvec2 s) { + pPosB = s; + return *this; + } + Triangle& SetPosC(fvec2 s) { + pPosC = s; + return *this; + } + Triangle& SetColor(u32 c) { + pColor = c; + return *this; + } + + fvec2 GetPosA() const { return pPosA; } + fvec2 GetPosB() const { return pPosB; } + fvec2 GetPosC() const { return pPosC; } + u32 GetColor() const { return pColor; } + + void Draw(PD::LI::DrawList::Ref l) const override; + + fvec2 pPosA; + fvec2 pPosB; + fvec2 pPosC; + u32 pColor; +}; + +class Button : public Container { + public: + Button() {} + ~Button() {} + PD_SMART_CTOR(Button) + + void Draw(PD::LI::DrawList::Ref l) const override; + + Button& OnPress(std::function f) { + pOnPress = f; + return *this; + } + + fvec2 pPos; + std::string pName; + std::function pOnPress; +}; + +class Text : public Container { + public: + Text() {} + ~Text() = default; + PD_SMART_CTOR(Text) + + Text& SetPos(fvec2 p) { + pPos = p; + return *this; + } + Text& SetBoxSize(fvec2 s) { + pSize = s; + return *this; + } + Text& SetColor(u32 c) { + pColor = c; + return *this; + } + Text& SetText(const std::string& text) { + pText = text; + return *this; + } + + fvec2 GetPos() const { return pPos; } + fvec2 GetBoxSize() const { return pSize; } + u32 GetColor() const { return pColor; } + const std::string& GetText() const { return pText; } + + void Draw(PD::LI::DrawList::Ref l) const override; + + std::string pText; + fvec2 pPos; + fvec2 pSize; + u32 pColor; +}; + +class Image : public Container { + public: + Image() {} + ~Image() = default; + PD_SMART_CTOR(Image) + + Image& SetPos(fvec2 p) { + pPos = p; + return *this; + } + Image& SetSize(fvec2 s) { + pSize = s; + return *this; + } + Image& SetColor(u32 c) { + pColor = c; + return *this; + } + Image& SetImage(PD::LI::Texture::Ref t) { + pTex = t; + return *this; + } + fvec2 GetPos() const { return pPos; } + fvec2 GetSize() const { return pSize; } + u32 GetColor() const { return pColor; } + PD::LI::Texture::Ref GetImage() const { return pTex; } + + void Draw(PD::LI::DrawList::Ref l) const override; + + PD::LI::Texture::Ref pTex; + fvec2 pPos; + fvec2 pSize = fvec2(-1.f, -1.f); + u32 pColor = 0xffffffff; +}; +} // namespace Flex +} // namespace PD \ No newline at end of file diff --git a/source/inspector_view.cpp b/source/inspector_view.cpp new file mode 100644 index 0000000..c2b6cbd --- /dev/null +++ b/source/inspector_view.cpp @@ -0,0 +1,381 @@ +#include +#include + +void Inspector::Update() { + delta.Update(); + cursor.Update(delta.GetSeconds() * 1000.f); + delta.Reset(); + + for (int i = 0; i < 12; i++) { + Top->Rect() + .SetPos(fvec2(0, 18 + 17 * i)) + .SetSize(fvec2(200, 17)) + .SetColor(((i % 2) == 0) ? PD::Color("#222222aa") + : PD::Color("#333333aa")); + Top->Rect() + .SetPos(fvec2(200, 18 + 17 * i)) + .SetSize(fvec2(200, 17)) + .SetColor(((i % 2) != 0) ? PD::Color("#222222aa") + : PD::Color("#333333aa")); + } + Top->Rect() + .SetPos(cursor) + .SetSize(fvec2(400, 17)) + .SetColor(PD::Color("#222222cc")); + for (int i = 0; i < int(pDL.size() > 12 ? 12 : pDL.size()); i++) { + Top->Text(pDL.at(sp + i)->First) + .SetPos(fvec2(5, 18 + 17 * i)) + .SetColor(PD::Colors::White); + Top->Text(pDL.at(sp + i)->Second) + .SetPos(fvec2(205, 18 + 17 * i)) + .SetColor(PD::Colors::White); + } + Top->Rect().SetColor(DesignerHeader).SetPos(0).SetSize(fvec2(400, 18)); + Top->Text("BCSTM-Player -> File Inspector") + .SetPos(fvec2(5, 1)) + .SetColor(PD::Colors::White); + Top->Rect() + .SetPos(fvec2(0, 222)) + .SetSize(fvec2(400, 18)) + .SetColor(DesignerHeader); + /** Only use Filename due to no space */ + Top->Text(std::filesystem::path(pPath).filename().string()) + .SetPos(fvec2(5, 223)) + .SetColor(PD::Colors::White); + if (pDL.size() > 12) { + float rect_h = (12.f / (float)pDL.size()) * 204.f; + /** Make sure the rect is still visible */ + rect_h = std::clamp(rect_h, 10.f, 204.f); + float rect_pos = + 18.f + ((float)sp / (float)(pDL.size() - 12)) * (204.f - rect_h); + Top->Rect() + .SetPos(fvec2(396, rect_pos)) + .SetSize(fvec2(4, rect_h)) + .SetColor(PD::Colors::DarkGray); + } + + if (Inp->IsUp(Inp->Down) && sp + cursor.pIndex < (int)pDL.size() - 1) { + if (cursor.pIndex == 11) { + sp++; + } else { + cursor++; + } + } + if (Inp->IsUp(Inp->Right) && sp + cursor.pIndex + 5 < (int)pDL.size()) { + if (cursor.pIndex == 11) { + sp += 5; + } else { + if (cursor.pIndex + 5 > 11) { + sp += (cursor.pIndex - 11 + 5); + cursor.SetIndex(11); + } else { + cursor += 5; + } + } + } + if (Inp->IsUp(Inp->Up) && cursor.pIndex + sp > 0) { + if (cursor.pIndex == 0) { + sp--; + } else { + cursor--; + } + } + if (Inp->IsUp(Inp->Left) && sp + cursor.pIndex - 5 >= 0) { + if (cursor.pIndex == 0) { + sp -= 5; + } else { + if (cursor.pIndex - 5 < 0) { + sp -= 5 - cursor.pIndex; + cursor.SetIndex(0); + } else { + cursor -= 5; + } + } + } + + if (Inp->IsDown(Inp->A)) { + auto FSE = pDL.at(cursor.pIndex + sp); + if (FSE->SubData.size() != 0) { + cursor.SetIndex(0); + sp = 0; + pStack.Push(pDL); + pDL = FSE->SubData; + } + } + + if (Inp->IsDown(Inp->B)) { + cursor.SetIndex(0); + sp = 0; + if (pStack.IsEmpty()) { + Back(); + } else { + pDL = pStack.Top(); + pStack.Pop(); + } + } +} + +/** MagicStr crates a string of the memory (unsafe) */ +std::string MagicStr(PD::u32 v) { + return std::format("{:08X} -> {}", v, std::string((char*)&v, 4)); +} + +/** Simply oneliner to fix stringstream problem with char values */ +std::string _8Str(const PD::u8& v) { return std::format("{:02X}", v); } + +/** Universal Type to hex string func */ +template +std::string _Str(const T& v) { + if (sizeof(T) == 1) { + return _8Str(v); + } + std::stringstream s; + s << std::uppercase << std::hex << std::setw(sizeof(T)) << std::setfill('0') + << v; + return s.str(); +} + +/** Hex String + Value as integer */ +template +std::string _vStr(const T& v) { + return std::format("{} -> {}", _Str(v), (PD::u32)v); +} + +/** Type Value String like Endianness2String */ +template +std::string _fStr(const T& v, std::function f) { + return std::format("{} -> {}", _Str(v), f(v)); +} + +/** + * Format Bytes value + * does not work with every type but dont care cause its internals + */ +template +std::string _bStr(const T& v) { + return std::format("{} -> {}", _Str(v), PD::Strings::FormatBytes(v)); +} + +/** true false str */ +template +std::string _tStr(const T& v) { + return std::format("{} -> {}", _Str(v), (bool)v); +} + +/** Wrapper for Inspector::MakeEntry */ +Inspector::TabEntry::Ref _me(const std::string& f, const std::string& e) { + return Inspector::MakeEntry(f, e); +} + +Inspector::TabEntry::Ref MakeRef(ctrff::BCSTM::Reference& b, + const std::string& name = "") { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + Lst.push_back(_me("TypeID", _fStr( + (ctrff::BCSTM::ReferenceTypes)b.TypeID, + ctrff::BCSTM::ReferenceType2String))); + Lst.push_back(_me("Padding", _Str(b.Padding))); + Lst.push_back(_me("Offset", _Str(b.Offset))); + e->First = "Referene"; + e->Second = name; + e->SubData = Lst; + return e; +} + +Inspector::TabEntry::Ref MakeSizedRef(ctrff::BCSTM::SizedReference& b, + const std::string& name = "") { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + Lst.push_back(MakeRef(b.Ref)); + Lst.push_back(_me("Size", _vStr(b.Size))); + e->First = "SizedRef"; + e->Second = name; + e->SubData = Lst; + return e; +} + +Inspector::TabEntry::Ref MakeRefListVec(PD::Vec& b, + const std::string& name = "") { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + for (size_t i = 0; i < b.Size(); i++) { + Lst.push_back(MakeRef(b[i], "Entry " + std::to_string(i))); + } + e->First = "Reference List"; + e->Second = name; + e->SubData = Lst; + return e; +} + +Inspector::TabEntry::Ref MakeRefTable(ctrff::BCSTM::ReferenceTable& b, + const std::string& name = "") { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + Lst.push_back(_me("Num References", _vStr(b.Count))); + Lst.push_back(MakeRefListVec(b.Refs)); + e->First = "Reference Table"; + e->Second = name; + e->SubData = Lst; + return e; +} + +Inspector::TabEntry::Ref MakeBlockHdr(ctrff::BCSTM::BlockHeader& b, + const std::string& name = "") { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + Lst.push_back(_me("Magic", MagicStr(b.Magic))); + Lst.push_back(_me("Size", _vStr(b.Size))); + e->First = "Block Header"; + e->Second = name; + e->SubData = Lst; + return e; +} + +Inspector::TabEntry::Ref MakeStreamInfo(ctrff::BCSTM::StreamInfo& b, + const std::string& name = "") { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + Lst.push_back(_me("Encoding", _fStr( + (ctrff::BCSTM::Encoding)b.Encoding, + ctrff::BCSTM::Encoding2String))); + Lst.push_back(_me("Loop", _tStr(b.Loop))); + Lst.push_back(_me("Channels", _vStr(b.ChannelCount))); + Lst.push_back(_me("Padding", _Str(b.Padding))); + Lst.push_back(_me("Sample Rate", _vStr(b.SampleRate))); + Lst.push_back(_me("Loop Start", _vStr(b.LoopStartFrame))); + Lst.push_back(_me("Loop End", _vStr(b.LoopEndFrame))); + Lst.push_back(_me("Sample Blocks", _vStr(b.SampleBlockNum))); + Lst.push_back(_me("Sample Block Size", _vStr(b.SampleBlockSize))); + Lst.push_back(_me("Sample Block Samples", _vStr(b.SampleBlockSampleNum))); + Lst.push_back(_me("Last Sample Block Size", _vStr(b.LastSampleBlockSize))); + Lst.push_back( + _me("Last Sample Block Samples", _vStr(b.LastSampleBlockSampleNum))); + Lst.push_back( + _me("Last Sample Block Padded Size", _vStr(b.LastSampleBlockPaddedSize))); + Lst.push_back(_me("Seek Data Size", _vStr(b.SeekDataSize))); + Lst.push_back(_me("Seek Interval Samples", _vStr(b.SeekIntervalSampleNum))); + Lst.push_back(MakeRef(b.SampleDataRef, "Sample Data")); + e->First = "Stream Info"; + e->Second = name; + e->SubData = Lst; + return e; +} + +Inspector::TabEntry::Ref MakeInfoBlock(ctrff::BCSTM::InfoBlock& b, + const std::string& name = "") { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + Lst.push_back(MakeBlockHdr(b.Header)); + Lst.push_back(MakeRef(b.StreamInfoRef, "Stream Info")); + Lst.push_back(MakeRef(b.TrackInfoTabRef, "Track Info Ref Table")); + Lst.push_back(MakeRef(b.ChannelInfoTabRef, "Channel Info Ref Table")); + Lst.push_back(MakeStreamInfo(b.StreamInfo)); + Lst.push_back(MakeRefTable(b.TrackInfoTab, "Track Info Table")); + Lst.push_back(MakeRefTable(b.ChannelInfoTab, "Channel Info Table")); + Lst.push_back(MakeRefListVec(b.ChannelInfoRefs, "Channel Info References")); + e->First = "Info Block"; + e->Second = name; + e->SubData = Lst; + return e; +} + +Inspector::TabEntry::Ref MakeDspAdpcmParam(ctrff::BCSTM::DSP_ADPCM_Param& b, + const std::string& name = "") { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + /** This code probably generates wrong information + * but to lazy to look on that yet + */ + for (int i = 0; i < 0x10; i += 4) { + std::string res; + for (int j = 0; j < 4; j++) { + res += _Str(b.Coefficients[i + j]) + " "; + } + Lst.push_back(_me(std::to_string(i) + " - " + std::to_string(i + 3), res)); + } + e->First = "DSP ADPCM Param"; + e->Second = name; + e->SubData = Lst; + return e; +} + +Inspector::TabEntry::Ref MakeDspAdpcmContext(ctrff::BCSTM::DSP_ADPCM_Context& b, + const std::string& name = "") { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + Lst.push_back(_me("Predictor / Scale", _Str(b.PredictorScale))); + Lst.push_back(_me("Reserved", _Str(b.Reserved))); + Lst.push_back(_me("Previous Sample", _vStr(b.PreviousSample))); + Lst.push_back(_me("Second Previous Sample", _vStr(b.SecondPreviousSample))); + e->First = "DSP ADPCM Context"; + e->Second = name; + e->SubData = Lst; + return e; +} + +Inspector::TabEntry::Ref MakeDspAdpcmInfo(ctrff::BCSTM::DSP_ADPCM_Info& b, + const std::string& name = "") { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + Lst.push_back(MakeDspAdpcmParam(b.Param, "Param")); + Lst.push_back(MakeDspAdpcmContext(b.Context, "Context")); + Lst.push_back(MakeDspAdpcmContext(b.LoopContext, "Loop Context")); + Lst.push_back(_me("Padding", _Str(b.Padding))); + e->First = "DSP ADPCM Info"; + e->Second = name; + e->SubData = Lst; + return e; +} + +Inspector::TabEntry::Ref MakeDspAdpcmInfoList( + PD::Vec& b, const std::string& name = "") { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + for (size_t i = 0; i < b.Size(); i++) { + Lst.push_back(MakeDspAdpcmInfo(b[i], "Entry " + std::to_string(i))); + } + e->First = "DSP ADPCM Info List"; + e->Second = name; + e->SubData = Lst; + return e; +} + +Inspector::TabEntry::Ref MakeHeader(ctrff::BCSTM& b) { + auto e = Inspector::TabEntry::New(); + std::vector Lst; + Lst.push_back(_me("Magic", MagicStr(b.pHeader.Magic))); + Lst.push_back( + _me("Endianness", _fStr( + (ctrff::BCSTM::Endianness)b.pHeader.Endianness, + ctrff::BCSTM::Endianness2String))); + Lst.push_back(_me("HeaderSize", _vStr(b.pHeader.HeaderSize))); + Lst.push_back(_me("Version", _vStr(b.pHeader.Version))); + Lst.push_back(_me("FileSize", _bStr(b.pHeader.FileSize))); + Lst.push_back(_me("NumBlocks", _vStr(b.pHeader.NumBlocks))); + Lst.push_back(_me("Reserved", _Str(b.pHeader.Reserved))); + Lst.push_back(MakeSizedRef(b.pInfoBlockRef, "Info Block")); + Lst.push_back(MakeSizedRef(b.pSeekBlockRef, "Seek Block")); + Lst.push_back(MakeSizedRef(b.pDataBlockRef, "Data Block")); + e->First = "Header"; + e->SubData = Lst; + return e; +} + +void Inspector::ReadFile(const std::string& path) { + List.clear(); + pPath = path; + ctrff::BCSTM b; + b.LoadFile(path); + List.push_back(MakeHeader(b)); + List.push_back(MakeInfoBlock(b.pInfoBlock)); + List.push_back(MakeDspAdpcmInfoList(b.pDSP_ADPCM_Info)); + pDL = List; +} + +Inspector::TabEntry::Ref Inspector::MakeEntry(const std::string& id, + const std::string& val) { + auto e = TabEntry::New(); + e->First = id; + e->Second = val; + return e; +} \ No newline at end of file diff --git a/source/inspector_view.hpp b/source/inspector_view.hpp new file mode 100644 index 0000000..064f11a --- /dev/null +++ b/source/inspector_view.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include + +class Inspector : public Stage { + public: + Inspector(PD::Hid::Ref inp, PD::LI::Texture::Ref wp, PD::LI::Font::Ref f) + : Stage(inp, wp, f) {} + ~Inspector() = default; + PD_SMART_CTOR(Inspector) + + Cursor cursor = Cursor(fvec2(0.f, 18.f), 17.f); + int sp = 0; + PD::Timer delta; + + void Update() override; + + void ReadFile(const std::string& path); + + struct TabEntry { + TabEntry() {} + PD_SMART_CTOR(TabEntry); + + std::string First; + std::string Second; + std::vector SubData; + }; + + static TabEntry::Ref MakeEntry(const std::string& id, const std::string& val); + + /** THIS System of navigation is very memory inefficient btw */ + std::vector pDL; + std::vector List; + PD::Stack> pStack; + std::string pPath; +}; \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index f3838bd..86151eb 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,34 +1,165 @@ -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -void Bcstm_Loop() { +/** + * Code is very unstable currently cause + * 1. PD Ctr Extension is unfinished pre alpha + * 2. Still using the bcstm/bcstmv2 impl which is based on the initial code + * implementing the 1 to 8 channel support of bcstm_player.cpp + * 3. bcstm_player.cpp is unused dead code due to it somehow crashes the fs + * module on data block read + * 4. inspector_view contains some bugs + * 5. the flex ui engine is a small lib planned to be more simple then ui7 but + * the way it works is bad + * 6. The whole code around the stage_mgr is just in alpha state + * 7. The whole code of bcstm_player 2.0.0 is completly hardcoded with extern + * references through all the code and still very unfinished + */ + +float Offset(float x) { + float y = cos(x) * 42; + return y - floor(y); +} + +void Append(PD::LI::DrawList::Ref l, int index, fvec2 position, fvec2 size, + float time) { + float offset = Offset(index) * 62; + float x_position = + position.x + size.x / 8 * ((index % 11) - 1) + cos(offset + time) * 10; + float y_position = position.y + size.y / 8 * (index / 11) + 40 + + sin(offset + time) * 10 + 30; + float color_effect = 1 - exp(-(index / 11) / 3.0f); + + l->DrawTriangleFilled( + fvec2(x_position, y_position), fvec2(x_position + 300, y_position + (90)), + fvec2(x_position - 300, y_position + (90)), + PD::Color(.94f - .17f * color_effect, .61f - .25f * color_effect, + .36f + .38f * color_effect)); +} + +void BCSTM_Handler(BCSTM_Ctrl* ctrl) { while (true) { - if (BP::player.IsLoaded()) BP::player.Update(); - PD::Thread::sleep(1 * 10); + if (ctrl->pFileLoaded) { + ctrl->plr.Update(); + } + if (ctrl->pRequests.Size() == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } else { + auto t = ctrl->pRequests.Front(); + if (t.req == BCSTM_Ctrl::OpenFile) { + try { + ctrl->plr.LoadFile(t.req_dat); + /** + * Set to true cause it would get set to false + * if an error got thrown + */ + ctrl->pFileLoaded = true; + } catch (const std::exception& e) { + std::cout << "BCSTM CTRL Error: " << e.what() << std::endl; + ctrl->pFileLoaded = false; + } + } else if (t.req == BCSTM_Ctrl::CloseFile) { + ctrl->plr.Stop(); + } else if (t.req == BCSTM_Ctrl::Stop) { + ctrl->plr.Stop(); + } else if (t.req == BCSTM_Ctrl::Play) { + ctrl->plr.Play(); + } else if (t.req == BCSTM_Ctrl::Pause) { + ctrl->plr.Pause(); + } else if (t.req == BCSTM_Ctrl::KillThread) { + break; /** Break the loop */ + } + ctrl->pRequests.PopFront(); + } + } +} + +/** Toter thread (prefetch kernel panic fehler bei BCSTM::ReadBlock) */ +void BCSTMPlayerThread2(D7::BcstmPlayer* player, bool* running) { + while (*running) { + PD::TT::Beg("BCSTM_UPT"); + player->Stream(); + PD::TT::End("BCSTM_UPT"); + /** Sleep only 200ms due to thread is not killable */ + std::this_thread::sleep_for(std::chrono::milliseconds(200)); } - BP::player.Stop(); } -int main(int argc, char* argv[]) { - d7mc_load_icons = false; - PD::Init::Main("BCSTM-Player"); - BP::thiz_path = argc == 0 ? "sdmc:/3ds/BCSTM-Player.3dsx" : argv[0]; - BP::hb_mode = argc != 0; - PD::Init::NdspFirm(); - BP::config.Load(); - PD::Lang::Load(BP::config.lang()); - aptSetSleepAllowed(false); - // Enable LRS as the Systemfont Renders Faster with it - PD::LI::GetFeatures() |= PDLithiumFlags_LRS; - - PD::Tasks::Create(Bcstm_Loop); - if (BP::config.search_updates()) BP::CheckForUpdate(); - BP::player.Stop(); // for ui - PD::Scene::Load(std::make_unique()); - - while (PD::MainLoop()) { - PD::FrameEnd(); +FileMgr::Ref Filebrowser; +Inspector::Ref FileInspector; +BCSTM_Ctrl bcstm_ctrl; + +void BottomScreenBeta(PD::LI::DrawList::Ref l) { + l->DrawRectFilled(0, fvec2(320, 240), 0xff222222); + l->DrawRectFilled(5, fvec2(310, 20), 0xff111111); + l->DrawRectFilled(7, fvec2(306, 16), 0xff222222); + float scale = 0.f; + if (bcstm_ctrl.plr.GetTotal() != 0) { + scale = + (float)bcstm_ctrl.plr.GetCurrent() / (float)bcstm_ctrl.plr.GetTotal(); + } + l->DrawRectFilled(7, fvec2(306 * scale, 16), 0xff00ff00); + l->DrawText(fvec2(5, 30), std::format(""), 0xffffffff); +} + +int main() { + PD::Ctr::EnableExceptionScreen(); + PD::Ctr::CreateContext(); + aptSetSleepAllowed(true); + auto ret = ndspInit(); + if (R_FAILED(ret)) { + throw std::runtime_error( + "ndspfirm.cdc was not found!\n\nNote: You can dump the file " + "in\n\nRosalina Menu\n Miscellaneous Options\n Dump DSP " + "Firmware\n\nThen " + "Restart this App."); + } + std::thread bcstm_player(BCSTM_Handler, &bcstm_ctrl); + auto font = PD::LI::Font::New(PD::Ctr::GetContext().Gfx); + font->LoadTTF("romfs:/fonts/ComicNeue.ttf"); + auto rl2 = PD::LI::DrawList::New(PD::Ctr::GetWhiteTex()); + auto rl3 = PD::LI::DrawList::New(PD::Ctr::GetWhiteTex()); + Filebrowser = + FileMgr::New(PD::Ctr::GetContext().Inp, PD::Ctr::GetWhiteTex(), font); + FileInspector = + Inspector::New(PD::Ctr::GetContext().Inp, PD::Ctr::GetWhiteTex(), font); + Stage::Goto(Filebrowser); + + PD::Timer time; + while (PD::Ctr::ContextUpdate()) { + time.Update(); + rl2->SetFont(font); + + rl2->DrawRectFilled(fvec2(0, 0), fvec2(400, 240), 0xff64c9fd); + for (int i = 0; i < 44; i++) + Append(rl2, i, fvec2(0, 0), fvec2(400, 240), (float)time.GetSeconds()); + BottomScreenBeta(rl3); + + Stage::DoUpdate(); + PD::Ctr::AddDrawList(rl2, false); + PD::Ctr::AddDrawList(rl3, true); + PD::Ctr::AddDrawList(Stage::GetDrawDataTop(), false); + PD::Ctr::AddDrawList(Stage::GetDrawDataBottom(), true); + if (PD::Ctr::GetContext().Inp->IsDown(PD::Ctr::GetContext().Inp->Start)) { + break; + } } + bcstm_ctrl.DoRequest(bcstm_ctrl.KillThread); + /** Make sure to unload before unloading the rest */ + Filebrowser = nullptr; + FileInspector = nullptr; + PD::Ctr::DestroyContext(); + ndspExit(); return 0; } diff --git a/source/pd_ctr_ext.cpp b/source/pd_ctr_ext.cpp new file mode 100644 index 0000000..15d76ce --- /dev/null +++ b/source/pd_ctr_ext.cpp @@ -0,0 +1,132 @@ +#include +#include +#include + +constexpr u32 DisplayTransferFlags = + (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | + GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | + GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)); + +/** Seems to not work if it happens in another thread */ +void HandleException(const std::string& e) { + gfxInitDefault(); + consoleInit(GFX_TOP, nullptr); + std::cout << "Palladium Exception Handler\n\n"; + std::cout << "Exeption: \n\n" << e << "\n\n"; + std::cout << "Press Start to Exit!" << std::endl; + while (aptMainLoop()) { + hidScanInput(); + if (hidKeysDown() & KEY_START) { + break; + } + } + gfxExit(); +} + +void CxxExceptionHandler() { + std::exception_ptr e_ = std::current_exception(); + if (e_) { + try { + std::rethrow_exception(e_); + } catch (const std::exception& e) { + HandleException(e.what()); + } catch (...) { + HandleException("Unknown Exception"); + } + } + std::abort(); +} + +namespace PD { +namespace Ctr { +Context* ActiveContext; + +Context* CheckContext() { + if (ActiveContext == nullptr) { + throw std::runtime_error( + "Context was nullptr. Did you forgot to call PD::Ctr::CreateContext?"); + } + return ActiveContext; +} + +void EnableExceptionScreen() { std::set_terminate(CxxExceptionHandler); } + +Context* CreateContext() { + Context* NewCtx = new Context; + osSetSpeedupEnable(true); + romfsInit(); + gfxInitDefault(); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE * 2); + NewCtx->Top = + C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + NewCtx->Bottom = + C3D_RenderTargetCreate(240, 320, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetOutput(NewCtx->Top, GFX_TOP, GFX_LEFT, + DisplayTransferFlags); + C3D_RenderTargetSetOutput(NewCtx->Bottom, GFX_BOTTOM, GFX_LEFT, + DisplayTransferFlags); + NewCtx->Gfx = PD::LI::Backend_C3D::New(); + NewCtx->Inp = PD::Hid3DS::New(); + NewCtx->Gfx->Init(); + std::vector wp(16 * 16 * 4, 0xff); + NewCtx->WhitePixel = NewCtx->Gfx->LoadTexture(wp, 16, 16); + if (ActiveContext == nullptr) { + ActiveContext = NewCtx; + } + return NewCtx; +} + +void DestroyContext(Context* ctx) { + if (ctx == nullptr) { + ctx = CheckContext(); + } + ctx->WhitePixel = nullptr; + ctx->Inp = nullptr; + ctx->Gfx->Deinit(); + ctx->Gfx = nullptr; + C3D_Fini(); + gfxExit(); +} + +bool ContextUpdate() { + auto c = CheckContext(); + c->Inp->Update(); + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C3D_RenderTargetClear(c->Top, C3D_CLEAR_ALL, 0x00000000, 0); + C3D_RenderTargetClear(c->Bottom, C3D_CLEAR_ALL, 0x00000000, 0); + C3D_FrameDrawOn(c->Top); + c->Gfx->ViewPort = ivec2(400, 240); + c->Gfx->NewFrame(); + for (auto& it : c->DrawLists[0]) { + c->Gfx->RenderDrawData(it->pDrawList); + it->Clear(); + } + C3D_FrameDrawOn(c->Bottom); + c->Gfx->ViewPort = ivec2(320, 240); + for (auto& it : c->DrawLists[1]) { + c->Gfx->RenderDrawData(it->pDrawList); + it->Clear(); + } + C3D_FrameEnd(0); + c->DrawLists[0].Clear(); + c->DrawLists[1].Clear(); + return aptMainLoop(); +} + +PD::LI::Texture::Ref GetWhiteTex() { + auto c = CheckContext(); + return c->WhitePixel; +} + +void AddDrawList(PD::LI::DrawList::Ref cmdl, bool bottom) { + auto c = CheckContext(); + c->DrawLists[bottom].Add(cmdl); +} + +Context& GetContext() { + CheckContext(); + return *ActiveContext; +} +} // namespace Ctr +} // namespace PD \ No newline at end of file diff --git a/source/pd_ctr_ext.hpp b/source/pd_ctr_ext.hpp new file mode 100644 index 0000000..f89b33e --- /dev/null +++ b/source/pd_ctr_ext.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace PD { +namespace Ctr { +struct Context { + PD::LI::Backend::Ref Gfx; + PD::Hid::Ref Inp; + PD::LI::Texture::Ref WhitePixel; + C3D_RenderTarget* Top; + C3D_RenderTarget* Bottom; + + PD::Vec DrawLists[2]; +}; + +Context* CreateContext(); +/** Pre alpha feature */ +void EnableExceptionScreen(); +void DestroyContext(Context* ctx = nullptr); +PD::LI::Texture::Ref GetWhiteTex(); +void AddDrawList(PD::LI::DrawList::Ref cmdl, bool bottom); +bool ContextUpdate(); +Context& GetContext(); +} // namespace Ctr +} // namespace PD \ No newline at end of file diff --git a/source/scenes/Filemanager.cpp b/source/scenes/Filemanager.cpp deleted file mode 100644 index d788706..0000000 --- a/source/scenes/Filemanager.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include - -namespace BP { -Filemanager::Filemanager(std::string idevice) { - this->device = idevice; - this->dir = this->device; - this->dircontent = PD::FileSystem::GetDirContentsExt(dir, {"bcstm"}); - this->reload_list(); - this->changeddir = false; -} - -void Filemanager::Update() { - // Render - PD::LI::OnScreen(false); - if (UI7::BeginMenu(PD::Lang::Get("HEAD_FILEMANAGER"))) { - if (config.clock()) { - UI7::SetCursorPos(NVec2(395, 2)); - UI7::Label(Clock(), PDTextFlags_AlignRight); - UI7::RestoreCursor(); - } - UI7::BrowserList(namelist, this->dirsel); - UI7::SetCursorPos(NVec2(5, 222)); - UI7::Label(this->dir); - UI7::RestoreCursor(); - UI7::EndMenu(); - } - - PD::LI::OnScreen(true); - if (UI7::BeginMenu(PD::Lang::Get("INFO"))) { - UI7::Label( - PD::Lang::Get("PLAYING") + - std::string(playing ? now_playing : PD::Lang::Get("NOTHING")), - PDTextFlags_Short); - UI7::Label(PD::Lang::Get("DIRENTRYS") + - std::to_string(this->dircontent.size())); - UI7::Label(PD::Lang::Get("LOADED") + - std::string(player.IsLoaded() ? PD::Lang::Get("YES") - : PD::Lang::Get("NO"))); - UI7::Label(PD::Lang::Get("LOOP") + std::string(player.IsLooping() - ? PD::Lang::Get("YES") - : PD::Lang::Get("NO"))); - UI7::Label(PD::Lang::Get("LOOPSTART") + - std::to_string(player.GetLoopStart())); - UI7::Label(PD::Lang::Get("LOOPEND") + std::to_string(player.GetLoopEnd())); - UI7::Label(PD::Lang::Get("CURRENT") + std::to_string(player.GetCurrent())); - UI7::Label(PD::Lang::Get("TOTAL") + std::to_string(player.GetTotal())); - UI7::Label(PD::Lang::Get("CHANNELS") + - std::to_string(player.GetChannelCount())); - UI7::Label(PD::Lang::Get("ERROR") + player.GetErrorMessage()); - if (UI7::Button(PD::Lang::Get("PLAY"))) { - player.Play(); - } - UI7::SameLine(); - if (UI7::Button(PD::Lang::Get("PAUSE"))) { - player.Pause(); - } - UI7::SameLine(); - if (UI7::Button(PD::Lang::Get("STOP"))) { - playing = false; - player.Stop(); - } - UI7::SetCursorPos(NVec2(5, 215)); - UI7::Progressbar((float)player.GetCurrent() / (float)player.GetTotal()); - UI7::RestoreCursor(); - UI7::EndMenu(); - } - // Logic - if (this->changeddir) { - this->dircontent.clear(); - std::vector temp = - PD::FileSystem::GetDirContentsExt(dir, {"bcstm"}); - - for (int i = 0; i < (int)temp.size(); i++) { - this->dircontent.push_back(temp[i]); - } - this->reload_list(); - this->changeddir = false; - } - if (hidKeysDown() & KEY_A) { - if (this->dircontent.size() > 0) { - if (this->dircontent[dirsel].dir) { - if (dir.substr(dir.length() - 1, 1) != "/") dir += "/"; - dir += this->dircontent[this->dirsel].name; - this->dircontent.clear(); - this->dirsel = 0; - this->changeddir = true; - } else { - if (PD::NameIsEndingWith(this->dircontent[this->dirsel].name, - {"bcstm"})) { - playing = false; - if (player.LoadFile(this->dircontent[this->dirsel].path)) { - player.Play(); - playing = true; - } - now_playing = this->dircontent[this->dirsel].name; - } - } - } - } - if (hidKeysDown() & KEY_B) { - if (strcmp(dir.c_str(), this->device.c_str()) == 0 || - strcmp(dir.c_str(), "/") == 0) { - PD::Scene::Back(); - } else { - dir = PD::FileSystem::GetParentPath(dir, this->device); - dirsel = 0; - changeddir = true; - } - } - if (hidKeysDown() & KEY_X) { - PD::Scene::Back(); - } - if (hidKeysDown() & KEY_UP && dirsel > 0) { - dirsel--; - } - if (hidKeysDown() & KEY_DOWN && dirsel < (int)dircontent.size() - 1) { - dirsel++; - } - if (hidKeysDown() & KEY_LEFT && dirsel - 6 > 0) { - dirsel -= 6; - } - if (hidKeysDown() & KEY_RIGHT && dirsel + 6 < (int)dircontent.size() - 1) { - dirsel += 6; - } -} -} // namespace BP \ No newline at end of file diff --git a/source/scenes/Filemanager.hpp b/source/scenes/Filemanager.hpp deleted file mode 100644 index 61ac10b..0000000 --- a/source/scenes/Filemanager.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include - -namespace BP { -class Filemanager : public PD::Scene { - public: - void Update() override; - Filemanager(std::string idevice = "sdmc:/"); - - private: - void reload_list() { - namelist.clear(); - for (const auto &it : dircontent) namelist.push_back(it.name); - } - std::string device; - std::string dir; - std::vector dircontent; - std::vector namelist; - mutable int dirsel = 0; - bool changeddir = false; -}; -} // namespace BP \ No newline at end of file diff --git a/source/scenes/MainMenu.cpp b/source/scenes/MainMenu.cpp deleted file mode 100644 index 7080cfe..0000000 --- a/source/scenes/MainMenu.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -namespace BP { -MainMenu::MainMenu() { this->load_menu(); } - -void MainMenu::Update() { - // Render - PD::LI::OnScreen(false); - if (UI7::BeginMenu(PD::Lang::Get("HEAD_MAINMENU"))) { - if (config.clock()) { - UI7::SetCursorPos(NVec2(395, 2)); - UI7::Label(Clock(), PDTextFlags_AlignRight); - UI7::RestoreCursor(); - } - UI7::Label( - PD::Lang::Get("PLAYING") + - std::string(playing ? now_playing : PD::Lang::Get("NOTHING")), - PDTextFlags_Short); - UI7::Label(PD::Lang::Get("LOADED") + - std::string(player.IsLoaded() ? PD::Lang::Get("YES") - : PD::Lang::Get("NO"))); - UI7::Label(PD::Lang::Get("LOOP") + std::string(player.IsLooping() - ? PD::Lang::Get("YES") - : PD::Lang::Get("NO"))); - UI7::Label(PD::Lang::Get("LOOPSTART") + - std::to_string(player.GetLoopStart())); - UI7::Label(PD::Lang::Get("LOOPEND") + std::to_string(player.GetLoopEnd())); - UI7::Label(PD::Lang::Get("CURRENT") + std::to_string(player.GetCurrent())); - UI7::Label(PD::Lang::Get("TOTAL") + std::to_string(player.GetTotal())); - UI7::Label(PD::Lang::Get("CHANNELS") + - std::to_string(player.GetChannelCount())); - UI7::Label(PD::Lang::Get("SRATE") + std::to_string(player.GetSampleRate())); - UI7::Label(PD::Lang::Get("ERROR") + player.GetErrorMessage()); - UI7::SetCursorPos(NVec2(5, 215)); - UI7::Progressbar((float)player.GetCurrent() / (float)player.GetTotal()); - UI7::RestoreCursor(); - UI7::EndMenu(); - } - PD::LI::OnScreen(true); - if (UI7::BeginMenu(PD::Lang::Get("CONTROLCENTER"))) { - if (UI7::Button(PD::Lang::Get("PLAY"))) { - player.Play(); - } - UI7::SameLine(); - if (UI7::Button(PD::Lang::Get("PAUSE"))) { - player.Pause(); - } - UI7::SameLine(); - if (UI7::Button(PD::Lang::Get("STOP"))) { - playing = false; - player.Stop(); - } - if (UI7::Button(PD::Lang::Get("BROWSE"))) { - PD::Scene::Load(std::make_unique()); - } - if (UI7::Button(PD::Lang::Get("SETTINGS"))) { - PD::Scene::Load(std::make_unique()); - } - if (rfsopt) { - if (UI7::Button(PD::Lang::Get("TITLES") + " (Citra only)")) { - PD::Scene::Load(std::make_unique()); - } - UI7::SameLine(); - if (UI7::Button("RomFS (Citra only)")) { - PD::Scene::Load(std::make_unique("title:/")); - } - } - if (UI7::Button(PD::Lang::Get("EXIT"))) { - PD::ExitApp(); - } - UI7::EndMenu(); - } - // Logic - // do only one request - if (hidKeysDown() & KEY_START) { - PD::ExitApp(); - } - if (current_lang != PD::Lang::GetShortcut() || - rfsopt != config.romfs_browse()) { - this->load_menu(); - } -} - -void MainMenu::load_menu() { - this->rfsopt = config.romfs_browse(); - this->current_lang = PD::Lang::GetShortcut(); -} -} // namespace BP \ No newline at end of file diff --git a/source/scenes/MainMenu.hpp b/source/scenes/MainMenu.hpp deleted file mode 100644 index 6de431d..0000000 --- a/source/scenes/MainMenu.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include - -namespace BP { -class MainMenu : public PD::Scene { - public: - void Update() override; - MainMenu(); - - private: - void load_menu(); - std::vector menu; - mutable int sel = 0; - std::string current_lang; - bool rfsopt; -}; -} // namespace BP \ No newline at end of file diff --git a/source/scenes/Settings.cpp b/source/scenes/Settings.cpp deleted file mode 100644 index e90d978..0000000 --- a/source/scenes/Settings.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#include -#include - -// #define RELEASE - -namespace BP { -Settings::Settings() { - auto dc = PD::FileSystem::GetDirContent("romfs:/lang"); - int n = 0; - for (auto &it : dc) { - if (it.name == PD::Lang::GetShortcut()) { - lang_sel = n; - } - if (it.dir) { - languages.push_back(it.name); - } - n++; - } - dispc = config.clock(); - h24 = config.is24h(); - dps = config.disp_seconds(); - romfsb = config.romfs_browse(); - search_updates = config.search_updates(); - use_nightly = config.use_nightly(); -} - -void Settings::Update() { - // Render - PD::LI::OnScreen(false); - if (UI7::BeginMenu(PD::Lang::Get("HEAD_SETTINGS"))) { - if (config.clock()) { - UI7::SetCursorPos(NVec2(395, 2)); - UI7::Label(Clock(), PDTextFlags_AlignRight); - UI7::RestoreCursor(); - } - UI7::Label(PD::Lang::Get("CREDITSL")); - UI7::Label(PD::Lang::Get("TPWMR")); - UI7::Label(" - " + PD::Lang::Get("VERSION") + std::string(PDVSTRING)); - UI7::SetCursorPos(NVec2(5, 222)); - UI7::Label(PD::Lang::Get("VERSION") + V_STRING); - UI7::RestoreCursor(); -#ifndef RELEASE - UI7::SetCursorPos(NVec2(395, 222)); - UI7::Label("nightly: " + std::string(N_STRING), PDTextFlags_AlignRight); - UI7::RestoreCursor(); -#endif - UI7::EndMenu(); - } - PD::LI::OnScreen(true); - if (UI7::BeginMenu(PD::Lang::Get("BGB"), NVec2(), UI7MenuFlags_Scrolling)) { - if (languages.size() != 0) { - UI7::Label(PD::Lang::Get("LANGUAGE")); - UI7::Label(" - " + PD::Lang::GetName() + " (" + PD::Lang::GetShortcut() + - ")"); - UI7::Label(" - " + PD::Lang::Get("AUTHOR") + PD::Lang::GetAuthor()); - if (UI7::Button(PD::Lang::Get("NEXT") + - (lang_sel < (int)languages.size() - 1 - ? languages[lang_sel + 1] - : languages[0]))) { - lang_sel++; - if (lang_sel + 1 > (int)languages.size()) { - lang_sel = 0; - } - lang_reload = true; - } - } - UI7::Label(PD::Lang::Get("APPEARANCE")); - UI7::Checkbox(PD::Lang::Get("CLOCK"), dispc); - UI7::Checkbox(PD::Lang::Get("24HRS"), h24); - UI7::Checkbox(PD::Lang::Get("SHOWSECONDS"), dps); - UI7::Label(PD::Lang::Get("UPDATER")); - UI7::Checkbox(PD::Lang::Get("AUTOSEARCHUPDATE"), search_updates); - UI7::Checkbox(PD::Lang::Get("USENIGHTLY"), use_nightly); - if (UI7::Button(PD::Lang::Get("CHECK"))) { - BP::CheckForUpdate(); - } - if (update_info.valid) { - if (!update_info.nightly && - update_info.version.substr(1).compare(V_STRING) < 0) { - UI7::Label("Already Up to date!"); - } else { - UI7::Label(PD::Lang::Get("UPDATE") + update_info.version); - UI7::Label(PD::Lang::Get("INFO") + ":\n" + update_info.text); - if (!downloading && !installing) { - if (UI7::Button(PD::Lang::Get("DOWNLOAD"))) { - PD::Tasks::Create([&]() { - downloading = true; - bool fail = false; - std::string fname = "BCSTM-Player."; - fname += hb_mode ? "3dsx" : "cia"; - if (update_info.nightly) { - auto ret = PD::Net::Download2File( - "https://raw.githubusercontent.com/NPI-D7/nightlys/master/" - "builds/BCSTM-Player/" + - fname, - hb_mode ? thiz_path : "sdmc:/BCSTM-Player.cia"); - if (ret) { - fail = true; - PD::PushMessage(PD::Lang::Get("UPDATER"), - PD::Lang::Get("UPDFAILED") + "\n" + - std::to_string(PD::Net::ErrorCode(ret)) + - "/" + - std::to_string(PD::Net::StatusCode(ret))); - } - } else { - auto ret = PD::Net::GitDownloadRelease( - "https://github.com/NPI-D7/BCSTM-Player", fname, - hb_mode ? thiz_path : "sdmc:/BCSTM-Player.cia"); - if (ret) { - fail = true; - PD::PushMessage(PD::Lang::Get("UPDATER"), - PD::Lang::Get("UPDFAILED") + "\n" + - std::to_string(PD::Net::ErrorCode(ret)) + - "/" + - std::to_string(PD::Net::StatusCode(ret))); - } - } - if (!hb_mode && !fail) { - downloading = false; - installing = true; - Result r = PD::InstallCia("sdmc:/BCSTM-Player.cia", true); - if (R_FAILED(r)) { - PD::PushMessage(PD::Lang::Get("UPDATER"), - PD::Lang::Get("UPDFAILED") + "\n" + - PD::Lang::Get("INSTERR")); - } - std::filesystem::remove("sdmc:/BCSTM-Player.cia"); - installing = false; - } - PD::PushMessage(PD::Lang::Get("UPDATER"), - PD::Lang::Get("UPDDONE")); - downloading = false; - }); - } - } - } - } - if (downloading) { - auto current = PD::Net::GetProgressCurrent(); - auto total = PD::Net::GetProgressTotal(); - UI7::Label(PD::Lang::Get("DOWNLOAD") + ": " + PD::FormatBytes(current) + - "/" + PD::FormatBytes(total)); - UI7::Progressbar((float)current / (float)total); - } else if (installing) { - auto current = PD::InstallGetInfo().current; - auto total = PD::InstallGetInfo().total; - UI7::Label(PD::Lang::Get("INSTALLING") + ": " + PD::FormatBytes(current) + - "/" + PD::FormatBytes(total)); - UI7::Progressbar((float)current / (float)total); - } - UI7::Label(PD::Lang::Get("DEVELOPER")); - UI7::Label(PD::Lang::Get("EXECMODE") + (hb_mode ? "3dsx" : "cia")); - UI7::Checkbox(PD::Lang::Get("SHOWTITLEOPT"), romfsb); - if (UI7::Button("Palladium")) { - PD::LoadSettings(); - } - UI7::EndMenu(); - } - // Logic - if (dispc != config.clock()) { - config.Set("clock", dispc); - } - if (h24 != config.is24h()) { - config.Set("24h", h24); - } - if (dps != config.disp_seconds()) { - config.Set("disp_seconds", dps); - } - if (romfsb != config.romfs_browse()) { - config.Set("romfs_browse", romfsb); - } - if (use_nightly != config.use_nightly()) { - config.Set("use_nightly", use_nightly); - } - if (search_updates != config.search_updates()) { - config.Set("search_updates", search_updates); - } - if (lang_reload) { - PD::Lang::Load(languages[lang_sel]); - config.Set("lang", languages[lang_sel]); - lang_reload = false; - } - if (hidKeysDown() & KEY_B) { - config.Save(); - PD::Scene::Back(); - } -} -} // namespace BP \ No newline at end of file diff --git a/source/scenes/Settings.hpp b/source/scenes/Settings.hpp deleted file mode 100644 index 5d1f892..0000000 --- a/source/scenes/Settings.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -namespace BP { -class Settings : public PD::Scene { - public: - void Update() override; - Settings(); - - private: - std::vector languages; - mutable int lang_sel = 0; - mutable bool dispc = false; - mutable bool h24 = false; - mutable bool dps = false; - mutable bool romfsb = false; - mutable bool downloading = false; - mutable bool use_nightly = false; - mutable bool search_updates = false; - mutable bool lang_reload = false; - mutable bool installing = false; -}; -} // namespace BP \ No newline at end of file diff --git a/source/scenes/Titles.cpp b/source/scenes/Titles.cpp deleted file mode 100644 index 02462dc..0000000 --- a/source/scenes/Titles.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include - -namespace BP { -Titles::Titles() { - std::filesystem::create_directories(PD::GetAppDirectory() + "/cache"); - D7MC::TitleManager::ScanSD(PD::GetAppDirectory() + "/"); - maxtitles = (int)D7MC::TitleManager::sdtitles.size(); - for (const auto &it : D7MC::TitleManager::sdtitles) - namelist.push_back(it->name()); -} - -void Titles::Update() { - // Render - PD::LI::OnScreen(false); - if (UI7::BeginMenu(PD::Lang::Get("HEAD_TITLES"))) { - if (config.clock()) { - UI7::SetCursorPos(NVec2(395, 2)); - UI7::Label(Clock(), PDTextFlags_AlignRight); - UI7::RestoreCursor(); - } - UI7::BrowserList(namelist, selection); - UI7::EndMenu(); - } - PD::LI::OnScreen(true); - if (UI7::BeginMenu(PD::Lang::Get("BGB"))) { - UI7::EndMenu(); - } - // Logic - if (hidKeysDown() & KEY_B) { - PD::Scene::Back(); - } - if (hidKeysDown() & KEY_A) { - if (romfs_is_mount) { - romfsUnmount("title"); - romfs_is_mount = false; - } - Result mntres = romfsMountFromTitle( - D7MC::TitleManager::sdtitles[selection]->id(), - D7MC::TitleManager::sdtitles[selection]->mediatype(), "title"); - if (R_FAILED(mntres)) { - PD::ResultDecoder d; - d.Load(mntres); - d.WriteLog(); - std::stringstream ss; - ss << "Failed to mount romfs: " << d.GetCode() << std::endl; - ss << "Module: " << d.GetModule() << std::endl; - ss << "Level: " << d.GetLevel() << std::endl; - ss << "Summary: " << d.GetSummary() << std::endl; - ss << "Description: " << d.GetDescription() << std::endl; - PD::Error(ss.str()); - } else { - romfs_is_mount = true; - } - } - if (hidKeysDown() & KEY_DOWN && - selection < (int)D7MC::TitleManager::sdtitles.size() - 1) { - selection++; - } - if (hidKeysDown() & KEY_UP && selection > 0) { - selection--; - } -} -} // namespace BP \ No newline at end of file diff --git a/source/scenes/Titles.hpp b/source/scenes/Titles.hpp deleted file mode 100644 index daeb568..0000000 --- a/source/scenes/Titles.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -namespace BP { -class Titles : public PD::Scene { - public: - void Update() override; - Titles(); - - private: - mutable int selection = 0; - int maxtitles = 0; - std::vector namelist; -}; -} // namespace BP \ No newline at end of file diff --git a/source/scenes/scenes.hpp b/source/scenes/scenes.hpp deleted file mode 100644 index 2ada39f..0000000 --- a/source/scenes/scenes.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include -#include -#include \ No newline at end of file diff --git a/source/stagemgr.cpp b/source/stagemgr.cpp new file mode 100644 index 0000000..c771816 --- /dev/null +++ b/source/stagemgr.cpp @@ -0,0 +1,3 @@ +#include + +PD::Stack Stage::pStages; \ No newline at end of file diff --git a/source/stagemgr.hpp b/source/stagemgr.hpp new file mode 100644 index 0000000..ada1d1c --- /dev/null +++ b/source/stagemgr.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +class Stage { + public: + Stage(PD::Hid::Ref inp, PD::LI::Texture::Ref wp, PD::LI::Font::Ref f) { + Top = PD::Flex::Context::New(wp, f, inp); + Bottom = PD::Flex::Context::New(wp, f, inp); + Inp = inp; + } + virtual ~Stage() {} + PD_SMART_CTOR(Stage); + + PD::Flex::Context::Ref Top; + PD::Flex::Context::Ref Bottom; + PD::Hid::Ref Inp; + + virtual void Update() {} + + static void Goto(Stage::Ref s) { pStages.Push(s); } + static void Back() { pStages.Pop(); } + static void DoUpdate() { + pStages.Top()->Top->pDrawList->Clear(); + pStages.Top()->Bottom->pDrawList->Clear(); + pStages.Top()->Update(); + pStages.Top()->Top->Update(); + pStages.Top()->Bottom->Update(); + }; + static PD::LI::DrawList::Ref GetDrawDataTop() { + return pStages.Top()->Top->pDrawList; + } + static PD::LI::DrawList::Ref GetDrawDataBottom() { + return pStages.Top()->Bottom->pDrawList; + } + + static PD::Stack pStages; +}; diff --git a/source/stages.hpp b/source/stages.hpp new file mode 100644 index 0000000..8e8f188 --- /dev/null +++ b/source/stages.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +extern FileMgr::Ref Filebrowser; +extern Inspector::Ref FileInspector; \ No newline at end of file From dee1acee003219bd419d1259d84b22c9ac4644a7 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Thu, 8 May 2025 20:32:45 +0200 Subject: [PATCH 02/23] Temp Remove Palladium --- .gitmodules | 3 --- palladium | 1 - 2 files changed, 4 deletions(-) delete mode 160000 palladium diff --git a/.gitmodules b/.gitmodules index 34a96e3..2267af1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "palladium"] - path = palladium - url = https://github.com/tobid7/palladium [submodule "ctrff"] path = ctrff url = https://dev.npid7.de/tobid7/ctrff-pub diff --git a/palladium b/palladium deleted file mode 160000 index f75f706..0000000 --- a/palladium +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f75f7067ffe44ffafc852691b1bb56867842bb6f From 25c8dc60e494ca6dccdc0a0c1b5a5a9390a91ef8 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Thu, 8 May 2025 20:39:26 +0200 Subject: [PATCH 03/23] Readd Palladium --- .gitmodules | 9 ++++++--- palladium | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) create mode 160000 palladium diff --git a/.gitmodules b/.gitmodules index 2267af1..e7925a1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ -[submodule "ctrff"] - path = ctrff - url = https://dev.npid7.de/tobid7/ctrff-pub +[submodule "ctrff"] + path = ctrff + url = https://dev.npid7.de/tobid7/ctrff-pub +[submodule "palladium"] + path = palladium + url = https://dev.npid7.de/tobid7/palladium diff --git a/palladium b/palladium new file mode 160000 index 0000000..f75f706 --- /dev/null +++ b/palladium @@ -0,0 +1 @@ +Subproject commit f75f7067ffe44ffafc852691b1bb56867842bb6f From 3b1b3f4dbaf77e2a3f2be6383e343a7593f3746d Mon Sep 17 00:00:00 2001 From: tobid7 Date: Thu, 8 May 2025 20:48:16 +0200 Subject: [PATCH 04/23] Use Palladium patch to fix build system (maybe) --- ctrff | 2 +- palladium | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ctrff b/ctrff index 9969987..3704e3d 160000 --- a/ctrff +++ b/ctrff @@ -1 +1 @@ -Subproject commit 996998785cf10394338f7a54ce54e324e1229e61 +Subproject commit 3704e3d9cc0acdbba8387a25814190638af06fa2 diff --git a/palladium b/palladium index f75f706..ea76a30 160000 --- a/palladium +++ b/palladium @@ -1 +1 @@ -Subproject commit f75f7067ffe44ffafc852691b1bb56867842bb6f +Subproject commit ea76a304d4d88115dc98d55dfa745f0413ece10f From 27b68565b7877c9444e3d3516bf1eddab45ffb39 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Thu, 8 May 2025 20:56:00 +0200 Subject: [PATCH 05/23] Forgot about the fact i switched to cmake --- .github/workflows/main.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1956ecb..481b3f6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,7 +30,7 @@ jobs: - name: Install tools run: | sudo apt-get update - sudo apt-get install p7zip-full g++ cmake -y + sudo apt-get install p7zip-full g++ cmake ninja-build -y git clone https://github.com/NPI-D7/bannertool cd bannertool cmake -B build @@ -46,10 +46,14 @@ jobs: - name: Build id: build run: | - make + mkdir -p build + cd build + cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="/opt/devkitpro/cmake/3DS.cmake" + ninja + cd .. mkdir -p ~/artifacts - cp BCSTM-Player.3dsx ~/artifacts - cp BCSTM-Player.cia ~/artifacts + cp build/BCSTM-Player.3dsx ~/artifacts + cp build/BCSTM-Player.cia ~/artifacts echo ::set-output name=commit_tag::$(git describe --abbrev=0 --tags) echo ::set-output name=commit_hash::$(git log --format=%h -1) # Webhook info From 2af82fd462ac4f03be0888c1a3a4a2d6538db146 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Fri, 28 Nov 2025 20:29:38 +0100 Subject: [PATCH 06/23] # Update - Move elf, 3dsx, and cia into bin dir (less azahar crashes) - Add CTRFF_DECODE Flag for building a working and non working version xd - Update Ctrff (more detailed errors) - update to latest palladium 040 - remove gfx dir (no use case for it) - Remember cursor pos in filebrowser - Show 0x in front of some values in inspector - break mainloop and wait for everything deinitialized if an exception occours in a thread --- .vscode/launch.json | 2 +- CMakeLists.txt | 28 +++++++++++++++++++--------- ctrff | 2 +- gfx/.gitkeep | 0 palladium | 2 +- source/bcstm/bcstmv2.cpp | 12 ++++++------ source/bcstm_ctrl.hpp | 8 ++++++++ source/filebrowser.cpp | 13 ++++++++++--- source/filebrowser.hpp | 1 + source/inspector_view.cpp | 8 ++++---- source/main.cpp | 21 +++++++-------------- source/pd_ctr_ext.cpp | 9 ++++++++- 12 files changed, 66 insertions(+), 40 deletions(-) delete mode 100644 gfx/.gitkeep diff --git a/.vscode/launch.json b/.vscode/launch.json index 8234252..a4de925 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,7 +4,7 @@ "name": "3DS GDB", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/build/BCSTM-Player.elf", + "program": "${workspaceFolder}/bin/BCSTM-Player.elf", "miDebuggerServerAddress": "localhost:4003", "miDebuggerPath": "C:\\devkitPro\\devkitARM\\bin\\arm-none-eabi-gdb.exe", "cwd": "${workspaceFolder}", diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cb9f9e..5945ba1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,9 @@ set(3DS_IP "192.168.2.224" CACHE STRING "3ds IP (make send)") # Set Project project(BCSTM-Player LANGUAGES C CXX VERSION 2.0.0) +# Decode with CTRFF +option(CTRFF_DECODE "Decode music data with ctrff (WIP)" OFF) + # Enable Compile Command Export set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -45,8 +48,11 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +# Better location to not always crash Citra / Azahar +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) + # Set Special C and CXX flags -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-psabi -O3") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-psabi") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} -fno-rtti") add_subdirectory(palladium) @@ -64,13 +70,13 @@ add_executable(BCSTM-Player #source/config.cpp source/bcstm/bcstmv2.cpp - #source/bcstm_player.cpp + source/bcstm_player.cpp source/flex/objects.cpp ) # Set dependencies, include dirs and definitions -target_link_libraries(BCSTM-Player PUBLIC m ctrff pd-backend-3ds palladium citro3d ctru) +target_link_libraries(BCSTM-Player PUBLIC m ctrff pd-backend-3ds palladium citro3d ctrud) target_include_directories(BCSTM-Player PUBLIC source ${DEVKITPRO}/portlibs/3ds/include @@ -81,10 +87,14 @@ target_compile_definitions(BCSTM-Player PUBLIC -DGIT_COMMIT="${GIT_SHORT_HASH}" ) +if(${CTRFF_DECODE}) + target_compile_definitions(BCSTM-Player PUBLIC -DCTRFF_DECODE=1) +endif() + # Command to send to console (make send) add_custom_target( send - COMMAND 3dslink -a "${3DS_IP}" "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.3dsx" + COMMAND 3dslink -a "${3DS_IP}" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.3dsx" ) ## Graphics @@ -115,7 +125,7 @@ ctr_generate_smdh( ) ctr_create_3dsx( BCSTM-Player - OUTPUT "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.3dsx" + OUTPUT "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.3dsx" SMDH "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh" ROMFS "${APP_ROMFS}" DEPENDS create_resources @@ -134,11 +144,11 @@ add_custom_command( ) add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.cia + OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.cia COMMAND ${MAKEROM} -f cia -target t -exefslogo - -o "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.cia" - -elf "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.elf" + -o "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.cia" + -elf "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.elf" -rsf ${APP_RSF} -banner "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr" -icon "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh" @@ -155,5 +165,5 @@ add_custom_target(make_banner ALL ) add_custom_target(make_cia ALL - DEPENDS ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.elf ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.cia + DEPENDS ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.elf ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.cia ) diff --git a/ctrff b/ctrff index 3704e3d..7197ecf 160000 --- a/ctrff +++ b/ctrff @@ -1 +1 @@ -Subproject commit 3704e3d9cc0acdbba8387a25814190638af06fa2 +Subproject commit 7197ecff02b750600345166c01691743cb576dee diff --git a/gfx/.gitkeep b/gfx/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/palladium b/palladium index ea76a30..a0960bd 160000 --- a/palladium +++ b/palladium @@ -1 +1 @@ -Subproject commit ea76a304d4d88115dc98d55dfa745f0413ece10f +Subproject commit a0960bd717bce6a659d17e157e59c9bf16486c04 diff --git a/source/bcstm/bcstmv2.cpp b/source/bcstm/bcstmv2.cpp index 38b179f..c789293 100644 --- a/source/bcstm/bcstmv2.cpp +++ b/source/bcstm/bcstmv2.cpp @@ -2,7 +2,7 @@ #include -bool D7::BCSTM2::LoadFile(const std::string &path) { +bool D7::BCSTM2::LoadFile(const std::string& path) { Stop(); pFile.open(path, std::ios::in | std::ios::binary); if (!pFile) { @@ -76,13 +76,13 @@ bool D7::BCSTM2::LoadFile(const std::string &path) { off.close(); // get adpcm data for (unsigned int i = 0; i < channel_count; i++) { - pFile.read(reinterpret_cast(adpcm_coefs[i]), + pFile.read(reinterpret_cast(adpcm_coefs[i]), sizeof(unsigned short) * 16); // beginning context - pFile.read(reinterpret_cast(&adpcm_data[i][0]), + pFile.read(reinterpret_cast(&adpcm_data[i][0]), sizeof(ndspAdpcmData)); // loop context - pFile.read(reinterpret_cast(&adpcm_data[i][1]), + pFile.read(reinterpret_cast(&adpcm_data[i][1]), sizeof(ndspAdpcmData)); // skip padding Read(); @@ -235,12 +235,12 @@ void D7::BCSTM2::fill_buffers() { for (unsigned int channelIndex = 0; channelIndex < channel_count; ++channelIndex) { - ndspWaveBuf *buf = &wave_buf[channelIndex][bufIndex]; + ndspWaveBuf* buf = &wave_buf[channelIndex][bufIndex]; memset(buf, 0, sizeof(ndspWaveBuf)); buf->data_adpcm = buffer_data[channelIndex][bufIndex].Data(); pFile.read( - reinterpret_cast(buf->data_adpcm), + reinterpret_cast(buf->data_adpcm), (current_block == num_blocks - 1) ? last_block_size : block_size); DSP_FlushDataCache(buf->data_adpcm, block_size); diff --git a/source/bcstm_ctrl.hpp b/source/bcstm_ctrl.hpp index 44b6d18..0524fd9 100644 --- a/source/bcstm_ctrl.hpp +++ b/source/bcstm_ctrl.hpp @@ -1,6 +1,10 @@ #pragma once +#ifdef CTRFF_DECODE +#include +#else #include +#endif struct BCSTM_Ctrl { enum ReqType { @@ -24,7 +28,11 @@ struct BCSTM_Ctrl { pRequests.PushBack(Request(t, dat)); } +#ifdef CTRFF_DECODE + D7::BcstmPlayer plr; +#else D7::BCSTM2 plr; +#endif PD::List pRequests; bool pFileLoaded = false; }; diff --git a/source/filebrowser.cpp b/source/filebrowser.cpp index 545e59d..3f58de2 100644 --- a/source/filebrowser.cpp +++ b/source/filebrowser.cpp @@ -16,7 +16,7 @@ void FileMgr::Update() { } if (pShowHelp) { Top->Text( - "Controls:\nUp -> Go 1 Entry Up\nDown -> Go 1 Entry Sown\nLeft -> " + "Controls:\nUp -> Go 1 Entry Up\nDown -> Go 1 Entry Down\nLeft -> " "Go 5 " "Entries Up\nRight -> Go 5 Entries Down\nA -> Go into Directory / " "Play " @@ -98,6 +98,7 @@ void FileMgr::Update() { if (Inp->IsDown(Inp->A)) { auto FSE = list[cursor.pIndex + sp]; if (FSE.Dir) { + pLastPos.Push(PD::Pair(sp, cursor.GetIndex())); cursor.SetIndex(0); sp = 0; ScanDir(FSE.Path); @@ -114,8 +115,14 @@ void FileMgr::Update() { if (cPath == "sdmc:") { cPath += "/"; } - cursor.SetIndex(0); - sp = 0; + if (!pLastPos.IsEmpty()) { + sp = pLastPos.Top().First; + cursor.SetIndex(pLastPos.Top().Second); + pLastPos.Pop(); + } else { + sp = 0; + cursor.SetIndex(0); + } ScanDir(cPath); } } diff --git a/source/filebrowser.hpp b/source/filebrowser.hpp index f63b01a..19b0cc5 100644 --- a/source/filebrowser.hpp +++ b/source/filebrowser.hpp @@ -16,6 +16,7 @@ class FileMgr : public Stage { PD_SMART_CTOR(FileMgr) Cursor cursor = Cursor(fvec2(0.f, 18.f), 17.f); + PD::Stack> pLastPos; int sp = 0; PD::Timer delta; diff --git a/source/inspector_view.cpp b/source/inspector_view.cpp index c2b6cbd..f008526 100644 --- a/source/inspector_view.cpp +++ b/source/inspector_view.cpp @@ -117,11 +117,11 @@ void Inspector::Update() { /** MagicStr crates a string of the memory (unsafe) */ std::string MagicStr(PD::u32 v) { - return std::format("{:08X} -> {}", v, std::string((char*)&v, 4)); + return std::format("0x{:08X} -> {}", v, std::string((char*)&v, 4)); } /** Simply oneliner to fix stringstream problem with char values */ -std::string _8Str(const PD::u8& v) { return std::format("{:02X}", v); } +std::string _8Str(const PD::u8& v) { return std::format("0x{:02X}", v); } /** Universal Type to hex string func */ template @@ -130,8 +130,8 @@ std::string _Str(const T& v) { return _8Str(v); } std::stringstream s; - s << std::uppercase << std::hex << std::setw(sizeof(T)) << std::setfill('0') - << v; + s << "0x" << std::uppercase << std::hex << std::setw(sizeof(T)) + << std::setfill('0') << v; return s.str(); } diff --git a/source/main.cpp b/source/main.cpp index 86151eb..9fea4bf 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -46,11 +46,14 @@ void Append(PD::LI::DrawList::Ref l, int index, fvec2 position, fvec2 size, PD::Color(.94f - .17f * color_effect, .61f - .25f * color_effect, .36f + .38f * color_effect)); } - void BCSTM_Handler(BCSTM_Ctrl* ctrl) { while (true) { if (ctrl->pFileLoaded) { +#ifdef CTRFF_DECODE + ctrl->plr.Stream(); +#else ctrl->plr.Update(); +#endif } if (ctrl->pRequests.Size() == 0) { std::this_thread::sleep_for(std::chrono::milliseconds(200)); @@ -84,17 +87,6 @@ void BCSTM_Handler(BCSTM_Ctrl* ctrl) { } } -/** Toter thread (prefetch kernel panic fehler bei BCSTM::ReadBlock) */ -void BCSTMPlayerThread2(D7::BcstmPlayer* player, bool* running) { - while (*running) { - PD::TT::Beg("BCSTM_UPT"); - player->Stream(); - PD::TT::End("BCSTM_UPT"); - /** Sleep only 200ms due to thread is not killable */ - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - } -} - FileMgr::Ref Filebrowser; Inspector::Ref FileInspector; BCSTM_Ctrl bcstm_ctrl; @@ -104,12 +96,13 @@ void BottomScreenBeta(PD::LI::DrawList::Ref l) { l->DrawRectFilled(5, fvec2(310, 20), 0xff111111); l->DrawRectFilled(7, fvec2(306, 16), 0xff222222); float scale = 0.f; +#ifndef CTRFF_DECODE if (bcstm_ctrl.plr.GetTotal() != 0) { scale = (float)bcstm_ctrl.plr.GetCurrent() / (float)bcstm_ctrl.plr.GetTotal(); } +#endif l->DrawRectFilled(7, fvec2(306 * scale, 16), 0xff00ff00); - l->DrawText(fvec2(5, 30), std::format(""), 0xffffffff); } int main() { @@ -149,7 +142,7 @@ int main() { PD::Ctr::AddDrawList(rl2, false); PD::Ctr::AddDrawList(rl3, true); PD::Ctr::AddDrawList(Stage::GetDrawDataTop(), false); - PD::Ctr::AddDrawList(Stage::GetDrawDataBottom(), true); + // PD::Ctr::AddDrawList(Stage::GetDrawDataBottom(), true); if (PD::Ctr::GetContext().Inp->IsDown(PD::Ctr::GetContext().Inp->Start)) { break; } diff --git a/source/pd_ctr_ext.cpp b/source/pd_ctr_ext.cpp index 15d76ce..2840903 100644 --- a/source/pd_ctr_ext.cpp +++ b/source/pd_ctr_ext.cpp @@ -8,8 +8,13 @@ constexpr u32 DisplayTransferFlags = GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)); +bool pExceptionCtx = false; +bool pExceptionCtxA = false; /** Seems to not work if it happens in another thread */ void HandleException(const std::string& e) { + pExceptionCtx = true; + while (!pExceptionCtxA) { + } gfxInitDefault(); consoleInit(GFX_TOP, nullptr); std::cout << "Palladium Exception Handler\n\n"; @@ -22,6 +27,7 @@ void HandleException(const std::string& e) { } } gfxExit(); + std::abort(); } void CxxExceptionHandler() { @@ -87,6 +93,7 @@ void DestroyContext(Context* ctx) { ctx->Gfx = nullptr; C3D_Fini(); gfxExit(); + pExceptionCtxA = true; } bool ContextUpdate() { @@ -111,7 +118,7 @@ bool ContextUpdate() { C3D_FrameEnd(0); c->DrawLists[0].Clear(); c->DrawLists[1].Clear(); - return aptMainLoop(); + return aptMainLoop() && !pExceptionCtx; } PD::LI::Texture::Ref GetWhiteTex() { From d6cefefdd59b37f19b036dbf2d71ee813c18f017 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Fri, 28 Nov 2025 20:31:25 +0100 Subject: [PATCH 07/23] Fix stupid naming issue in ctrff based player - also cleaned up the loading to dynamically support only allocating memory for the num of channels we use --- source/bcstm_player.cpp | 26 ++++++++++++++------------ source/bcstm_player.hpp | 6 ++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/source/bcstm_player.cpp b/source/bcstm_player.cpp index 14abd13..85a0c8a 100644 --- a/source/bcstm_player.cpp +++ b/source/bcstm_player.cpp @@ -12,18 +12,18 @@ void BcstmPlayer::LoadFile(const std::string& path) { /** Resize the Player Internal Data holders */ /** Using this allows to not have to much memory allocated */ pChannels.Resize(pCurrentFile.GetNumChannels()); - pWaveBuf.Resize(pCurrentFile.GetNumChannels()); - // pBufferData.Resize(pCurrentFile.GetNumChannels()); - for (PD::u32 i = 0; i < pCurrentFile.GetNumChannels(); i++) { - pWaveBuf[i].Resize(BufferCount); - // pBufferData[i].Resize(BufferCount); + pWaveBuf.resize(pCurrentFile.GetNumChannels()); + pBufferData.resize(pCurrentFile.GetNumChannels()); + for (PD::u8 i = 0; i < pCurrentFile.GetNumChannels(); i++) { + pWaveBuf[i].resize(BufferCount); + pBufferData[i].resize(BufferCount); } pIsLoaded = true; } void BcstmPlayer::Play() { if (pIsPaused) { - for (PD::u32 i = 0; i < pCurrentFile.GetNumChannels(); i++) { + for (PD::u8 i = 0; i < pCurrentFile.GetNumChannels(); i++) { ndspChnSetPaused(pChannels[i], false); } pIsPaused = false; @@ -83,8 +83,7 @@ void BcstmPlayer::Play() { ndspChnSetMix(pChannels[i], mix); ndspChnSetAdpcmCoefs(pChannels[i], pCurrentFile.pDSP_ADPCM_Info[i].Param.Coefficients); - - for (unsigned int j = 0; j < BufferCount; j++) { + for (int j = 0; j < BufferCount; j++) { /** still Prefer fill_n over memset */ std::memset(&pWaveBuf[i][j], 0, sizeof(ndspWaveBuf)); pWaveBuf[i][j].status = NDSP_WBUF_DONE; @@ -145,14 +144,15 @@ void BcstmPlayer::pFillBuffers() { this->Stop(); } - for (PD::u32 chn_idx = 0; chn_idx < pCurrentFile.GetNumChannels(); + for (PD::u8 chn_idx = 0; chn_idx < pCurrentFile.GetNumChannels(); ++chn_idx) { ndspWaveBuf* buf = &pWaveBuf[chn_idx][buf_idx]; memset(buf, 0, sizeof(ndspWaveBuf)); - buf->data_adpcm = pBufferData[chn_idx][buf_idx].Data(); + buf->adpcm_data = + (ndspAdpcmData*)pBufferData.at(chn_idx).at(buf_idx).Data(); pCurrentFile.ReadBlock(pCurrentBlock, (PD::u8*)buf->adpcm_data); - DSP_FlushDataCache(buf->data_adpcm, pCurrentFile.GetBlockSize()); + DSP_FlushDataCache(buf->adpcm_data, pCurrentFile.GetBlockSize()); if (pCurrentBlock == 0) { buf->adpcm_data = @@ -187,7 +187,9 @@ void BcstmPlayer::CleanUp() { pIsStreaming = false; pIsPaused = false; pActiveChannels = 0; + pCurrentBlock = 0; pChannels.Clear(); - pWaveBuf.Clear(); + pWaveBuf.clear(); + pBufferData.clear(); } } // namespace D7 \ No newline at end of file diff --git a/source/bcstm_player.hpp b/source/bcstm_player.hpp index 1be6111..a672a05 100644 --- a/source/bcstm_player.hpp +++ b/source/bcstm_player.hpp @@ -36,9 +36,7 @@ class BcstmPlayer { PD::u32 pCurrentBlock = 0; PD::u32 pActiveChannels = 0; PD::Vec pChannels; - PD::Vec> pWaveBuf; - PD::Vec> pBufferData[8][20]; - // PD::Vec> pBufferData; - // PD::Vec>> pBufferData; + std::vector> pWaveBuf; + std::vector>>> pBufferData; }; } // namespace D7 \ No newline at end of file From 922bb8d96d49a718daa5a5bef6c78f16a796bce2 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Fri, 28 Nov 2025 20:36:05 +0100 Subject: [PATCH 08/23] Fix CI build?? --- .github/workflows/main.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 481b3f6..fa52cbd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -52,8 +52,9 @@ jobs: ninja cd .. mkdir -p ~/artifacts - cp build/BCSTM-Player.3dsx ~/artifacts - cp build/BCSTM-Player.cia ~/artifacts + cp bin/BCSTM-Player.3dsx ~/artifacts + cp bin/BCSTM-Player.cia ~/artifacts + cp bin/BCSTM-Player.elf ~/artifacts # ELF for debugging echo ::set-output name=commit_tag::$(git describe --abbrev=0 --tags) echo ::set-output name=commit_hash::$(git log --format=%h -1) # Webhook info @@ -107,7 +108,7 @@ jobs: CURRENT_DATE=$(date +"%Y%m%d-%H%M%S") echo ::set-output name=current_date::$CURRENT_DATE git config --global user.email "tobid7@outlook.de" - git config --global user.name "Tobi-D7" + git config --global user.name "tobid7" git clone --depth 1 https://${{ secrets.TOKEN }}@github.com/NPI-D7/nightlys.git cd nightlys mkdir -p builds/BCSTM-Player/ From 70f4fd22cc8c8a2be3150a73ac09c65d8fe21654 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Fri, 28 Nov 2025 20:55:05 +0100 Subject: [PATCH 09/23] Fix CI (again) --- .github/workflows/main.yml | 41 ++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fa52cbd..cf303fc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,8 +25,10 @@ jobs: uses: actions/checkout@v2 with: submodules: recursive + - name: Set up Git safe directory run: git config --global --add safe.directory /__w/BCSTM-Player/BCSTM-Player + - name: Install tools run: | sudo apt-get update @@ -54,43 +56,42 @@ jobs: mkdir -p ~/artifacts cp bin/BCSTM-Player.3dsx ~/artifacts cp bin/BCSTM-Player.cia ~/artifacts - cp bin/BCSTM-Player.elf ~/artifacts # ELF for debugging - echo ::set-output name=commit_tag::$(git describe --abbrev=0 --tags) - echo ::set-output name=commit_hash::$(git log --format=%h -1) - # Webhook info - echo ::set-output name=author_name::$(git log -1 "$GITHUB_SHA" --pretty="%aN") - echo ::set-output name=committer_name::$(git log -1 "$GITHUB_SHA" --pretty="%cN") - echo ::set-output name=commit_subject::$(git log -1 "$GITHUB_SHA" --pretty="%s") - echo ::set-output name=commit_message::$(git log -1 "$GITHUB_SHA" --pretty="%b") + cp bin/BCSTM-Player.elf ~/artifacts + echo "commit_tag=$(git describe --abbrev=0 --tags)" >> $GITHUB_ENV + echo "commit_hash=$(git log --format=%h -1)" >> $GITHUB_ENV + echo "author_name=$(git log -1 $GITHUB_SHA --pretty=%aN)" >> $GITHUB_ENV + echo "committer_name=$(git log -1 $GITHUB_SHA --pretty=%cN)" >> $GITHUB_ENV + echo "commit_subject=$(git log -1 $GITHUB_SHA --pretty=%s)" >> $GITHUB_ENV + echo "commit_message=$(git log -1 $GITHUB_SHA --pretty=%b)" >> $GITHUB_ENV + - name: Publish build to GH Actions uses: actions/upload-artifact@v4 with: path: ~/artifacts/* name: build - # Only run this for non-PR jobs. publish_build: runs-on: ubuntu-latest name: Publish build to NPI-D7/nightlys if: ${{ success() && !startsWith(github.ref, 'refs/pull') }} needs: build env: - COMMIT_TAG: ${{ needs.build.outputs.commit_tag }} - COMMIT_HASH: ${{ needs.build.outputs.commit_hash }} - AUTHOR_NAME: ${{ needs.build.outputs.author_name }} - COMMIT_MESSAGE: ${{ needs.build.outputs.commit_message }} + COMMIT_TAG: ${{ env.commit_tag }} + COMMIT_HASH: ${{ env.commit_hash }} + AUTHOR_NAME: ${{ env.author_name }} + COMMIT_MESSAGE: ${{ env.commit_message }} outputs: current_date: ${{ steps.commit.outputs.current_date }} steps: - name: Install tools - run: | - sudo apt-get update - sudo apt-get install qrencode -y + run: sudo apt-get update && sudo apt-get install qrencode -y + - name: Download artifacts uses: actions/download-artifact@v4 with: name: build path: build + - name: Upload to ${{ github.repository }} release if: ${{ startsWith(github.ref, 'refs/tags') }} run: | @@ -102,11 +103,12 @@ jobs: UPLOAD_URL="https://uploads.github.com/repos/${{ github.repository }}/releases/$ID/assets?name=$(basename $file)" curl -XPOST -H "$AUTH_HEADER" -H "$CONTENT_LENGTH" -H "$CONTENT_TYPE" --upload-file "$file" "$UPLOAD_URL" done + - name: Commit and push to NPI-D7/nightlys id: commit run: | CURRENT_DATE=$(date +"%Y%m%d-%H%M%S") - echo ::set-output name=current_date::$CURRENT_DATE + echo "current_date=$CURRENT_DATE" >> $GITHUB_OUTPUT git config --global user.email "tobid7@outlook.de" git config --global user.name "tobid7" git clone --depth 1 https://${{ secrets.TOKEN }}@github.com/NPI-D7/nightlys.git @@ -116,10 +118,11 @@ jobs: qrencode -o BCSTM-Player.png https://github.com/NPI-D7/nightlys/raw/v$CURRENT_DATE/builds/BCSTM-Player/BCSTM-Player.cia qrencode -o BCSTM-Player-release.png https://github.com/NPI-D7/nightlys/releases/download/$COMMIT_TAG/BCSTM-Player.cia cp ${{ github.workspace }}/build/* . - git stage . + git add . git commit -m "BCSTM-Player | $COMMIT_HASH" git tag v$CURRENT_DATE git push origin master v$CURRENT_DATE + - name: Release to idk run: | ls ${{ github.workspace }}/build @@ -135,4 +138,4 @@ jobs: CONTENT_TYPE="Content-Type: application/7z-x-compressed" UPLOAD_URL="https://uploads.github.com/repos/NPI-D7/nightlys/releases/$ID/assets?name=$(basename $file)" curl -XPOST -H "$AUTH_HEADER" -H "$CONTENT_LENGTH" -H "$CONTENT_TYPE" --upload-file "$file" "$UPLOAD_URL" - done \ No newline at end of file + done From 5ebf3c272a16af6df5a1120f942263d69124f763 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Fri, 28 Nov 2025 20:55:36 +0100 Subject: [PATCH 10/23] Add IntelliSel to inspector view --- source/inspector_view.cpp | 11 +++++++++-- source/inspector_view.hpp | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/source/inspector_view.cpp b/source/inspector_view.cpp index f008526..71ebd3d 100644 --- a/source/inspector_view.cpp +++ b/source/inspector_view.cpp @@ -96,6 +96,7 @@ void Inspector::Update() { if (Inp->IsDown(Inp->A)) { auto FSE = pDL.at(cursor.pIndex + sp); if (FSE->SubData.size() != 0) { + pLastPos.Push(PD::Pair(sp, cursor.GetIndex())); cursor.SetIndex(0); sp = 0; pStack.Push(pDL); @@ -104,8 +105,14 @@ void Inspector::Update() { } if (Inp->IsDown(Inp->B)) { - cursor.SetIndex(0); - sp = 0; + if (!pLastPos.IsEmpty()) { + sp = pLastPos.Top().First; + cursor.SetIndex(pLastPos.Top().Second); + pLastPos.Pop(); + } else { + sp = 0; + cursor.SetIndex(0); + } if (pStack.IsEmpty()) { Back(); } else { diff --git a/source/inspector_view.hpp b/source/inspector_view.hpp index 064f11a..854b3be 100644 --- a/source/inspector_view.hpp +++ b/source/inspector_view.hpp @@ -35,5 +35,6 @@ class Inspector : public Stage { std::vector pDL; std::vector List; PD::Stack> pStack; + PD::Stack> pLastPos; std::string pPath; }; \ No newline at end of file From 8833e92bb492be6e06228d139a7586693265f57b Mon Sep 17 00:00:00 2001 From: tobid7 Date: Fri, 28 Nov 2025 21:04:56 +0100 Subject: [PATCH 11/23] Fix CI AGAIN --- .github/workflows/main.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cf303fc..7ebbe71 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,12 +57,12 @@ jobs: cp bin/BCSTM-Player.3dsx ~/artifacts cp bin/BCSTM-Player.cia ~/artifacts cp bin/BCSTM-Player.elf ~/artifacts - echo "commit_tag=$(git describe --abbrev=0 --tags)" >> $GITHUB_ENV - echo "commit_hash=$(git log --format=%h -1)" >> $GITHUB_ENV - echo "author_name=$(git log -1 $GITHUB_SHA --pretty=%aN)" >> $GITHUB_ENV - echo "committer_name=$(git log -1 $GITHUB_SHA --pretty=%cN)" >> $GITHUB_ENV - echo "commit_subject=$(git log -1 $GITHUB_SHA --pretty=%s)" >> $GITHUB_ENV - echo "commit_message=$(git log -1 $GITHUB_SHA --pretty=%b)" >> $GITHUB_ENV + echo "commit_tag=$(git describe --abbrev=0 --tags)" >> $GITHUB_OUTPUT + echo "commit_hash=$(git log --format=%h -1)" >> $GITHUB_OUTPUT + echo "author_name=$(git log -1 $GITHUB_SHA --pretty=%aN)" >> $GITHUB_OUTPUT + echo "committer_name=$(git log -1 $GITHUB_SHA --pretty=%cN)" >> $GITHUB_OUTPUT + echo "commit_subject=$(git log -1 $GITHUB_SHA --pretty=%s)" >> $GITHUB_OUTPUT + echo "commit_message=$(git log -1 $GITHUB_SHA --pretty=%b)" >> $GITHUB_OUTPUT - name: Publish build to GH Actions uses: actions/upload-artifact@v4 @@ -76,10 +76,10 @@ jobs: if: ${{ success() && !startsWith(github.ref, 'refs/pull') }} needs: build env: - COMMIT_TAG: ${{ env.commit_tag }} - COMMIT_HASH: ${{ env.commit_hash }} - AUTHOR_NAME: ${{ env.author_name }} - COMMIT_MESSAGE: ${{ env.commit_message }} + COMMIT_TAG: ${{ needs.build.commit_tag }} + COMMIT_HASH: ${{ needs.build.commit_hash }} + AUTHOR_NAME: ${{ needs.build.author_name }} + COMMIT_MESSAGE: ${{ needs.build.commit_message }} outputs: current_date: ${{ steps.commit.outputs.current_date }} steps: From d0e9d868143f71ddcb0673732acb1982e3c7c24a Mon Sep 17 00:00:00 2001 From: tobid7 Date: Sat, 29 Nov 2025 01:09:17 +0100 Subject: [PATCH 12/23] The Real Fix (partial) - Also added Getters like in BCSTMV2 - Added some information to bottom screen --- ctrff | 2 +- source/bcstm_player.cpp | 12 +++++------- source/bcstm_player.hpp | 21 +++++++++++++++++++++ source/main.cpp | 14 ++++++++++++-- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/ctrff b/ctrff index 7197ecf..f6b81d7 160000 --- a/ctrff +++ b/ctrff @@ -1 +1 @@ -Subproject commit 7197ecff02b750600345166c01691743cb576dee +Subproject commit f6b81d701ac90deebe75dde8d78553ea9e7a68f0 diff --git a/source/bcstm_player.cpp b/source/bcstm_player.cpp index 85a0c8a..b4f4382 100644 --- a/source/bcstm_player.cpp +++ b/source/bcstm_player.cpp @@ -138,9 +138,8 @@ void BcstmPlayer::pFillBuffers() { pCurrentBlock == pCurrentFile.GetLoopEnd()) { pCurrentBlock = pCurrentFile.GetLoopStart(); pCurrentFile.ReadGotoBeginning(true); - } - if (!pCurrentFile.IsLooping() && - pCurrentBlock == pCurrentFile.GetLoopEnd()) { + } else if (!pCurrentFile.IsLooping() && + pCurrentBlock == pCurrentFile.GetLoopEnd()) { this->Stop(); } @@ -149,10 +148,9 @@ void BcstmPlayer::pFillBuffers() { ndspWaveBuf* buf = &pWaveBuf[chn_idx][buf_idx]; memset(buf, 0, sizeof(ndspWaveBuf)); - buf->adpcm_data = - (ndspAdpcmData*)pBufferData.at(chn_idx).at(buf_idx).Data(); - pCurrentFile.ReadBlock(pCurrentBlock, (PD::u8*)buf->adpcm_data); - DSP_FlushDataCache(buf->adpcm_data, pCurrentFile.GetBlockSize()); + buf->data_adpcm = pBufferData.at(chn_idx).at(buf_idx).Data(); + pCurrentFile.ReadBlock(pCurrentBlock, (PD::u8*)buf->data_adpcm); + DSP_FlushDataCache(buf->data_adpcm, pCurrentFile.GetBlockSize()); if (pCurrentBlock == 0) { buf->adpcm_data = diff --git a/source/bcstm_player.hpp b/source/bcstm_player.hpp index a672a05..23d9c62 100644 --- a/source/bcstm_player.hpp +++ b/source/bcstm_player.hpp @@ -20,6 +20,27 @@ class BcstmPlayer { void Stop(); void Pause(); + /** INFO */ + inline bool IsLooping() { return pCurrentFile.IsLooping(); } + inline bool IsLoaded() { return pIsLoaded; } + inline unsigned int GetLoopStart() { return pCurrentFile.GetLoopStart(); } + inline unsigned int GetLoopEnd() { return pCurrentFile.GetLoopEnd(); } + inline unsigned int GetChannelCount() { + return pCurrentFile.GetNumChannels(); + } + inline unsigned int GetTotal() { return pCurrentFile.GetNumBlocks(); } + inline unsigned int GetCurrent() { return pCurrentBlock; } + inline unsigned int GetSampleRate() { return pCurrentFile.GetSampleRate(); } + inline unsigned int GetSamples() const { + return 0; // (block_size * num_blocks) / sample_rate; + } + inline unsigned int GetBlcokSize() const { + return pCurrentFile.GetBlockSize(); + } + inline unsigned int GetBlockSamples() const { + return pCurrentFile.GetBlockSamples(); + } + void pFillBuffers(); /** Probably unused */ diff --git a/source/main.cpp b/source/main.cpp index 9fea4bf..3e8c998 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -96,13 +96,22 @@ void BottomScreenBeta(PD::LI::DrawList::Ref l) { l->DrawRectFilled(5, fvec2(310, 20), 0xff111111); l->DrawRectFilled(7, fvec2(306, 16), 0xff222222); float scale = 0.f; -#ifndef CTRFF_DECODE + D7::BcstmPlayer ll; + if (bcstm_ctrl.plr.GetTotal() != 0) { scale = (float)bcstm_ctrl.plr.GetCurrent() / (float)bcstm_ctrl.plr.GetTotal(); } -#endif l->DrawRectFilled(7, fvec2(306 * scale, 16), 0xff00ff00); + l->DrawText( + PD::fvec2(7, 28), + std::format("Info:\n Block Pos: {}/{}\n Sample Rate: {}\n Loop: {}\n " + "Loop Start: " + "{}\n Loop End: {}", + bcstm_ctrl.plr.GetCurrent(), bcstm_ctrl.plr.GetTotal(), + bcstm_ctrl.plr.GetSampleRate(), bcstm_ctrl.plr.IsLooping(), + bcstm_ctrl.plr.GetLoopStart(), bcstm_ctrl.plr.GetLoopEnd()), + 0xffffffff); } int main() { @@ -122,6 +131,7 @@ int main() { font->LoadTTF("romfs:/fonts/ComicNeue.ttf"); auto rl2 = PD::LI::DrawList::New(PD::Ctr::GetWhiteTex()); auto rl3 = PD::LI::DrawList::New(PD::Ctr::GetWhiteTex()); + rl3->SetFont(font); Filebrowser = FileMgr::New(PD::Ctr::GetContext().Inp, PD::Ctr::GetWhiteTex(), font); FileInspector = From 44d37ba0129e81a06a0820f98e853e260bf816cd Mon Sep 17 00:00:00 2001 From: tobid7 Date: Sat, 29 Nov 2025 01:34:08 +0100 Subject: [PATCH 13/23] Create a base class for bcstm players --- source/bcstm/base.hpp | 40 ++++++++++++++++++++++++++++++++++++++++ source/bcstm/bcstmv2.cpp | 26 ++++++++------------------ source/bcstm/bcstmv2.hpp | 34 ++++++++++++++++++---------------- source/bcstm_player.hpp | 3 ++- source/main.cpp | 14 ++++++++------ 5 files changed, 76 insertions(+), 41 deletions(-) create mode 100644 source/bcstm/base.hpp diff --git a/source/bcstm/base.hpp b/source/bcstm/base.hpp new file mode 100644 index 0000000..f79a2ad --- /dev/null +++ b/source/bcstm/base.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +/** + * Base class for In app switching between CTRFF and BCSTMV2 Decoders + */ + +namespace D7 { +class BCSTMPlayerBase { + public: + BCSTMPlayerBase() = default; + BCSTMPlayerBase(const std::string& n) : pName(n) {} + virtual ~BCSTMPlayerBase() = default; + + virtual void LoadFile(const std::string& path) = 0; + virtual void Stream() = 0; + + virtual void CleanUp() = 0; + + virtual void Play() = 0; + virtual void Stop() = 0; + virtual void Pause() = 0; + + /** INFO */ + virtual bool IsLooping() { return false; } + virtual bool IsLoaded() { return false; } + virtual unsigned int GetLoopStart() { return 0; } + virtual unsigned int GetLoopEnd() { return 0; } + virtual unsigned int GetChannelCount() { return 0; } + virtual unsigned int GetTotal() { return 0; } + virtual unsigned int GetCurrent() { return 0; } + virtual unsigned int GetSampleRate() { return 0; } + virtual unsigned int GetSamples() const { return 0; } + virtual unsigned int GetBlcokSize() const { return 0; } + virtual unsigned int GetBlockSamples() const { return 0; } + + std::string pName = "Unknown"; +}; +} // namespace D7 \ No newline at end of file diff --git a/source/bcstm/bcstmv2.cpp b/source/bcstm/bcstmv2.cpp index c789293..0075904 100644 --- a/source/bcstm/bcstmv2.cpp +++ b/source/bcstm/bcstmv2.cpp @@ -2,12 +2,11 @@ #include -bool D7::BCSTM2::LoadFile(const std::string& path) { +void D7::BCSTM2::LoadFile(const std::string& path) { Stop(); pFile.open(path, std::ios::in | std::ios::binary); if (!pFile) { - std::cout << "BCSTM [ERROR]: Unable to load File!" << std::endl; - return false; + throw std::runtime_error("BCSTM [ERROR]: Unable to load File!"); } pBigEndian = false; is_little_endian = true; // default to true @@ -18,8 +17,7 @@ bool D7::BCSTM2::LoadFile(const std::string& path) { } if (magic != 0x4D545343) { // CSTM pFile.close(); - std::cout << "BCSTM [ERROR]: File is invalid!" << std::endl; - return false; + throw std::runtime_error("BCSTM [ERROR]: File is invalid!"); } pFile.seekg(0x10); auto sbc = Read(); @@ -36,20 +34,18 @@ bool D7::BCSTM2::LoadFile(const std::string& path) { data_offset = off; } if (!info_offset || !data_offset) { - std::cout << "BCSTM [ERROR]: Data/Info Section not found" << std::endl; - return false; + throw std::runtime_error("BCSTM [ERROR]: Data/Info Section not found"); } pFile.seekg((info_offset + 0x20)); if (Read() != 2) { - std::cout << "BCSTM [ERROR]: Unknown Error!" << std::endl; - return false; + throw std::runtime_error( + "BCSTM [ERROR]: Only DSP ADPCM Format is supported!"); } is_looping = Read(); channel_count = Read(); /*if (channel_count > 2) { - std::cout << "BCSTM [ERROR]: File has not 2 channels!" << std::endl; - return false; + throw std::runtime_error("BCSTM [ERROR]: File has not 2 channels!"); }*/ pFile.seekg((info_offset + 0x24)); sample_rate = Read(); @@ -69,11 +65,7 @@ bool D7::BCSTM2::LoadFile(const std::string& path) { while (Read() != ChannelInfo) { // Find Channel Info } - std::ofstream off("test.dump.txt"); - off << pFile.tellg() << std::endl; file_advance(Read() + channel_count * 8 - 12); - off << pFile.tellg() << std::endl; - off.close(); // get adpcm data for (unsigned int i = 0; i < channel_count; i++) { pFile.read(reinterpret_cast(adpcm_coefs[i]), @@ -90,10 +82,9 @@ bool D7::BCSTM2::LoadFile(const std::string& path) { pFile.seekg((data_offset + 0x20)); is_loaded = true; - return true; } -void D7::BCSTM2::Update() { this->stream(); } +void D7::BCSTM2::Stream() { this->stream(); } void D7::BCSTM2::Play() { if (is_paused) { @@ -194,7 +185,6 @@ void D7::BCSTM2::Stop() { active_channels = 0; is_paused = false; is_looping = false; - std::cout << "BCSTM [ERROR]: None"; is_loaded = false; if (!is_streaming) return; is_streaming = false; diff --git a/source/bcstm/bcstmv2.hpp b/source/bcstm/bcstmv2.hpp index a9055e8..74deda0 100644 --- a/source/bcstm/bcstmv2.hpp +++ b/source/bcstm/bcstmv2.hpp @@ -2,15 +2,16 @@ #include <3ds.h> +#include #include #include #include #include namespace D7 { -class BCSTM2 { +class BCSTM2 : public BCSTMPlayerBase { public: - BCSTM2() {} + BCSTM2() : BCSTMPlayerBase("BCSTMV2") {} ~BCSTM2() { Stop(); } template @@ -47,25 +48,26 @@ class BCSTM2 { return o; } - bool LoadFile(const std::string& path); - void Update(); + void LoadFile(const std::string& path); + void Stream(); void Play(); void Pause(); void Stop(); - - inline bool IsLooping() { return this->is_looping; } - inline bool IsLoaded() { return this->is_loaded; } - inline unsigned int GetLoopStart() { return this->loop_start; } - inline unsigned int GetLoopEnd() { return this->loop_end; } - inline unsigned int GetChannelCount() { return this->channel_count; } - inline unsigned int GetTotal() { return this->num_blocks; } - inline unsigned int GetCurrent() { return this->current_block; } - inline unsigned int GetSampleRate() { return this->sample_rate; } - inline unsigned int GetSamples() const { + void CleanUp() { Stop(); } + + bool IsLooping() { return this->is_looping; } + bool IsLoaded() { return this->is_loaded; } + unsigned int GetLoopStart() { return this->loop_start; } + unsigned int GetLoopEnd() { return this->loop_end; } + unsigned int GetChannelCount() { return this->channel_count; } + unsigned int GetTotal() { return this->num_blocks; } + unsigned int GetCurrent() { return this->current_block; } + unsigned int GetSampleRate() { return this->sample_rate; } + unsigned int GetSamples() const { return (block_size * num_blocks) / sample_rate; } - inline unsigned int GetBlcokSize() const { return block_size; } - inline unsigned int GetBlockSamples() const { return block_samples; } + unsigned int GetBlcokSize() const { return block_size; } + unsigned int GetBlockSamples() const { return block_samples; } bool pBigEndian = false; std::fstream pFile; diff --git a/source/bcstm_player.hpp b/source/bcstm_player.hpp index 23d9c62..ba4d9dc 100644 --- a/source/bcstm_player.hpp +++ b/source/bcstm_player.hpp @@ -2,11 +2,12 @@ #include <3ds.h> +#include #include #include namespace D7 { -class BcstmPlayer { +class BcstmPlayer : public BCSTMPlayerBase { public: BcstmPlayer() = default; ~BcstmPlayer() { CleanUp(); } diff --git a/source/main.cpp b/source/main.cpp index 3e8c998..1c4a477 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -49,11 +49,7 @@ void Append(PD::LI::DrawList::Ref l, int index, fvec2 position, fvec2 size, void BCSTM_Handler(BCSTM_Ctrl* ctrl) { while (true) { if (ctrl->pFileLoaded) { -#ifdef CTRFF_DECODE ctrl->plr.Stream(); -#else - ctrl->plr.Update(); -#endif } if (ctrl->pRequests.Size() == 0) { std::this_thread::sleep_for(std::chrono::milliseconds(200)); @@ -107,10 +103,16 @@ void BottomScreenBeta(PD::LI::DrawList::Ref l) { PD::fvec2(7, 28), std::format("Info:\n Block Pos: {}/{}\n Sample Rate: {}\n Loop: {}\n " "Loop Start: " - "{}\n Loop End: {}", + "{}\n Loop End: {}\n Decoder: {}", bcstm_ctrl.plr.GetCurrent(), bcstm_ctrl.plr.GetTotal(), bcstm_ctrl.plr.GetSampleRate(), bcstm_ctrl.plr.IsLooping(), - bcstm_ctrl.plr.GetLoopStart(), bcstm_ctrl.plr.GetLoopEnd()), + bcstm_ctrl.plr.GetLoopStart(), bcstm_ctrl.plr.GetLoopEnd(), +#ifdef CTRFF_DECODE + "CTRFF (WIP)" +#else + "BCSTMV2" +#endif + ), 0xffffffff); } From a8a9dbb55b9a3ccbe7fe542eeed73c261d76d94f Mon Sep 17 00:00:00 2001 From: tobid7 Date: Sat, 29 Nov 2025 01:51:05 +0100 Subject: [PATCH 14/23] Updates - Remove CTRFF_DECODE Flag - Use BCSTMPLayerBase Pointer in BCSMT_Ctrl - Move bcstm_player.cpp to ctrff_decode.cpp - Add a GetName to base class - Update main.cpp to support the new base pointer --- CMakeLists.txt | 9 +--- source/bcstm/base.hpp | 2 + .../ctrff_decode.cpp} | 16 +++--- .../ctrff_decode.hpp} | 6 +-- source/bcstm_ctrl.hpp | 11 +--- source/main.cpp | 53 ++++++++----------- 6 files changed, 37 insertions(+), 60 deletions(-) rename source/{bcstm_player.cpp => bcstm/ctrff_decode.cpp} (95%) rename source/{bcstm_player.hpp => bcstm/ctrff_decode.hpp} (93%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5945ba1..c1a5b59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,9 +38,6 @@ set(3DS_IP "192.168.2.224" CACHE STRING "3ds IP (make send)") # Set Project project(BCSTM-Player LANGUAGES C CXX VERSION 2.0.0) -# Decode with CTRFF -option(CTRFF_DECODE "Decode music data with ctrff (WIP)" OFF) - # Enable Compile Command Export set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -70,7 +67,7 @@ add_executable(BCSTM-Player #source/config.cpp source/bcstm/bcstmv2.cpp - source/bcstm_player.cpp + source/bcstm/ctrff_decode.cpp source/flex/objects.cpp ) @@ -87,10 +84,6 @@ target_compile_definitions(BCSTM-Player PUBLIC -DGIT_COMMIT="${GIT_SHORT_HASH}" ) -if(${CTRFF_DECODE}) - target_compile_definitions(BCSTM-Player PUBLIC -DCTRFF_DECODE=1) -endif() - # Command to send to console (make send) add_custom_target( send diff --git a/source/bcstm/base.hpp b/source/bcstm/base.hpp index f79a2ad..173cc23 100644 --- a/source/bcstm/base.hpp +++ b/source/bcstm/base.hpp @@ -35,6 +35,8 @@ class BCSTMPlayerBase { virtual unsigned int GetBlcokSize() const { return 0; } virtual unsigned int GetBlockSamples() const { return 0; } + std::string GetName() const { return pName; } + std::string pName = "Unknown"; }; } // namespace D7 \ No newline at end of file diff --git a/source/bcstm_player.cpp b/source/bcstm/ctrff_decode.cpp similarity index 95% rename from source/bcstm_player.cpp rename to source/bcstm/ctrff_decode.cpp index b4f4382..1865ca9 100644 --- a/source/bcstm_player.cpp +++ b/source/bcstm/ctrff_decode.cpp @@ -1,8 +1,8 @@ -#include +#include #include /** std::memset :( */ namespace D7 { -void BcstmPlayer::LoadFile(const std::string& path) { +void CTRFFDec::LoadFile(const std::string& path) { CleanUp(); try { pCurrentFile.LoadFile(path); @@ -21,7 +21,7 @@ void BcstmPlayer::LoadFile(const std::string& path) { pIsLoaded = true; } -void BcstmPlayer::Play() { +void CTRFFDec::Play() { if (pIsPaused) { for (PD::u8 i = 0; i < pCurrentFile.GetNumChannels(); i++) { ndspChnSetPaused(pChannels[i], false); @@ -93,7 +93,7 @@ void BcstmPlayer::Play() { pIsStreaming = true; } -void BcstmPlayer::Pause() { +void CTRFFDec::Pause() { if (!pIsStreaming) { return; } @@ -103,7 +103,7 @@ void BcstmPlayer::Pause() { } } -void BcstmPlayer::Stop() { +void CTRFFDec::Stop() { if (!pIsStreaming) { return; } @@ -114,7 +114,7 @@ void BcstmPlayer::Stop() { pIsStreaming = false; } -void BcstmPlayer::Stream() { +void CTRFFDec::Stream() { pCurrentTime = svcGetSystemTick(); if (pCurrentTime - pLastTime >= 100000000 && pIsLoaded) { if (!pIsStreaming) return; @@ -123,7 +123,7 @@ void BcstmPlayer::Stream() { } } -void BcstmPlayer::pFillBuffers() { +void CTRFFDec::pFillBuffers() { for (PD::u32 buf_idx = 0; buf_idx < BufferCount; buf_idx++) { bool all_ready = true; for (PD::u32 ch = 0; ch < pCurrentFile.GetNumChannels(); ch++) { @@ -172,7 +172,7 @@ void BcstmPlayer::pFillBuffers() { } } -void BcstmPlayer::CleanUp() { +void CTRFFDec::CleanUp() { Stop(); if (pIsLoaded) { try { diff --git a/source/bcstm_player.hpp b/source/bcstm/ctrff_decode.hpp similarity index 93% rename from source/bcstm_player.hpp rename to source/bcstm/ctrff_decode.hpp index ba4d9dc..60ecdbc 100644 --- a/source/bcstm_player.hpp +++ b/source/bcstm/ctrff_decode.hpp @@ -7,10 +7,10 @@ #include namespace D7 { -class BcstmPlayer : public BCSTMPlayerBase { +class CTRFFDec : public BCSTMPlayerBase { public: - BcstmPlayer() = default; - ~BcstmPlayer() { CleanUp(); } + CTRFFDec() : BCSTMPlayerBase("CTRFF (WIP)") {}; + ~CTRFFDec() { CleanUp(); } void LoadFile(const std::string& path); void Stream(); diff --git a/source/bcstm_ctrl.hpp b/source/bcstm_ctrl.hpp index 0524fd9..e3168f4 100644 --- a/source/bcstm_ctrl.hpp +++ b/source/bcstm_ctrl.hpp @@ -1,10 +1,7 @@ #pragma once -#ifdef CTRFF_DECODE -#include -#else #include -#endif +#include struct BCSTM_Ctrl { enum ReqType { @@ -28,11 +25,7 @@ struct BCSTM_Ctrl { pRequests.PushBack(Request(t, dat)); } -#ifdef CTRFF_DECODE - D7::BcstmPlayer plr; -#else - D7::BCSTM2 plr; -#endif + D7::BCSTMPlayerBase* player = nullptr; PD::List pRequests; bool pFileLoaded = false; }; diff --git a/source/main.cpp b/source/main.cpp index 1c4a477..697b383 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,6 +1,4 @@ -#include #include -#include #include #include #include @@ -14,15 +12,10 @@ /** * Code is very unstable currently cause * 1. PD Ctr Extension is unfinished pre alpha - * 2. Still using the bcstm/bcstmv2 impl which is based on the initial code - * implementing the 1 to 8 channel support of bcstm_player.cpp - * 3. bcstm_player.cpp is unused dead code due to it somehow crashes the fs - * module on data block read - * 4. inspector_view contains some bugs - * 5. the flex ui engine is a small lib planned to be more simple then ui7 but + * 2. the flex ui engine is a small lib planned to be more simple then ui7 but * the way it works is bad - * 6. The whole code around the stage_mgr is just in alpha state - * 7. The whole code of bcstm_player 2.0.0 is completly hardcoded with extern + * 3. The whole code around the stage_mgr is just in alpha state + * 4. The whole code of bcstm_player 2.0.0 is completly hardcoded with extern * references through all the code and still very unfinished */ @@ -47,9 +40,10 @@ void Append(PD::LI::DrawList::Ref l, int index, fvec2 position, fvec2 size, .36f + .38f * color_effect)); } void BCSTM_Handler(BCSTM_Ctrl* ctrl) { + ctrl->player = new D7::BCSTM2; // Stable while (true) { if (ctrl->pFileLoaded) { - ctrl->plr.Stream(); + ctrl->player->Stream(); } if (ctrl->pRequests.Size() == 0) { std::this_thread::sleep_for(std::chrono::milliseconds(200)); @@ -57,7 +51,7 @@ void BCSTM_Handler(BCSTM_Ctrl* ctrl) { auto t = ctrl->pRequests.Front(); if (t.req == BCSTM_Ctrl::OpenFile) { try { - ctrl->plr.LoadFile(t.req_dat); + ctrl->player->LoadFile(t.req_dat); /** * Set to true cause it would get set to false * if an error got thrown @@ -68,13 +62,13 @@ void BCSTM_Handler(BCSTM_Ctrl* ctrl) { ctrl->pFileLoaded = false; } } else if (t.req == BCSTM_Ctrl::CloseFile) { - ctrl->plr.Stop(); + ctrl->player->Stop(); } else if (t.req == BCSTM_Ctrl::Stop) { - ctrl->plr.Stop(); + ctrl->player->Stop(); } else if (t.req == BCSTM_Ctrl::Play) { - ctrl->plr.Play(); + ctrl->player->Play(); } else if (t.req == BCSTM_Ctrl::Pause) { - ctrl->plr.Pause(); + ctrl->player->Pause(); } else if (t.req == BCSTM_Ctrl::KillThread) { break; /** Break the loop */ } @@ -92,27 +86,22 @@ void BottomScreenBeta(PD::LI::DrawList::Ref l) { l->DrawRectFilled(5, fvec2(310, 20), 0xff111111); l->DrawRectFilled(7, fvec2(306, 16), 0xff222222); float scale = 0.f; - D7::BcstmPlayer ll; - if (bcstm_ctrl.plr.GetTotal() != 0) { - scale = - (float)bcstm_ctrl.plr.GetCurrent() / (float)bcstm_ctrl.plr.GetTotal(); + if (bcstm_ctrl.player->GetTotal() != 0) { + scale = (float)bcstm_ctrl.player->GetCurrent() / + (float)bcstm_ctrl.player->GetTotal(); } l->DrawRectFilled(7, fvec2(306 * scale, 16), 0xff00ff00); l->DrawText( PD::fvec2(7, 28), - std::format("Info:\n Block Pos: {}/{}\n Sample Rate: {}\n Loop: {}\n " - "Loop Start: " - "{}\n Loop End: {}\n Decoder: {}", - bcstm_ctrl.plr.GetCurrent(), bcstm_ctrl.plr.GetTotal(), - bcstm_ctrl.plr.GetSampleRate(), bcstm_ctrl.plr.IsLooping(), - bcstm_ctrl.plr.GetLoopStart(), bcstm_ctrl.plr.GetLoopEnd(), -#ifdef CTRFF_DECODE - "CTRFF (WIP)" -#else - "BCSTMV2" -#endif - ), + std::format( + "Info:\n Block Pos: {}/{}\n Sample Rate: {}\n Loop: {}\n " + "Loop Start: " + "{}\n Loop End: {}\n Decoder: {}", + bcstm_ctrl.player->GetCurrent(), bcstm_ctrl.player->GetTotal(), + bcstm_ctrl.player->GetSampleRate(), bcstm_ctrl.player->IsLooping(), + bcstm_ctrl.player->GetLoopStart(), bcstm_ctrl.player->GetLoopEnd(), + bcstm_ctrl.player->GetName()), 0xffffffff); } From ded644bf8aa3c3fe4ff5b2c5becbf09c7669e455 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Sat, 29 Nov 2025 02:46:05 +0100 Subject: [PATCH 15/23] Add Switch Decoder Ability - Cleanup cmake and add special options for release --- CMakeLists.txt | 9 +++++---- source/bcstm/base.hpp | 18 +++++++++++++++++- source/bcstm/bcstmv2.hpp | 2 +- source/bcstm/ctrff_decode.cpp | 17 ++++------------- source/bcstm/ctrff_decode.hpp | 4 +++- source/bcstm_ctrl.hpp | 1 + source/main.cpp | 29 +++++++++++++++++++++++++++++ source/pd_ctr_ext.cpp | 2 ++ 8 files changed, 62 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1a5b59..9de9985 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,10 +48,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE) # Better location to not always crash Citra / Azahar set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) -# Set Special C and CXX flags -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-psabi") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} -fno-rtti") - add_subdirectory(palladium) add_subdirectory(palladium/backends/3ds) add_subdirectory(ctrff) @@ -83,6 +79,11 @@ target_compile_definitions(BCSTM-Player PUBLIC -DVERSION="${PROJECT_VERSION}" -DGIT_COMMIT="${GIT_SHORT_HASH}" ) +if(${CMAKE_BUILD_TYPE} STREQUAL "Release") + target_compile_options(BCSTM-Player PUBLIC + -fno-rtti + ) +endif() # Command to send to console (make send) add_custom_target( diff --git a/source/bcstm/base.hpp b/source/bcstm/base.hpp index 173cc23..b083711 100644 --- a/source/bcstm/base.hpp +++ b/source/bcstm/base.hpp @@ -9,8 +9,23 @@ namespace D7 { class BCSTMPlayerBase { public: + using Features = unsigned int; + enum Features_ : Features { + None = 0, + SingleChannel = 1 << 0, + DualChannel = 1 << 1, + QuadraChannel = 1 << 2, + HexaChannel = 1 << 3, + OktaChannel = 1 << 4, + AllChannels = + SingleChannel | DualChannel | QuadraChannel | HexaChannel | OktaChannel, + NonLoopEnd = 1 << 5, + FormatBCSTM = 1 << 6, + Default = + FormatBCSTM | SingleChannel | DualChannel, // all support these iirc + }; BCSTMPlayerBase() = default; - BCSTMPlayerBase(const std::string& n) : pName(n) {} + BCSTMPlayerBase(const std::string& n, Features f) : pName(n), pFeatures(f) {} virtual ~BCSTMPlayerBase() = default; virtual void LoadFile(const std::string& path) = 0; @@ -38,5 +53,6 @@ class BCSTMPlayerBase { std::string GetName() const { return pName; } std::string pName = "Unknown"; + Features pFeatures = Default; }; } // namespace D7 \ No newline at end of file diff --git a/source/bcstm/bcstmv2.hpp b/source/bcstm/bcstmv2.hpp index 74deda0..d3f3446 100644 --- a/source/bcstm/bcstmv2.hpp +++ b/source/bcstm/bcstmv2.hpp @@ -11,7 +11,7 @@ namespace D7 { class BCSTM2 : public BCSTMPlayerBase { public: - BCSTM2() : BCSTMPlayerBase("BCSTMV2") {} + BCSTM2() : BCSTMPlayerBase("BCSTMV2", FormatBCSTM | AllChannels) {} ~BCSTM2() { Stop(); } template diff --git a/source/bcstm/ctrff_decode.cpp b/source/bcstm/ctrff_decode.cpp index 1865ca9..fd73c13 100644 --- a/source/bcstm/ctrff_decode.cpp +++ b/source/bcstm/ctrff_decode.cpp @@ -4,11 +4,8 @@ namespace D7 { void CTRFFDec::LoadFile(const std::string& path) { CleanUp(); - try { - pCurrentFile.LoadFile(path); - } catch (const std::exception& e) { - throw std::runtime_error(e.what()); - } + pCurrentFile.LoadFile(path); + /** Resize the Player Internal Data holders */ /** Using this allows to not have to much memory allocated */ pChannels.Resize(pCurrentFile.GetNumChannels()); @@ -39,7 +36,7 @@ void CTRFFDec::Play() { pChannels[i]++; } if (pChannels[i] == 24) { - throw std::range_error( + throw std::runtime_error( "BCSTM Player: Out of Range (channel == 24) detected!"); } pActiveChannels |= 1 << pChannels[i]; @@ -174,13 +171,7 @@ void CTRFFDec::pFillBuffers() { void CTRFFDec::CleanUp() { Stop(); - if (pIsLoaded) { - try { - pCurrentFile.CleanUp(); - } catch (const std::exception& e) { - throw std::runtime_error(e.what()); - } - } + pCurrentFile.CleanUp(); pIsLoaded = false; pIsStreaming = false; pIsPaused = false; diff --git a/source/bcstm/ctrff_decode.hpp b/source/bcstm/ctrff_decode.hpp index 60ecdbc..053bcd8 100644 --- a/source/bcstm/ctrff_decode.hpp +++ b/source/bcstm/ctrff_decode.hpp @@ -9,7 +9,9 @@ namespace D7 { class CTRFFDec : public BCSTMPlayerBase { public: - CTRFFDec() : BCSTMPlayerBase("CTRFF (WIP)") {}; + CTRFFDec() : BCSTMPlayerBase("CTRFF (WIP)", FormatBCSTM | AllChannels) { + CleanUp(); + }; ~CTRFFDec() { CleanUp(); } void LoadFile(const std::string& path); diff --git a/source/bcstm_ctrl.hpp b/source/bcstm_ctrl.hpp index e3168f4..55d0baa 100644 --- a/source/bcstm_ctrl.hpp +++ b/source/bcstm_ctrl.hpp @@ -11,6 +11,7 @@ struct BCSTM_Ctrl { Pause, /** Can be direct accessded due to no overhead */ Stop, /** No Overhead */ KillThread, /** Should be called on close */ + SwitchDec, /** Stops and switches decoder */ }; struct Request { Request(ReqType t, const std::string& d) { diff --git a/source/main.cpp b/source/main.cpp index 697b383..bafe301 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -39,6 +39,13 @@ void Append(PD::LI::DrawList::Ref l, int index, fvec2 position, fvec2 size, PD::Color(.94f - .17f * color_effect, .61f - .25f * color_effect, .36f + .38f * color_effect)); } + +#ifndef __RTTI +/// IF NO RTTI WE INITIALIZE THIS AS FALSE +// CAUSE IT GETS REVERSED LATER +static bool v2 = false; +#endif + void BCSTM_Handler(BCSTM_Ctrl* ctrl) { ctrl->player = new D7::BCSTM2; // Stable while (true) { @@ -50,6 +57,7 @@ void BCSTM_Handler(BCSTM_Ctrl* ctrl) { } else { auto t = ctrl->pRequests.Front(); if (t.req == BCSTM_Ctrl::OpenFile) { +#if __EXCEPTIONS try { ctrl->player->LoadFile(t.req_dat); /** @@ -61,6 +69,10 @@ void BCSTM_Handler(BCSTM_Ctrl* ctrl) { std::cout << "BCSTM CTRL Error: " << e.what() << std::endl; ctrl->pFileLoaded = false; } +#else + ctrl->player->LoadFile(t.req_dat); + ctrl->pFileLoaded = true; +#endif } else if (t.req == BCSTM_Ctrl::CloseFile) { ctrl->player->Stop(); } else if (t.req == BCSTM_Ctrl::Stop) { @@ -71,6 +83,19 @@ void BCSTM_Handler(BCSTM_Ctrl* ctrl) { ctrl->player->Pause(); } else if (t.req == BCSTM_Ctrl::KillThread) { break; /** Break the loop */ + } else if (t.req == BCSTM_Ctrl::SwitchDec) { + ctrl->player->Stop(); +#ifdef __RTTI + bool v2 = dynamic_cast(ctrl->player); +#else + v2 = !v2; +#endif + delete ctrl->player; + if (v2) { + ctrl->player = new D7::CTRFFDec(); + } else { + ctrl->player = new D7::BCSTM2(); + } } ctrl->pRequests.PopFront(); } @@ -134,6 +159,10 @@ int main() { time.Update(); rl2->SetFont(font); + if (PD::Ctr::GetContext().Inp->IsUp(PD::Hid::Y)) { + bcstm_ctrl.DoRequest(BCSTM_Ctrl::SwitchDec); + } + rl2->DrawRectFilled(fvec2(0, 0), fvec2(400, 240), 0xff64c9fd); for (int i = 0; i < 44; i++) Append(rl2, i, fvec2(0, 0), fvec2(400, 240), (float)time.GetSeconds()); diff --git a/source/pd_ctr_ext.cpp b/source/pd_ctr_ext.cpp index 2840903..9645ce1 100644 --- a/source/pd_ctr_ext.cpp +++ b/source/pd_ctr_ext.cpp @@ -31,6 +31,7 @@ void HandleException(const std::string& e) { } void CxxExceptionHandler() { +#ifdef __EXCEPTIONS std::exception_ptr e_ = std::current_exception(); if (e_) { try { @@ -41,6 +42,7 @@ void CxxExceptionHandler() { HandleException("Unknown Exception"); } } +#endif std::abort(); } From cdb4e3a82655e4f522c35df715de3c00e186fee4 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Sun, 30 Nov 2025 02:29:02 +0100 Subject: [PATCH 16/23] Update - Add cheuble to credits (cause i used his bcstm playing code from freeshop) - Add a settings menu --- CMakeLists.txt | 2 + README.md | 1 + source/filebrowser.cpp | 4 + source/main.cpp | 12 ++- source/settings.cpp | 188 +++++++++++++++++++++++++++++++++++++++++ source/settings.hpp | 50 +++++++++++ source/stages.hpp | 4 +- 7 files changed, 253 insertions(+), 8 deletions(-) create mode 100644 source/settings.cpp create mode 100644 source/settings.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9de9985..085134f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ add_executable(BCSTM-Player source/pd_ctr_ext.cpp source/filebrowser.cpp source/inspector_view.cpp + source/settings.cpp source/stagemgr.cpp #ource/app.cpp #source/config.cpp @@ -79,6 +80,7 @@ target_compile_definitions(BCSTM-Player PUBLIC -DVERSION="${PROJECT_VERSION}" -DGIT_COMMIT="${GIT_SHORT_HASH}" ) + if(${CMAKE_BUILD_TYPE} STREQUAL "Release") target_compile_options(BCSTM-Player PUBLIC -fno-rtti diff --git a/README.md b/README.md index cfea097..714f90e 100644 --- a/README.md +++ b/README.md @@ -13,4 +13,5 @@ - [tobid7](https://github.com/tobid7): Lead Developer, author of palladium, ctrff - [devkitpro](https://github.com/devkitpro): libctru, citro3d +- [cheuble](https://github.com/cheuble): Source of BCSTMV1 Decoding code (based on freeshop) - [3dbrew](https://www.3dbrew.org/wiki/BCSTM): BCSTM Documentation diff --git a/source/filebrowser.cpp b/source/filebrowser.cpp index 3f58de2..d4a679c 100644 --- a/source/filebrowser.cpp +++ b/source/filebrowser.cpp @@ -134,6 +134,10 @@ void FileMgr::Update() { Goto(FileInspector); } } + + if (Inp->IsDown(Inp->L)) { + Goto(Settings); + } } pShowHelp = Inp->IsHeld(Inp->Select); diff --git a/source/main.cpp b/source/main.cpp index bafe301..03978ed 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -40,12 +41,6 @@ void Append(PD::LI::DrawList::Ref l, int index, fvec2 position, fvec2 size, .36f + .38f * color_effect)); } -#ifndef __RTTI -/// IF NO RTTI WE INITIALIZE THIS AS FALSE -// CAUSE IT GETS REVERSED LATER -static bool v2 = false; -#endif - void BCSTM_Handler(BCSTM_Ctrl* ctrl) { ctrl->player = new D7::BCSTM2; // Stable while (true) { @@ -88,7 +83,7 @@ void BCSTM_Handler(BCSTM_Ctrl* ctrl) { #ifdef __RTTI bool v2 = dynamic_cast(ctrl->player); #else - v2 = !v2; + bool v2 = ctrl->player->GetName() == "BCSTMV2"; #endif delete ctrl->player; if (v2) { @@ -104,6 +99,7 @@ void BCSTM_Handler(BCSTM_Ctrl* ctrl) { FileMgr::Ref Filebrowser; Inspector::Ref FileInspector; +Settings::Ref Settings; BCSTM_Ctrl bcstm_ctrl; void BottomScreenBeta(PD::LI::DrawList::Ref l) { @@ -152,6 +148,8 @@ int main() { FileMgr::New(PD::Ctr::GetContext().Inp, PD::Ctr::GetWhiteTex(), font); FileInspector = Inspector::New(PD::Ctr::GetContext().Inp, PD::Ctr::GetWhiteTex(), font); + Settings = + Settings::New(PD::Ctr::GetContext().Inp, PD::Ctr::GetWhiteTex(), font); Stage::Goto(Filebrowser); PD::Timer time; diff --git a/source/settings.cpp b/source/settings.cpp new file mode 100644 index 0000000..c170051 --- /dev/null +++ b/source/settings.cpp @@ -0,0 +1,188 @@ +#include +#include + +void Settings::Update() { + delta.Update(); + cursor.Update(delta.GetSeconds() * 1000.f); + delta.Reset(); + + for (int i = 0; i < 12; i++) { + Top->Rect() + .SetPos(fvec2(0, 18 + 17 * i)) + .SetSize(fvec2(200, 17)) + .SetColor(((i % 2) == 0) ? PD::Color("#222222aa") + : PD::Color("#333333aa")); + Top->Rect() + .SetPos(fvec2(200, 18 + 17 * i)) + .SetSize(fvec2(200, 17)) + .SetColor(((i % 2) != 0) ? PD::Color("#222222aa") + : PD::Color("#333333aa")); + } + Top->Rect() + .SetPos(cursor) + .SetSize(fvec2(400, 17)) + .SetColor(PD::Color("#222222cc")); + for (int i = 0; i < int(pDL.size() > 12 ? 12 : pDL.size()); i++) { + Top->Text(pDL.at(sp + i)->First) + .SetPos(fvec2(5, 18 + 17 * i)) + .SetColor(PD::Colors::White); + Top->Text(pDL.at(sp + i)->Second) + .SetPos(fvec2(205, 18 + 17 * i)) + .SetColor(PD::Colors::White); + } + Top->Rect().SetColor(DesignerHeader).SetPos(0).SetSize(fvec2(400, 18)); + Top->Text("BCSTM-Player -> Settings") + .SetPos(fvec2(5, 1)) + .SetColor(PD::Colors::White); + Top->Rect() + .SetPos(fvec2(0, 222)) + .SetSize(fvec2(400, 18)) + .SetColor(DesignerHeader); + if (pDL.size() > 12) { + float rect_h = (12.f / (float)pDL.size()) * 204.f; + /** Make sure the rect is still visible */ + rect_h = std::clamp(rect_h, 10.f, 204.f); + float rect_pos = + 18.f + ((float)sp / (float)(pDL.size() - 12)) * (204.f - rect_h); + Top->Rect() + .SetPos(fvec2(396, rect_pos)) + .SetSize(fvec2(4, rect_h)) + .SetColor(PD::Colors::DarkGray); + } + + if (Inp->IsUp(Inp->Down) && sp + cursor.pIndex < (int)pDL.size() - 1) { + if (cursor.pIndex == 11) { + sp++; + } else { + cursor++; + } + } + if (Inp->IsUp(Inp->Right) && sp + cursor.pIndex + 5 < (int)pDL.size()) { + if (cursor.pIndex == 11) { + sp += 5; + } else { + if (cursor.pIndex + 5 > 11) { + sp += (cursor.pIndex - 11 + 5); + cursor.SetIndex(11); + } else { + cursor += 5; + } + } + } + if (Inp->IsUp(Inp->Up) && cursor.pIndex + sp > 0) { + if (cursor.pIndex == 0) { + sp--; + } else { + cursor--; + } + } + if (Inp->IsUp(Inp->Left) && sp + cursor.pIndex - 5 >= 0) { + if (cursor.pIndex == 0) { + sp -= 5; + } else { + if (cursor.pIndex - 5 < 0) { + sp -= 5 - cursor.pIndex; + cursor.SetIndex(0); + } else { + cursor -= 5; + } + } + } + + if (Inp->IsDown(Inp->A)) { + auto FSE = pDL.at(cursor.pIndex + sp); + if (FSE->pFunc) { + FSE->pFunc(FSE->Second); + } else if (FSE->SubData.size() != 0) { + pLastPos.Push(PD::Pair(sp, cursor.GetIndex())); + cursor.SetIndex(0); + sp = 0; + pStack.Push(pDL); + pDL = FSE->SubData; + } + } + + if (Inp->IsDown(Inp->B)) { + if (!pLastPos.IsEmpty()) { + sp = pLastPos.Top().First; + cursor.SetIndex(pLastPos.Top().Second); + pLastPos.Pop(); + } else { + sp = 0; + cursor.SetIndex(0); + } + if (pStack.IsEmpty()) { + Back(); + } else { + pDL = pStack.Top(); + pStack.Pop(); + } + } +} + +Settings::TabEntry::Ref Settings::MakeEntry(const std::string& id, + const std::string& val) { + auto e = TabEntry::New(); + e->First = id; + e->Second = val; + return e; +} + +Settings::TabEntry::Ref MakeInfo() { + auto e = Settings::TabEntry::New(); + std::vector pData; + pData.push_back(Settings::MakeEntry("Version", VERSION)); + pData.push_back(Settings::MakeEntry("Commit", GIT_COMMIT)); + pData.push_back(Settings::MakeEntry( + "C++", std::format("{} ({}-{})", (__cplusplus / 100) % 100, + __cplusplus / 100, __cplusplus % 100))); + pData.push_back( + Settings::MakeEntry("Compiler", PD::Strings::GetCompilerVersion())); + pData.push_back(Settings::MakeEntry("Build", __DATE__ " " __TIME__)); + pData.push_back(Settings::MakeEntry("RTTI", +#ifdef __RTTI + "ON" +#else + "OFF" +#endif + )); + pData.push_back(Settings::MakeEntry("Exceptions", +#ifdef __EXCEPTIONS + "ON" +#else + "OFF" +#endif + )); + e->First = "Show Info"; + e->SubData = pData; + return e; +} + +Settings::TabEntry::Ref MakeCredits() { + auto e = Settings::TabEntry::New(); + std::vector pData; + pData.push_back(Settings::MakeEntry("tobid7", "Lead deceloper, assets")); + pData.push_back(Settings::MakeEntry("", "palladium, ctrff")); + pData.push_back(Settings::MakeEntry("devkitpro", "libctru, citro3d")); + pData.push_back(Settings::MakeEntry("cheuble", "Original BCSTMV1-Decoder")); + pData.push_back(Settings::MakeEntry("3DBrew", "BCSTM Documentation")); + pData.push_back(Settings::MakeEntry("crozynski", "ComicNeue Font")); + + e->First = "Credits"; + e->SubData = pData; + return e; +} + +void Settings::Init() { + List.clear(); + + List.push_back(MakeInfo()); + List.push_back(MakeCredits()); + List.push_back(TabEntry::New( + "Decoder", bcstm_ctrl.player->GetName(), [=, this](std::string& s) { + s = bcstm_ctrl.player->GetName() == "BCSTMV2" ? "CTRFF (WIP)" + : "BCSTMV2"; + bcstm_ctrl.DoRequest(bcstm_ctrl.SwitchDec); + })); + pDL = List; +} \ No newline at end of file diff --git a/source/settings.hpp b/source/settings.hpp new file mode 100644 index 0000000..e09b582 --- /dev/null +++ b/source/settings.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include + +class Settings : public Stage { + public: + Settings(PD::Hid::Ref inp, PD::LI::Texture::Ref wp, PD::LI::Font::Ref f) + : Stage(inp, wp, f) { + Init(); + } + ~Settings() = default; + PD_SMART_CTOR(Settings); + + void Update() override; + void Init(); + + Cursor cursor = Cursor(fvec2(0.f, 18.f), 17.f); + int sp = 0; + PD::Timer delta; + struct TabEntry { + TabEntry() {} + TabEntry(const std::string& f, const std::string& s) { + First = f; + Second = s; + } + TabEntry(const std::string& f, const std::string& s, + std::function fu) { + First = f; + Second = s; + pFunc = fu; + } + PD_SMART_CTOR(TabEntry); + + std::string First; + std::string Second; + std::function pFunc; + std::vector SubData; + }; + + static TabEntry::Ref MakeEntry(const std::string& id, const std::string& val); + + /** THIS System of navigation is very memory inefficient btw */ + std::vector pDL; + std::vector List; + PD::Stack> pStack; + PD::Stack> pLastPos; +}; \ No newline at end of file diff --git a/source/stages.hpp b/source/stages.hpp index 8e8f188..16ccfe1 100644 --- a/source/stages.hpp +++ b/source/stages.hpp @@ -2,6 +2,8 @@ #include #include +#include extern FileMgr::Ref Filebrowser; -extern Inspector::Ref FileInspector; \ No newline at end of file +extern Inspector::Ref FileInspector; +extern Settings::Ref Settings; From 33ff86bff48a16d0ba0123dff743009f78c49b19 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Sun, 30 Nov 2025 12:59:20 +0100 Subject: [PATCH 17/23] Add ability to play non loop files to end - (Only for CTRFF Decode for now) - Fixed .clang-format encoding - Disable Format on Save --- .clang-format | Bin 10226 -> 4929 bytes .gitignore | 3 ++- .vscode/settings.json | 2 +- source/bcstm/ctrff_decode.cpp | 35 +++++++++++++++++++++++++--------- source/bcstm/ctrff_decode.hpp | 3 ++- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/.clang-format b/.clang-format index 5f9d444fc3d85e50943115f5123a5ccfac0c856c..b9adbd543f699a2c03af2557cc7175679f7edc23 100644 GIT binary patch literal 4929 zcmb^#Yj5K=?7M%3fepyr4ei?AJ`BOIAxYhvce&UXyX)S8>L`hJj^xR5AH#n9NJ_Tl zTryz80<^G6ilq3a&(6+*Ig{IlZTUEKzgblUpM+CZ^K~LK^IRh8jZ)i^2a}@Ub^T7Q z#YS+QY&JDF|9ICm!{% zlez}Lo=bC*WGc*!;N|)x!8I>RrkN2SbRZ|H3s&*>YHXKL?sGP*&nOZ5o+1BgY;s?IeKoz@#IqGUCNEJW=Pp`_8O z?92;yCwIGZ7&C3k56-o|MdSdnfT*l1Q0`EL(LvsZ|f6fazx{Jy0J&Cjpni!3vqg%U-Ix4E={+N(*sDcP(9ZI9$Hr!;a#jO%wyKzk$8jb8kGw)*vj;kEgzfyRdq zFJFH1>8LfirOs5N3*MeVB$m9cy|j7OgIgEcXtD4&$|yT1xEQqHG`$uYf($|!_)sOY zh(SfC?Drr}1(!MK+N&0XU68Nal1pLQW^N4Aq&EI=hN(|7v9e@^7L|G5C@=#KdF+7) zJsjI>yssc|+WWrcyrTM!d0tOUzJpfJv`IF@`%^9uXv((&+Sh0l^e{+T3{HYQ_3Uzm z?8o6{&}Evn3sncFHFnW}U+J_b+u7<>Fi=o$j5EW6PLZ|>8% zGBXZeUtA0jbaW0I2L8dRt0CHU1b|;0oI5gnAyvyedcv(`wu8aCfC6e6cs%;~FOjjS zlSgPUIL2S0n2(AsZ468HXtl9r*Hy8jyA($BH7~Jc!Ay_k4u5B1QFM>9S@&4L_SI70 zCe}TtY47Dj+agJwcx$tEtnKIimN^QkZe2x@s8VF4k_5H{vUj@ ztrpYSu9F`&r9d|*?)$IaCiLuR3l;M%-; z%by?n(U#8IT<>dvH^N%Qu-aH62xEWV8K~eWhPyEgdAJ!I^6_Tc6BOV9jtf5yU;ge> z2cFJL_~5}QA0T&eo&1PdEU9)=6&+~n5G2Ozc#XZqvuAB@<2NPftw3{S<=3&C;D!#6 z!Lb-Wf2?U&W&XnEvnCn??H)(b!AMM31J=EhPwE%LCJGihrg|(h$VAQEr<>u$?7fZ? zwBKULil__CmQ!VR0d!EgX1Ef$AI_l&wF|#LSH54}E^f2v8j=tO%Y3zr-rvT{X!U)x z%;IDbm+?f=>K-@#!+6&tri~<;x8H5Pfy88@8BC~uMFW$sI UMf8@Yt0BM+w*&C``FRlh4N^CJ6951J literal 10226 zcmchdYj0yo5r+FS68~W(kivpyU|2q^MhKK-J&Pe5dvak|ZqbbGT-d}3wzHWaemn3! zU8!{UIem_utQH-`w^LPJ_jcP!W+HLwEFZ}GQJS+MBMMAN3hTAC4OC24Yblk z9Qbk?_Vs!feLdGF^T(b(;q%fPqsW2h=a1TwC0)KhiaK39$UY0i;z}10bdRk>bE{u` zrAyJ=MTwVsUBrw`9ON4MeiZ+nY(--%?UUjeU^ElaUQ6y!ymd9`#*?fi%9A_e{ra)g1O)jji`ZlCz_DT*um3CkaneEsI8U zlB``>PUX@)KIokq!oF_p+BseQ51W1rmT%h-t;QH6BkTls`o)3I3_A1~tPGg(xt?zUD( z;=7U+^g$}HUU??%Yu#T)3+cb}K=m?7+TEtPc1d5W`ZkBnsabd@o9MVV50<@-8d&%A zNVTg8dCflFzhmQT5tOxHp5>v>~uPk%)B7g2x1oT+pAowwmc7LSCB2hx8x{4Pe@@9yZ%NKrbf zQW1N0;J0z6rD^Up^hre9XYwX{06Re`d2FiB6i@WyGyvXQb0!w2G9P2a(W`k;slSdC ztMK#iGW<0BRj=paHxE_n^i(+=hgY&L?+Zrr%g2@Zei&(2sm|_Q<4raFE*jeT zkX!V9YG@tKSL*hGQ+)rv!*5U3>a%A>w9L_=a*|UsYPXE>R`^Nyi(-Eg{-#eSToMnR7z{DS< z6K5RF-kH;nRV!b}PYco7DF)r!cyg?l^K|bA5{{_T&?Rz7< zmbQJvna@IAInpaRaT)U>o$#?Eg)_I;anPZ>LNB=y|64^G@4A0DcLXij2}F&<_t8Vm zoE!0?MLQmUkc64;r@ewt&!zf1dV`FayWr84y8EeSl<&h#UTJkYF-~+~&9i7Zwr)LS zc8$_zHdI_W<;SXyN3zUqfXg~wii+=t^N1!i()7IViOVTWH?z#1=>75$jTm=+@|XDC zL;1R8##skU98>}4Q8_N>Y=Is@<|S{Y^At45=etW|FK5^(RfSn$IK<~!8M!uhu1lX6 zavi{!>|Qvlx|h~&SM6B<`kNoRxN;3ZJ)AtRggcOb8Be6BD^L|)TEtqi9O#l*Vk9*9 zOeo)Ga~DrqnY!Z)$=~6BB^&zrsUIrYk?5k%~`^d~0=((EpF8TV3-s_8O0#$� z`UKAVI(VPnYkNF0C70?{;4+@S`D*#^yUM!S?-SM9P4!NC4(M`tXN_HXdR*n%(&|6c zRU#MX+Y)E{F4e#;85A+m=gYXGD6J(v-c4Pb{wI@AfwS|cldi}52#~!zdrDm+_ub~v zxDk@bIbDOXI!o>M8%5T7b?xq0IBBGrHY#|TIqXY##%VR$obp|{G6?_FT*l+e4oh8M zqhx=|#HxG6X~&wp2dajFkfMD)Qq66v)=tDd2!GNmcWSoOWWZy4vZwKEwhny-l;aT} z={u;-kO}(uMdsV=%IT9Yp2Eo#NSyUpyJof~ClEvmJ-fO8f3-w+J&jp+r>f4+mkd>v zlil}j;EA+{c1k>)&tRuMe#~epuIuS{$=OB@yRkFdxeNZmd-RhMJ=8GAp6mkmSyuw% zZpulN^63Z0yVr~W$F|R+)7m*+s3DH}$#UHTxh(7DUFTAL-YBKmc{WkxRH1yPP8QIG z=Fz+BbwW!0-1;d}tUADKmQ85U_sq+l2-ZE$9A`4m)Sbx_+c6O{GJ)r{o7jk5X+M&1 zZ=aV-o@5iJlGHnYef&HTrXJCiUItcb@7C~=@1hL1CO<6W?vfL0`%eR}&8{jjdh<4? zlzu`=j{+~UOQ8mA&3@gn-s5H`4eUaRxxcoJ=%%Xj!NQHg?|0NejA z^~W!d-qU2T*USI6C}pVo=OV_G2=IiDjAGtcj5uFm&R9?KNoVhVzMH7?h^E~ymiYu@ zMsmh_tP_D_tufFnE$=b=IemSr`@{I@B>Yj|K@3A7oBc?CB&|N^X+(Z9UuySxq+>$x ztm{D8@+MYDN!9G~MsL#bNc&>Xn*JBYo^)dO@^MdksBd%|%Xw(KeF1JckX`%XOTGSC bcIMsyMW4v-KAQYYx{v;)PJv%W>8$WSoe76q diff --git a/.gitignore b/.gitignore index 118a7dd..6ef52e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build BCSTM-Player.* icon.bin -banner.bin \ No newline at end of file +banner.bin +compile_commands.json \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index b8ec699..fcff0af 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "editor.formatOnSave": true, + "editor.formatOnSave": false, "files.associations": { "*.tcc": "cpp", "cctype": "cpp", diff --git a/source/bcstm/ctrff_decode.cpp b/source/bcstm/ctrff_decode.cpp index fd73c13..d885cca 100644 --- a/source/bcstm/ctrff_decode.cpp +++ b/source/bcstm/ctrff_decode.cpp @@ -2,7 +2,7 @@ #include /** std::memset :( */ namespace D7 { -void CTRFFDec::LoadFile(const std::string& path) { +void CTRFFDec::LoadFile(const std::string &path) { CleanUp(); pCurrentFile.LoadFile(path); @@ -104,7 +104,7 @@ void CTRFFDec::Stop() { if (!pIsStreaming) { return; } - for (unsigned int i = 0; i < pCurrentFile.GetNumChannels(); i++) { + for (PD::u8 i = 0; i < pCurrentFile.GetNumChannels(); i++) { ndspChnWaveBufClear(pChannels[i]); pActiveChannels &= ~(1 << pChannels[i]); } @@ -114,6 +114,21 @@ void CTRFFDec::Stop() { void CTRFFDec::Stream() { pCurrentTime = svcGetSystemTick(); if (pCurrentTime - pLastTime >= 100000000 && pIsLoaded) { + if (pIsEnding) { + bool all_done = true; + for (PD::u32 buf = 0; buf < BufferCount; buf++) { + for (PD::u8 chn = 0; chn < pCurrentFile.GetNumChannels(); chn++) { + if (pWaveBuf[chn][buf].status != NDSP_WBUF_DONE) { + all_done = false; + break; + } + } + } + if (all_done) { + Stop(); + } + return; + } if (!pIsStreaming) return; if (!pIsPaused) pFillBuffers(); pLastTime = pCurrentTime; @@ -123,7 +138,7 @@ void CTRFFDec::Stream() { void CTRFFDec::pFillBuffers() { for (PD::u32 buf_idx = 0; buf_idx < BufferCount; buf_idx++) { bool all_ready = true; - for (PD::u32 ch = 0; ch < pCurrentFile.GetNumChannels(); ch++) { + for (PD::u8 ch = 0; ch < pCurrentFile.GetNumChannels(); ch++) { if (pWaveBuf[ch][buf_idx].status != NDSP_WBUF_DONE) { all_ready = false; break; @@ -137,24 +152,25 @@ void CTRFFDec::pFillBuffers() { pCurrentFile.ReadGotoBeginning(true); } else if (!pCurrentFile.IsLooping() && pCurrentBlock == pCurrentFile.GetLoopEnd()) { - this->Stop(); + pIsEnding = true; + return; } for (PD::u8 chn_idx = 0; chn_idx < pCurrentFile.GetNumChannels(); - ++chn_idx) { - ndspWaveBuf* buf = &pWaveBuf[chn_idx][buf_idx]; + chn_idx++) { + ndspWaveBuf *buf = &pWaveBuf[chn_idx][buf_idx]; memset(buf, 0, sizeof(ndspWaveBuf)); buf->data_adpcm = pBufferData.at(chn_idx).at(buf_idx).Data(); - pCurrentFile.ReadBlock(pCurrentBlock, (PD::u8*)buf->data_adpcm); + pCurrentFile.ReadBlock(pCurrentBlock, (PD::u8 *)buf->data_adpcm); DSP_FlushDataCache(buf->data_adpcm, pCurrentFile.GetBlockSize()); if (pCurrentBlock == 0) { buf->adpcm_data = - (ndspAdpcmData*)&pCurrentFile.pDSP_ADPCM_Info[chn_idx].Context; + (ndspAdpcmData *)&pCurrentFile.pDSP_ADPCM_Info[chn_idx].Context; } else if (pCurrentBlock == pCurrentFile.GetLoopStart()) { buf->adpcm_data = - (ndspAdpcmData*)&pCurrentFile.pDSP_ADPCM_Info[chn_idx].LoopContext; + (ndspAdpcmData *)&pCurrentFile.pDSP_ADPCM_Info[chn_idx].LoopContext; } if (pCurrentBlock == pCurrentFile.GetNumBlocks() - 1) { @@ -175,6 +191,7 @@ void CTRFFDec::CleanUp() { pIsLoaded = false; pIsStreaming = false; pIsPaused = false; + pIsEnding = false; pActiveChannels = 0; pCurrentBlock = 0; pChannels.Clear(); diff --git a/source/bcstm/ctrff_decode.hpp b/source/bcstm/ctrff_decode.hpp index 053bcd8..e9d7ca3 100644 --- a/source/bcstm/ctrff_decode.hpp +++ b/source/bcstm/ctrff_decode.hpp @@ -14,7 +14,7 @@ class CTRFFDec : public BCSTMPlayerBase { }; ~CTRFFDec() { CleanUp(); } - void LoadFile(const std::string& path); + void LoadFile(const std::string &path); void Stream(); void CleanUp(); @@ -53,6 +53,7 @@ class CTRFFDec : public BCSTMPlayerBase { bool pIsLoaded = false; bool pIsStreaming = false; bool pIsPaused = false; + bool pIsEnding = false; PD::u64 pCurrentTime = 0; PD::u64 pLastTime = 0; From c7d0a226136c7209ae00f452a2abb964cd03e06c Mon Sep 17 00:00:00 2001 From: tobid7 Date: Sun, 30 Nov 2025 13:29:24 +0100 Subject: [PATCH 18/23] Add Git Branch to indo - Build Cia only if makerom and banenrtool are found - Add clang-format target --- CMakeLists.txt | 118 +++++++++++++++++++++++++------------------- source/settings.cpp | 1 + 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 085134f..80e0a5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,13 @@ execute_process( OUTPUT_STRIP_TRAILING_WHITESPACE ) +execute_process( + COMMAND git rev-parse --abbrev-ref HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE GIT_BRANCH + OUTPUT_STRIP_TRAILING_WHITESPACE +) + # Set Result specific Data set(APP_NAME "BCSTM-Player") set(APP_DESC "BCSTM Player for the 3ds") @@ -33,7 +40,7 @@ set(APP_RSF "${CMAKE_SOURCE_DIR}/app/build-cia.rsf") set(APP_LOGO "${CMAKE_SOURCE_DIR}/app/splash.lz") # Set 3ds IP for make send -set(3DS_IP "192.168.2.224" CACHE STRING "3ds IP (make send)") +set(3DS_IP "" CACHE STRING "3ds IP (make send)") # Set Project project(BCSTM-Player LANGUAGES C CXX VERSION 2.0.0) @@ -79,6 +86,10 @@ target_compile_definitions(BCSTM-Player PUBLIC -D_GNU_SOURCE=1 -DVERSION="${PROJECT_VERSION}" -DGIT_COMMIT="${GIT_SHORT_HASH}" + -DGIT_BRANCH="${GIT_BRANCH}" +) +target_compile_options(BCSTM-Player PUBLIC + -Wno-psabi ) if(${CMAKE_BUILD_TYPE} STREQUAL "Release") @@ -93,24 +104,6 @@ add_custom_target( COMMAND 3dslink -a "${3DS_IP}" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.3dsx" ) -## Graphics -file(GLOB TEX3DS_DEF "${CMAKE_SOURCE_DIR}/gfx/*.t3s") - -foreach(FILE_ ${TEX3DS_DEF}) - get_filename_component(TNAME ${FILE_} NAME_WE) - set(T3X_RES "${CMAKE_SOURCE_DIR}/romfs/gfx/${TNAME}.t3x") - add_custom_command( - OUTPUT ${T3X_RES} - COMMAND tex3ds.exe -i ${FILE_} -o ${T3X_RES} - DEPENDS ${FILE_} - COMMENT "Converting ${TNAME}.t3x" - VERBATIM - ) - list(APPEND T3X ${T3X_RES}) -endforeach() - -add_custom_target(create_resources ALL DEPENDS ${T3X}) - # Generate 3DSX ctr_generate_smdh( ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh @@ -124,42 +117,63 @@ ctr_create_3dsx( OUTPUT "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.3dsx" SMDH "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh" ROMFS "${APP_ROMFS}" - DEPENDS create_resources ) +if(MAKEROM AND BANNERTOOL) + message(STATUS "Found Makerom and Bannertool. Building Cia...") -add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr - COMMAND ${BANNERTOOL} makebanner - -i ${APP_BANNER} - -a ${APP_BANNERAUDIO} - -o ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr - DEPENDS ${APP_BANNER} ${APP_BANNERAUDIO} - COMMENT "Creating Banner for cia file..." - VERBATIM -) + add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr + COMMAND ${BANNERTOOL} makebanner + -i ${APP_BANNER} + -a ${APP_BANNERAUDIO} + -o ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr + DEPENDS ${APP_BANNER} ${APP_BANNERAUDIO} + COMMENT "Creating Banner for cia file..." + VERBATIM + ) -add_custom_command( - OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.cia - COMMAND ${MAKEROM} - -f cia -target t -exefslogo - -o "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.cia" - -elf "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.elf" - -rsf ${APP_RSF} - -banner "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr" - -icon "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh" - -logo ${APP_LOGO} -DAPP_ROMFS="${APP_ROMFS}" - -major 1 -minor 0 -micro 0 - -DAPP_VERSION_MAJOR=1 - DEPENDS ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.elf ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh - COMMENT "Creating Cia file..." - VERBATIM -) + add_custom_command( + OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.cia + COMMAND ${MAKEROM} + -f cia -target t -exefslogo + -o "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.cia" + -elf "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.elf" + -rsf ${APP_RSF} + -banner "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr" + -icon "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh" + -logo ${APP_LOGO} -DAPP_ROMFS="${APP_ROMFS}" + -major 1 -minor 0 -micro 0 + -DAPP_VERSION_MAJOR=1 + DEPENDS ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.elf ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.smdh + COMMENT "Creating Cia file..." + VERBATIM + ) -add_custom_target(make_banner ALL - DEPENDS ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr -) + add_custom_target(make_banner ALL + DEPENDS ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr + ) -add_custom_target(make_cia ALL - DEPENDS ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bnr ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.elf ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.cia -) + add_custom_target(make_cia ALL + DEPENDS make_banner ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.elf ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}.cia + ) +endif() + +# Add clang-format target if clang-format was found +find_program(CLANG_FORMAT clang-format) +if(CLANG_FORMAT) + message(STATUS "Clang Format found.") + set(SOURCES ${CMAKE_SOURCE_DIR}/source) + set(COMMENT "Formatting Project Sources") + if(WIN32) + add_custom_target(clang-format + COMMAND powershell.exe -Command "Get-ChildItem '${SOURCES}/*' -Include *.cpp,*.hpp -Recurse | Foreach {&'${CLANG_FORMAT}' -i $_.fullname}" + COMMENT ${CCOMMENT}) + else() + add_custom_target(clang-format + COMMAND find ${SOURCES} -iname *.hpp -o -iname *.cpp | xargs ${CLANG_FORMAT} -i + COMMENT ${COMMENT} + ) + endif() + unset(COMMENT) +endif() diff --git a/source/settings.cpp b/source/settings.cpp index c170051..a273a3a 100644 --- a/source/settings.cpp +++ b/source/settings.cpp @@ -133,6 +133,7 @@ Settings::TabEntry::Ref MakeInfo() { std::vector pData; pData.push_back(Settings::MakeEntry("Version", VERSION)); pData.push_back(Settings::MakeEntry("Commit", GIT_COMMIT)); + pData.push_back(Settings::MakeEntry("Banch", GIT_BRANCH)); pData.push_back(Settings::MakeEntry( "C++", std::format("{} ({}-{})", (__cplusplus / 100) % 100, __cplusplus / 100, __cplusplus % 100))); From 70a4e48fa6d47aabc674caf094be53ec505a63f8 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Wed, 3 Dec 2025 20:59:48 +0100 Subject: [PATCH 19/23] Add support for bcstmv2 to play file endings --- source/bcstm/bcstmv2.cpp | 18 +++++++++++++++++- source/bcstm/bcstmv2.hpp | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/source/bcstm/bcstmv2.cpp b/source/bcstm/bcstmv2.cpp index 0075904..eff91bf 100644 --- a/source/bcstm/bcstmv2.cpp +++ b/source/bcstm/bcstmv2.cpp @@ -193,6 +193,21 @@ void D7::BCSTM2::Stop() { void D7::BCSTM2::stream() { current_time = svcGetSystemTick(); if (current_time - last_time >= 100000000 && is_loaded) { + if (m_is_ending) { + bool all_done = true; + for (PD::u32 buf = 0; buf < buffer_count; buf++) { + for (PD::u8 chn = 0; chn < channel_count; chn++) { + if (wave_buf[chn][buf].status != NDSP_WBUF_DONE) { + all_done = false; + break; + } + } + } + if (all_done) { + Stop(); + } + return; + } if (!is_streaming) return; if (!is_paused) fill_buffers(); last_time = current_time; @@ -220,7 +235,8 @@ void D7::BCSTM2::fill_buffers() { (data_offset + 0x20 + block_size * channel_count * loop_start)); } if (!is_looping && current_block == loop_end) { - this->Stop(); + m_is_ending = true; + return; } for (unsigned int channelIndex = 0; channelIndex < channel_count; diff --git a/source/bcstm/bcstmv2.hpp b/source/bcstm/bcstmv2.hpp index d3f3446..c6a90de 100644 --- a/source/bcstm/bcstmv2.hpp +++ b/source/bcstm/bcstmv2.hpp @@ -103,6 +103,7 @@ class BCSTM2 : public BCSTMPlayerBase { bool is_paused = false; bool is_looping = false; bool is_streaming = false; + bool m_is_ending = false; bool is_little_endian; unsigned int channel_count = 0; From 6c3e870b817a90ebfa4bc51569d38400a80488ea Mon Sep 17 00:00:00 2001 From: tobid7 Date: Wed, 3 Dec 2025 21:00:38 +0100 Subject: [PATCH 20/23] hotfix (bcstmv2) --- source/bcstm/bcstmv2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/source/bcstm/bcstmv2.cpp b/source/bcstm/bcstmv2.cpp index eff91bf..d1f1c27 100644 --- a/source/bcstm/bcstmv2.cpp +++ b/source/bcstm/bcstmv2.cpp @@ -186,6 +186,7 @@ void D7::BCSTM2::Stop() { is_paused = false; is_looping = false; is_loaded = false; + m_is_ending = false; if (!is_streaming) return; is_streaming = false; } From 105895909b2b425db837d55e99c146ed0b471372 Mon Sep 17 00:00:00 2001 From: tobid7 Date: Wed, 3 Dec 2025 22:57:55 +0100 Subject: [PATCH 21/23] Update ctrff and cleanup in Stop (CTRFF DECODER) --- .gitmodules | 6 +++--- ctrff | 2 +- source/bcstm/ctrff_decode.cpp | 1 + source/inspector_view.cpp | 8 ++++---- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.gitmodules b/.gitmodules index e7925a1..4bbfe4e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "ctrff"] - path = ctrff - url = https://dev.npid7.de/tobid7/ctrff-pub +[submodule "ctrff"] + path = ctrff + url = https://github.com/tobid7/ctrff [submodule "palladium"] path = palladium url = https://dev.npid7.de/tobid7/palladium diff --git a/ctrff b/ctrff index f6b81d7..945941e 160000 --- a/ctrff +++ b/ctrff @@ -1 +1 @@ -Subproject commit f6b81d701ac90deebe75dde8d78553ea9e7a68f0 +Subproject commit 945941ef5c741e7f691ea9443e43c4d979cdf266 diff --git a/source/bcstm/ctrff_decode.cpp b/source/bcstm/ctrff_decode.cpp index d885cca..637a51e 100644 --- a/source/bcstm/ctrff_decode.cpp +++ b/source/bcstm/ctrff_decode.cpp @@ -109,6 +109,7 @@ void CTRFFDec::Stop() { pActiveChannels &= ~(1 << pChannels[i]); } pIsStreaming = false; + CleanUp(); } void CTRFFDec::Stream() { diff --git a/source/inspector_view.cpp b/source/inspector_view.cpp index 71ebd3d..21bb607 100644 --- a/source/inspector_view.cpp +++ b/source/inspector_view.cpp @@ -201,11 +201,11 @@ Inspector::TabEntry::Ref MakeSizedRef(ctrff::BCSTM::SizedReference& b, return e; } -Inspector::TabEntry::Ref MakeRefListVec(PD::Vec& b, +Inspector::TabEntry::Ref MakeRefListVec(std::vector& b, const std::string& name = "") { auto e = Inspector::TabEntry::New(); std::vector Lst; - for (size_t i = 0; i < b.Size(); i++) { + for (size_t i = 0; i < b.size(); i++) { Lst.push_back(MakeRef(b[i], "Entry " + std::to_string(i))); } e->First = "Reference List"; @@ -335,10 +335,10 @@ Inspector::TabEntry::Ref MakeDspAdpcmInfo(ctrff::BCSTM::DSP_ADPCM_Info& b, } Inspector::TabEntry::Ref MakeDspAdpcmInfoList( - PD::Vec& b, const std::string& name = "") { + std::vector& b, const std::string& name = "") { auto e = Inspector::TabEntry::New(); std::vector Lst; - for (size_t i = 0; i < b.Size(); i++) { + for (size_t i = 0; i < b.size(); i++) { Lst.push_back(MakeDspAdpcmInfo(b[i], "Entry " + std::to_string(i))); } e->First = "DSP ADPCM Info List"; From 5e327a80f4328ddb63dcb6eccea5c64697f5910e Mon Sep 17 00:00:00 2001 From: tobid7 Date: Wed, 3 Dec 2025 23:57:37 +0100 Subject: [PATCH 22/23] Update ctrff (fixes ctrff decoder almost completly --- ctrff | 2 +- source/inspector_view.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ctrff b/ctrff index 945941e..7ea0d11 160000 --- a/ctrff +++ b/ctrff @@ -1 +1 @@ -Subproject commit 945941ef5c741e7f691ea9443e43c4d979cdf266 +Subproject commit 7ea0d11d651b744c172e63f8c252cca097f52ed5 diff --git a/source/inspector_view.cpp b/source/inspector_view.cpp index 21bb607..b90a07b 100644 --- a/source/inspector_view.cpp +++ b/source/inspector_view.cpp @@ -335,7 +335,8 @@ Inspector::TabEntry::Ref MakeDspAdpcmInfo(ctrff::BCSTM::DSP_ADPCM_Info& b, } Inspector::TabEntry::Ref MakeDspAdpcmInfoList( - std::vector& b, const std::string& name = "") { + std::vector& b, + const std::string& name = "") { auto e = Inspector::TabEntry::New(); std::vector Lst; for (size_t i = 0; i < b.size(); i++) { From e6f0eb205f3476769bc80906499a21b76986de1e Mon Sep 17 00:00:00 2001 From: tobid7 Date: Thu, 4 Dec 2025 09:45:04 +0100 Subject: [PATCH 23/23] Update CTRFF (fixes noise bug) --- .vscode/c_cpp_properties.json | 12 +++++++++++- ctrff | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 0d07302..1112c65 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,12 +1,22 @@ { "configurations": [ { - "name": "3DS", + "name": "3DS | Windows", "includePath": [ "${workspaceFolder}/**", "${workspaceFolder}/palladium/include/**", "C:/devkitpro/libctru/include/**", "C:/devkitpro/portlibs/3ds/include/**", + ], + "cStandard": "c17", + "cppStandard": "c++20", + "compileCommands": "${workspaceFolder}/build/compile_commands.json" + }, + { + "name": "3DS | Linux/Mac", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/palladium/include/**", "/opt/devkitpro/libctru/include/**", "/opt/devkitpro/portlibs/3ds/include/**" ], diff --git a/ctrff b/ctrff index 7ea0d11..fb8f275 160000 --- a/ctrff +++ b/ctrff @@ -1 +1 @@ -Subproject commit 7ea0d11d651b744c172e63f8c252cca097f52ed5 +Subproject commit fb8f275ebbc02493ae984ae01772026fe49d1a69