├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE.md ├── Makefile ├── README.md ├── deps.mk ├── docs ├── Doxyfile └── Makefile ├── examples ├── Makefile ├── example01-simple.c └── example02-browse.c ├── include ├── pulsar.h └── pulsar │ ├── archive │ ├── archive.h │ ├── archive_file.h │ └── archive_internal.h │ ├── bfgrp │ ├── bfgrp.h │ ├── bfgrp_internal.h │ └── bfgrp_location.h │ ├── bfsar │ ├── bfsar.h │ ├── bfsar_file.h │ ├── bfsar_group.h │ ├── bfsar_internal.h │ ├── bfsar_item.h │ ├── bfsar_sound.h │ ├── bfsar_string.h │ └── bfsar_wave_archive.h │ ├── bfstm │ ├── bfstm.h │ ├── bfstm_channel.h │ ├── bfstm_info.h │ └── bfstm_internal.h │ ├── bfwar │ ├── bfwar.h │ ├── bfwar_file.h │ └── bfwar_internal.h │ ├── bfwav │ ├── bfwav.h │ ├── bfwav_info.h │ └── bfwav_internal.h │ ├── bfwsd │ ├── bfwsd.h │ ├── bfwsd_internal.h │ ├── bfwsd_sound_data.h │ └── bfwsd_wave_id.h │ ├── player │ ├── player.h │ ├── player_load.h │ ├── player_load_formats.h │ └── player_load_lookup.h │ └── types.h └── src ├── archive ├── archive.c └── archive_file.c ├── bfgrp ├── bfgrp.c └── bfgrp_location.c ├── bfsar ├── bfsar.c ├── bfsar_file.c ├── bfsar_group.c ├── bfsar_sound.c ├── bfsar_string.c └── bfsar_wave_archive.c ├── bfstm ├── bfstm.c ├── bfstm_channel.c └── bfstm_info.c ├── bfwar ├── bfwar.c └── bfwar_file.c ├── bfwav ├── bfwav.c └── bfwav_info.c ├── bfwsd ├── bfwsd.c ├── bfwsd_sound_data.c └── bfwsd_wave_id.c └── player ├── player.c ├── player_load.c ├── player_load_formats.c └── player_load_lookup.c /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | dist: 7 | runs-on: ubuntu-latest 8 | container: devkitpro/devkita64 9 | steps: 10 | - name: env 11 | run: | 12 | cat /proc/cpuinfo 13 | free -m 14 | - name: checkout - project 15 | uses: actions/checkout@v2 16 | - name: build 17 | # DKP docker images are not up to date yet with libnx 18 | # so we skip building examples for now 19 | run: | 20 | make dist-bin dist-src dist-docs -j4 21 | - name: store 22 | uses: actions/upload-artifact@v2 23 | with: 24 | name: dist-artifacts 25 | path: | 26 | *.tar.xz 27 | deploy-docs: 28 | runs-on: ubuntu-latest 29 | if: github.ref == 'refs/heads/develop' 30 | needs: [dist] 31 | steps: 32 | - name: Download dist-artifacts 33 | uses: actions/download-artifact@v2 34 | with: {name: dist-artifacts} 35 | - name: Extract docs 36 | run: | 37 | tar xvf libpulsar-docs.tar.xz 38 | - name: Deploy to GitHub Pages 39 | uses: peaceiris/actions-gh-pages@v3 40 | with: 41 | github_token: ${{ secrets.GITHUB_TOKEN }} 42 | publish_dir: ./html 43 | user_name: 'github-actions[bot]' 44 | user_email: 'github-actions[bot]@users.noreply.github.com' 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /lib 3 | *.xz 4 | *.pyc 5 | __pycache__ 6 | /docs/build 7 | /examples/build 8 | /examples/exampleXX-*.c 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 p-sam 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITPRO)/libnx/switch_rules 11 | include $(TOPDIR)/deps.mk 12 | 13 | #--------------------------------------------------------------------------------- 14 | # TARGET is the name of the output 15 | #--------------------------------------------------------------------------------- 16 | TARGET := $(PLSR_TARGET) 17 | SOURCES := $(addprefix $(PLSR_DIR), $(PLSR_SOURCES)) 18 | INCLUDES := $(addprefix $(PLSR_DIR), $(PLSR_INCLUDES)) 19 | BUILD := build 20 | OUT := lib 21 | 22 | #--------------------------------------------------------------------------------- 23 | # options for code generation 24 | #--------------------------------------------------------------------------------- 25 | ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec 26 | 27 | DEFINES := -D__SWITCH__ 28 | CFLAGS := -g -Wall -Werror \ 29 | -ffunction-sections \ 30 | -fdata-sections \ 31 | $(ARCH) \ 32 | $(DEFINES) \ 33 | $(BUILD_CFLAGS) 34 | 35 | CFLAGS += $(INCLUDE) 36 | 37 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 38 | 39 | ASFLAGS := -g $(ARCH) 40 | 41 | #--------------------------------------------------------------------------------- 42 | # list of directories containing libraries, this must be the top level containing 43 | # include and lib 44 | #--------------------------------------------------------------------------------- 45 | LIBDIRS := $(PORTLIBS) $(LIBNX) 46 | 47 | #--------------------------------------------------------------------------------- 48 | # no real need to edit anything past this point unless you need to add additional 49 | # rules for different file extensions 50 | #--------------------------------------------------------------------------------- 51 | ifneq ($(BUILD_TYPE),$(notdir $(CURDIR))) 52 | #--------------------------------------------------------------------------------- 53 | export TOPDIR := $(CURDIR) 54 | export VPATH := $(foreach dir,$(SOURCES),$(dir)) 55 | 56 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 57 | 58 | export LD := $(CC) 59 | export OFILES_SRC := $(CFILES:.c=.o) 60 | export OFILES := $(OFILES_SRC) 61 | 62 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(dir)) \ 63 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) 64 | 65 | .PHONY: clean all examples docs 66 | 67 | #--------------------------------------------------------------------------------- 68 | all: debug release 69 | 70 | examples: release 71 | @$(MAKE) -C examples 72 | 73 | docs: 74 | @$(MAKE) -C docs 75 | 76 | $(OUT): 77 | @[ -d $@ ] || mkdir -p $@ 78 | 79 | $(OUT)/lib$(TARGET).a : $(OUT) $(SOURCES) $(INCLUDES) 80 | @[ -d $(BUILD)/release ] || mkdir -p $(BUILD)/release 81 | @$(MAKE) BUILD_TYPE=release OUTPUT=$(CURDIR)/$@ \ 82 | BUILD_CFLAGS="-DNDEBUG=1 -O2" \ 83 | DEPSDIR=$(CURDIR)/$(BUILD)/release \ 84 | --no-print-directory -C $(BUILD)/release \ 85 | -f $(TOPDIR)/Makefile 86 | 87 | $(OUT)/lib$(TARGET)d.a : $(OUT) $(SOURCES) $(INCLUDES) 88 | @[ -d $(BUILD)/debug ] || mkdir -p $(BUILD)/debug 89 | @$(MAKE) BUILD_TYPE=debug OUTPUT=$(CURDIR)/$@ \ 90 | BUILD_CFLAGS="-DDEBUG=1 -Og" \ 91 | DEPSDIR=$(CURDIR)/$(BUILD)/debug \ 92 | --no-print-directory -C $(BUILD)/debug \ 93 | -f $(TOPDIR)/Makefile 94 | 95 | debug: $(OUT)/lib$(TARGET)d.a 96 | 97 | release: $(OUT)/lib$(TARGET).a 98 | 99 | dist-bin: all 100 | @tar --exclude=*~ -cJf lib$(TARGET).tar.xz include lib 101 | 102 | dist-src: 103 | @tar --exclude=*~ -cJf lib$(TARGET)-src.tar.xz include src Makefile deps.mk *.md 104 | 105 | dist-docs: docs 106 | @tar --exclude=*~ -cJf lib$(TARGET)-docs.tar.xz -C docs/build html 107 | 108 | dist-examples: examples 109 | @tar --exclude=*~ -cJf lib$(TARGET)-examples.tar.xz examples/build/*.nro examples/*.c 110 | 111 | dist: dist-bin dist-src dist-docs dist-examples 112 | 113 | #--------------------------------------------------------------------------------- 114 | clean: 115 | @echo clean ... 116 | @rm -fr $(BUILD) lib *.xz 117 | 118 | mrproper: clean 119 | @$(MAKE) -C examples clean 120 | @$(MAKE) -C docs clean 121 | 122 | #--------------------------------------------------------------------------------- 123 | else 124 | 125 | DEPENDS := $(OFILES:.o=.d) 126 | 127 | #--------------------------------------------------------------------------------- 128 | # main targets 129 | #--------------------------------------------------------------------------------- 130 | $(OUTPUT) : $(OFILES) 131 | 132 | #--------------------------------------------------------------------------------- 133 | %_bin.h %.bin.o : %.bin 134 | #--------------------------------------------------------------------------------- 135 | @echo $(notdir $<) 136 | @$(bin2o) 137 | 138 | 139 | -include $(DEPENDS) 140 | 141 | #--------------------------------------------------------------------------------------- 142 | endif 143 | #--------------------------------------------------------------------------------------- 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pulsar 2 | ================ 3 | [![CI](https://github.com/p-sam/switch-libpulsar/workflows/CI/badge.svg?branch=develop)](https://github.com/p-sam/switch-libpulsar/actions?query=workflow%3ACI) 4 | 5 | Switch homebrew library to load, parse, and play sounds from BFSAR files and related file formats 6 | 7 | This library is in development and may not implement complete parsing of each format it supports. Details not needed for sound browsing and playback on the Switch may be left unimplemented. 8 | 9 | ## Links 10 | 11 | * [Github repository](https://github.com/p-sam/switch-libpulsar) 12 | * [Online documentation](https://p-sam.github.io/switch-libpulsar/) 13 | 14 | ## Including in your project 15 | 16 | See [Setting up Development Environment](https://switchbrew.org/wiki/Setting_up_Development_Environment) to install the required build dependencies 17 | 18 | There are multiple ways to bundle the code as part of your project which are: 19 | 20 | ### Building the library 21 | 22 | Build with `make` and link against `pulsar` in your project. 23 | Then add the `include` and `lib` dirs to your corresponding search paths 24 | (If you're using a standard homebrew Makefile, this is as easy as adding this directory to the `LIBDIRS` variable). 25 | 26 | ### Include the library sources in your project 27 | 28 | Include `deps.mk` in your Makefile, and then use the imported variables to add this library sources to your project (just like the Makefile at the root of this repo does). 29 | 30 | * `PLSR_SOURCES`: Absolute paths to source folders 31 | * `PLSR_INCLUDES`: Absolute path to the include folder 32 | 33 | ## Additional resources 34 | 35 | ### Building examples 36 | 37 | Build with `make examples` at the root of this repo, or with `make` directly in the `examples` folder. 38 | Binaries will be located at `./examples/build/*.nro` 39 | 40 | ### Building the documentation 41 | 42 | [Doxygen](https://www.doxygen.nl) is required to generate documentation from source code. 43 | 44 | Build with `make docs` at the root of this repo, or with `make` directly in the `docs` folder. 45 | The resulting documentation will be located at `./docs/build/html`. 46 | 47 | If you find yourself editing the documentation often, use `make docs-watch` in the `docs` folder to regenerate the documentation when the source files change. 48 | -------------------------------------------------------------------------------- /deps.mk: -------------------------------------------------------------------------------- 1 | export PLSR_DEP_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) 2 | export PLSR_DIR := $(dir $(PLSR_DEP_MAKEFILE)) 3 | 4 | export PLSR_TARGET := pulsar 5 | export PLSR_NAME := Pulsar 6 | 7 | export PLSR_SOURCES := src/archive \ 8 | src/bfgrp \ 9 | src/bfsar \ 10 | src/bfwar \ 11 | src/bfwav \ 12 | src/bfwsd \ 13 | src/bfstm \ 14 | src/player 15 | 16 | export PLSR_INCLUDES := include 17 | -------------------------------------------------------------------------------- /docs/Doxyfile: -------------------------------------------------------------------------------- 1 | DOXYFILE_ENCODING = UTF-8 2 | PROJECT_NAME = $(PLSR_NAME) 3 | PROJECT_NUMBER = $(DOCS_VERSION) 4 | OUTPUT_DIRECTORY = $(DOCS_DIR) 5 | 6 | JAVADOC_AUTOBRIEF = YES 7 | QT_AUTOBRIEF = YES 8 | MACRO_EXPANSION = YES 9 | STRIP_CODE_COMMENTS = NO 10 | REFERENCES_LINK_SOURCE = NO 11 | OPTIMIZE_OUTPUT_FOR_C = YES 12 | 13 | ENABLE_PREPROCESSING = YES 14 | MACRO_EXPANSION = YES 15 | EXPAND_ONLY_PREDEF = YES 16 | PREDEFINED = $(DOCS_DEFINES) 17 | 18 | WARN_AS_ERROR = $(DOCS_STRICT) 19 | 20 | INPUT = $(DOCS_INPUT) $(DOCS_EXAMPLE) 21 | EXAMPLE_PATH = $(DOCS_EXAMPLE) 22 | FILE_PATTERNS = *.h *.c *.md LICENSE 23 | EXCLUDE_PATTERNS = *_internal.h 24 | RECURSIVE = YES 25 | USE_MDFILE_AS_MAINPAGE = $(DOCS_MAIN) 26 | 27 | HAVE_DOT = NO 28 | GENERATE_LATEX = NO 29 | GENERATE_HTML = YES 30 | 31 | HTML_TIMESTAMP = YES 32 | ENUM_VALUES_PER_LINE = 1 33 | 34 | # Theme 35 | HTML_HEADER = $(THEME_DIR)/header.html 36 | HTML_EXTRA_STYLESHEET = $(THEME_DIR)/that_style.css 37 | HTML_EXTRA_FILES = $(THEME_DIR)/img/nav_edge_left.svg \ 38 | $(THEME_DIR)/img/nav_edge_right.svg \ 39 | $(THEME_DIR)/img/nav_edge_inter.svg \ 40 | $(THEME_DIR)/img/sync_off.png \ 41 | $(THEME_DIR)/img/sync_on.png \ 42 | $(THEME_DIR)/img/splitbar_handle.svg \ 43 | $(THEME_DIR)/img/doc.svg \ 44 | $(THEME_DIR)/img/mag_glass.svg \ 45 | $(THEME_DIR)/img/folderclosed.svg \ 46 | $(THEME_DIR)/img/folderopen.svg \ 47 | $(THEME_DIR)/js/striped_bg.js 48 | 49 | # Aliases 50 | ALIASES += wraps{1}="@copydoc \1 \n @see \1" 51 | ALIASES += unimplemented="Unimplemented" -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | include $(CURDIR)/../deps.mk 2 | 3 | BUILD := build 4 | 5 | export DOCS_DIR := $(BUILD) 6 | export DOCS_VERSION := $(shell git describe --dirty --always --tags) 7 | export DOCS_CONF := $(CURDIR)/Doxyfile 8 | export DOCS_EXAMPLE := $(PLSR_DIR)/examples 9 | export DOCS_INPUT := $(addprefix $(PLSR_DIR), $(PLSR_INCLUDES) README.md LICENSE.md) 10 | export DOCS_MAIN := $(addprefix $(PLSR_DIR), README.md) 11 | export DOCS_DEFINES := __SWITCH__ 12 | DOCS_DEFINES += $(shell grep -ihR '\#define _' $(DOCS_INPUT) | sed 's/\#define //g; s/ /=/g') 13 | 14 | export THEME_VER := 95d0f34acb 15 | export THEME_URL := "https://github.com/jl-wynen/that_style/archive/$(THEME_VER).tar.gz" 16 | export THEME_FILE := $(BUILD)/theme-$(THEME_VER).tar.gz 17 | export THEME_DIR := $(BUILD)/theme-$(THEME_VER) 18 | export THEME_COLOR := \#256aa7 19 | 20 | BIN_CURL := $(shell which curl) 21 | BIN_WGET := $(shell which wget) 22 | BIN_DOXY := $(shell which doxygen) 23 | 24 | ifeq ($(strip $(BIN_CURL))$(strip $(BIN_WGET)),) 25 | $(error "curl or wget not found in path (at least one is needed)") 26 | endif 27 | 28 | ifeq ($(strip $(BIN_DOXY))),) 29 | $(error "doxygen not found in path") 30 | endif 31 | 32 | .PHONY: clean docs docs-watch 33 | 34 | all: docs 35 | 36 | clean: 37 | @echo clean ... 38 | @rm -rf $(DOCS_DIR) 39 | 40 | theme: $(THEME_DIR) 41 | 42 | docs: $(THEME_DIR) 43 | $(BIN_DOXY) $(DOCS_CONF) 44 | 45 | docs-watch: docs 46 | @echo Watching for changes... 47 | @while inotifywait -rq $(DOCS_CONF) $(DOCS_INPUT) -e modify,move,create,delete; do $(MAKE) docs; done 48 | 49 | $(BUILD): 50 | @[ -d $@ ] || mkdir -p $@ 51 | 52 | $(THEME_FILE): 53 | @[ -d $(BUILD) ] || mkdir -p $(BUILD) 54 | @[ -z $(BIN_CURL) ] || $(BIN_CURL) -L "$(THEME_URL)" > "$(THEME_FILE)" 55 | @[ -z $(BIN_WGET) ] || $(BIN_WGET) "$(THEME_URL)" -O "$(THEME_FILE)" 56 | 57 | $(THEME_DIR): $(THEME_FILE) 58 | @[ -d $@ ] || mkdir -p $@ 59 | tar xvf $(THEME_FILE) --strip-components=1 -C $(THEME_DIR) 60 | sed -i "s/#5f082b/$(THEME_COLOR)/g" "$(THEME_DIR)/that_style.css" 61 | 62 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | ROOTDIR ?= $(CURDIR) 10 | include $(DEVKITPRO)/libnx/switch_rules 11 | include $(ROOTDIR)/../deps.mk 12 | 13 | #--------------------------------------------------------------------------------- 14 | 15 | BUILD := build 16 | EXAMPLE_DIR := $(abspath $(CURDIR)) 17 | 18 | #--------------------------------------------------------------------------------- 19 | # options for code generation 20 | #--------------------------------------------------------------------------------- 21 | ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE 22 | 23 | CFLAGS := -g -Wall -O2 -ffunction-sections \ 24 | $(ARCH) $(DEFINES) 25 | 26 | CFLAGS += $(INCLUDE) -D__SWITCH__ 27 | 28 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 29 | 30 | ASFLAGS := -g $(ARCH) 31 | LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 32 | 33 | LIBS := -l$(PLSR_TARGET) -lnx 34 | LIBDIRS := $(LIBNX) $(PLSR_DIR) 35 | 36 | #--------------------------------------------------------------------------------- 37 | ifneq ($(BUILD),$(notdir $(CURDIR))) 38 | #--------------------------------------------------------------------------------- 39 | 40 | export OUTPUT := $(CURDIR)/$(TARGET) 41 | export ROOTDIR := $(CURDIR) 42 | export VPATH := $(EXAMPLE_DIR) 43 | export DEPSDIR := $(CURDIR)/$(BUILD) 44 | export LD := $(CC) 45 | 46 | export INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include) 47 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 48 | 49 | CFILES := $(notdir $(wildcard $(EXAMPLE_DIR)/*.c)) 50 | 51 | #--------------------------------------------------------------------------------- 52 | 53 | export OFILES := $(CFILES:.c=.o) 54 | export ELFFILES := $(OFILES:.o=.elf) 55 | export NROFILES := $(ELFFILES:.elf=.nro) 56 | 57 | .PHONY: $(BUILD) clean all 58 | 59 | all: $(BUILD) 60 | 61 | $(BUILD): 62 | @[ -d $(BUILD) ] || mkdir -p $(BUILD) 63 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile $(NROFILES) 64 | 65 | #--------------------------------------------------------------------------------- 66 | clean: 67 | @echo clean ... 68 | @rm -fr $(BUILD) 69 | 70 | #--------------------------------------------------------------------------------- 71 | else 72 | .PHONY: all 73 | 74 | DEPENDS := $(OFILES:.o=.d) 75 | 76 | #--------------------------------------------------------------------------------- 77 | # main targets 78 | #--------------------------------------------------------------------------------- 79 | 80 | $(NROFILES): %.nro: %.elf 81 | @echo packing $(notdir $@) 82 | @elf2nro $< $@ 83 | 84 | $(ELFFILES): %.elf: %.o 85 | @echo link $(notdir $@) 86 | @$(LD) $(LDFLAGS) $< $(LIBPATHS) $(LIBS) -o $@ 87 | 88 | 89 | all : $(ELFFILES) 90 | 91 | #--------------------------------------------------------------------------------------- 92 | endif 93 | #--------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /examples/example01-simple.c: -------------------------------------------------------------------------------- 1 | /** @example example01-simple.c 2 | * Basic example, loads and play a sound from qlaunch sound archive 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | static PLSR_BFSAR g_bfsar; 10 | static PLSR_PlayerSoundId g_soundId = PLSR_PLAYER_INVALID_SOUND; 11 | 12 | static PLSR_RC _exampleInit() { 13 | // Initialize our player using the default configuration 14 | PLSR_RC_TRY(plsrPlayerInit()); 15 | 16 | // Open the sound archive from qlaunch sound archive 17 | PLSR_RC_TRY(plsrBFSAROpen("qlaunch:/sound/qlaunch.bfsar", &g_bfsar)); 18 | 19 | // Load the sound in memory from the previously opened archive 20 | PLSR_RC_TRY(plsrPlayerLoadSoundByName(&g_bfsar, "SeUnlockKeyZR", &g_soundId)); 21 | return PLSR_RC_OK; 22 | } 23 | 24 | static void _exampleExit() { 25 | // Free the sound from memory 26 | plsrPlayerFree(g_soundId); 27 | 28 | // Close the archive 29 | plsrBFSARClose(&g_bfsar); 30 | 31 | // De-initialize our player 32 | plsrPlayerExit(); 33 | } 34 | 35 | static PLSR_RC _examplePlay(float pitch, float volume) { 36 | // Set the requested pitch and volume factors 37 | plsrPlayerSetPitch(g_soundId, pitch); 38 | plsrPlayerSetVolume(g_soundId, volume); 39 | 40 | // Play the previously loaded sound 41 | return plsrPlayerPlay(g_soundId); 42 | } 43 | 44 | int main() { 45 | consoleInit(NULL); 46 | padConfigureInput(1, HidNpadStyleSet_NpadStandard); 47 | 48 | PadState pad; 49 | padInitializeDefault(&pad); 50 | 51 | // Mount qlaunch ROMFS storage (so that the sound archive can be read) 52 | printf("romfsMountDataStorageFromProgram: 0x%x\n", romfsMountDataStorageFromProgram(0x0100000000001000, "qlaunch")); 53 | 54 | // Initialize resources used in this example 55 | PLSR_RC initRC = _exampleInit(); 56 | printf("_exampleInit() = 0x%08X\n\n", initRC); 57 | printf("Press + to exit\n"); 58 | 59 | if(R_SUCCEEDED(initRC)) { 60 | printf("Change pitch with the left stick Y axis\n"); 61 | printf("Change volume with the right stick Y axis\n"); 62 | printf("Press A to play a sound\n"); 63 | } 64 | 65 | // Main loop 66 | while(appletMainLoop()) { 67 | padUpdate(&pad); 68 | u64 kDown = padGetButtonsDown(&pad); 69 | 70 | if(kDown & HidNpadButton_Plus) { 71 | break; 72 | } 73 | 74 | if(R_SUCCEEDED(initRC) && (kDown & HidNpadButton_A)) { 75 | HidAnalogStickState analog_stick_l = padGetStickPos(&pad, 0); 76 | HidAnalogStickState analog_stick_r = padGetStickPos(&pad, 1); 77 | float pitch = 1.0f + (float)analog_stick_l.y / (float)JOYSTICK_MAX; 78 | float volume = 1.0f + (float)analog_stick_r.y / (float)JOYSTICK_MAX; 79 | 80 | if(pitch <= 0.05f) { 81 | pitch = 0.05f; 82 | } 83 | 84 | printf("_examplePlay(%f, %f) = 0x%08X\n", pitch, volume, _examplePlay(pitch, volume)); 85 | } 86 | 87 | consoleUpdate(NULL); 88 | } 89 | 90 | // Clean up used resources 91 | _exampleExit(); 92 | 93 | consoleExit(NULL); 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /examples/example02-browse.c: -------------------------------------------------------------------------------- 1 | /** @example example02-browse.c 2 | * Lists sounds from a specified sound archive and allows to play them 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define LIST_LINES 30 10 | 11 | static PLSR_BFSAR g_bfsar; 12 | static PLSR_PlayerSoundId g_soundId = PLSR_PLAYER_INVALID_SOUND; 13 | static u32 g_selected = 0; 14 | 15 | static PLSR_RC _exampleInit(const char* sarPath) { 16 | // Initialize our player using the default configuration 17 | PLSR_RC_TRY(plsrPlayerInit()); 18 | 19 | // Open the sound archive from the requested path 20 | PLSR_RC_TRY(plsrBFSAROpen(sarPath, &g_bfsar)); 21 | 22 | return PLSR_RC_OK; 23 | } 24 | 25 | static void _exampleExit() { 26 | // Free any loaded sound from memory 27 | plsrPlayerFree(g_soundId); 28 | 29 | // Close the archive 30 | plsrBFSARClose(&g_bfsar); 31 | 32 | // De-initialize our player 33 | plsrPlayerExit(); 34 | } 35 | 36 | static PLSR_RC _examplePlay() { 37 | // Stop and free the last loaded sound 38 | plsrPlayerStop(g_soundId); 39 | plsrPlayerWaitNextFrame(); 40 | plsrPlayerFree(g_soundId); 41 | g_soundId = PLSR_PLAYER_INVALID_SOUND; 42 | 43 | // Construct the item id from type and index 44 | PLSR_BFSARItemId itemId = { 45 | .type = PLSR_BFSARItemType_Sound, 46 | .index = g_selected 47 | }; 48 | 49 | // Load and play the selected sound 50 | PLSR_RC_TRY(plsrPlayerLoadSoundByItemId(&g_bfsar, itemId, &g_soundId)); 51 | PLSR_RC_TRY(plsrPlayerPlay(g_soundId)); 52 | return PLSR_RC_OK; 53 | } 54 | 55 | static PLSR_RC _exampleList() { 56 | // Compute which indexes to display on the screen 57 | u32 displayMin = g_selected < (LIST_LINES/2) ? 0 : g_selected - (LIST_LINES/2); 58 | u32 displayMax = displayMin + LIST_LINES; 59 | 60 | // Erase part of the screen, and print indexes 61 | printf(CONSOLE_ESC(10;1H) CONSOLE_ESC(0J)); 62 | printf("---- %04u / %04u ----\n", g_selected + 1, plsrBFSARSoundCount(&g_bfsar)); 63 | 64 | // Retrieve infos to display about each item, and print them 65 | PLSR_BFSARSoundInfo soundInfo; 66 | char label[0x100]; 67 | const char* desc; 68 | 69 | for(u32 i = displayMin; i < plsrBFSARSoundCount(&g_bfsar) && i <= displayMax; i++) { 70 | PLSR_RC_TRY(plsrBFSARSoundGet(&g_bfsar, i, &soundInfo)); 71 | 72 | // Fetch the sound name if present 73 | if(soundInfo.hasStringIndex) { 74 | PLSR_RC_TRY(plsrBFSARStringGet(&g_bfsar, soundInfo.stringIndex, label, sizeof(label))); 75 | } else { 76 | strcpy(label, "???"); 77 | } 78 | 79 | switch(soundInfo.type) { 80 | case PLSR_BFSARSoundType_Wave: 81 | desc = "WAV"; 82 | break; 83 | case PLSR_BFSARSoundType_Sequence: 84 | desc = "SEQ"; 85 | break; 86 | case PLSR_BFSARSoundType_Stream: 87 | desc = "STM"; 88 | break; 89 | default: 90 | desc = "???"; 91 | break; 92 | } 93 | 94 | printf("%c %s [%s]\n", g_selected == i ? '>' : ' ', label, desc); 95 | } 96 | 97 | printf("---------------------\n"); 98 | 99 | return PLSR_RC_OK; 100 | } 101 | 102 | int main(int argc, char *argv[]) { 103 | consoleInit(NULL); 104 | padConfigureInput(1, HidNpadStyleSet_NpadStandard); 105 | 106 | PadState pad; 107 | padInitializeDefault(&pad); 108 | 109 | // Mount qlaunch ROMFS storage (just so the sound archive inside can be opened later) 110 | printf("romfsMountDataStorageFromProgram: 0x%x\n", romfsMountDataStorageFromProgram(0x0100000000001000, "qlaunch")); 111 | 112 | // Initialize resources used in this example 113 | // We use the qlaunch sound archive unless another one was specified via arguments 114 | const char* sarPath = argc > 1 ? argv[1] : "qlaunch:/sound/qlaunch.bfsar"; 115 | PLSR_RC initRC = _exampleInit(sarPath); 116 | printf("sarPath: %s\n", sarPath); 117 | printf("_exampleInit() = 0x%08X\n\n", initRC); 118 | printf("Press + to exit\n"); 119 | 120 | // If everything is OK, do the initial listing, ignoring its error code 121 | if(R_SUCCEEDED(initRC)) { 122 | printf("Press A to play a sound\n"); 123 | printf("Press UP or DOWN to change your selection\n"); 124 | printf("Use the RIGHT STICK to scroll faster\n\n"); 125 | _exampleList(); 126 | } 127 | 128 | // Main loop 129 | while(appletMainLoop()) { 130 | padUpdate(&pad); 131 | u64 kDown = padGetButtonsDown(&pad); 132 | u64 kHeld = padGetButtons(&pad); 133 | 134 | if(kDown & HidNpadButton_Plus) { 135 | break; 136 | } 137 | 138 | if(R_SUCCEEDED(initRC)) { 139 | if(kDown & HidNpadButton_A) { 140 | printf("_examplePlay() = 0x%08X\n", _examplePlay()); 141 | // Move cursor up one line 142 | printf(CONSOLE_ESC(1A)); 143 | } 144 | 145 | if(((kDown & (HidNpadButton_Up | HidNpadButton_StickLUp)) || (kHeld & HidNpadButton_StickRUp)) && g_selected > 0) { 146 | g_selected--; 147 | _exampleList(); 148 | } 149 | 150 | if(((kDown & (HidNpadButton_Down | HidNpadButton_StickLDown)) || (kHeld & HidNpadButton_StickRDown)) && g_selected < plsrBFSARSoundCount(&g_bfsar) - 1) { 151 | g_selected++; 152 | _exampleList(); 153 | } 154 | } 155 | 156 | consoleUpdate(NULL); 157 | } 158 | 159 | // Clean up used resources 160 | _exampleExit(); 161 | 162 | consoleExit(NULL); 163 | return 0; 164 | } 165 | -------------------------------------------------------------------------------- /include/pulsar.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Main header meant to be included in user projects 4 | */ 5 | #pragma once 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #ifdef __SWITCH__ 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #endif 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif -------------------------------------------------------------------------------- /include/pulsar/archive/archive.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Archive container interface 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | /// Archive version found in header 12 | typedef union { 13 | u32 raw; 14 | struct { 15 | u8 revision; 16 | u8 minor; 17 | u8 major; 18 | }; 19 | } PLSR_ArchiveVersion; 20 | 21 | /// Common archive header information 22 | typedef struct { 23 | PLSR_ArchiveVersion version; 24 | u32 fileSize; 25 | u16 sectionCount; 26 | } PLSR_ArchiveHeaderInfo; 27 | 28 | /// Archive section header information 29 | typedef struct { 30 | u32 blockSize; ///< Section size 31 | } PLSR_ArchiveSectionInfo; 32 | 33 | /// Archive section 34 | typedef struct { 35 | u32 offset; ///< Section start offset in archive 36 | PLSR_ArchiveSectionInfo info; ///< Cached section information 37 | } PLSR_ArchiveSection; 38 | 39 | /// Archive section header information 40 | typedef struct { 41 | u32 count; ///< Table entry/block count 42 | } PLSR_ArchiveTableInfo; 43 | 44 | /// Archive table (entry list) 45 | typedef struct { 46 | u32 offset; ///< Table start offset in archive 47 | PLSR_ArchiveTableInfo info; ///< Cached table information 48 | } PLSR_ArchiveTable; 49 | 50 | /// Archive table entry (fixed size entry) 51 | typedef struct { 52 | u32 offset; ///< Entry start offset in archive 53 | } PLSR_ArchiveTableEntry; 54 | 55 | /// Archive table block (sized entry) 56 | typedef struct { 57 | u32 offset; ///< Entry start offset in archive 58 | u32 size; 59 | } PLSR_ArchiveTableBlock; 60 | 61 | /// Archive list information 62 | typedef struct { 63 | u32 entrySize; 64 | u32 count; 65 | } PLSR_ArchiveListInfo; 66 | 67 | /// Archive list (fixed size data array) 68 | typedef struct { 69 | u32 offset; ///< List start offset in archive 70 | PLSR_ArchiveListInfo info; ///< Cached list information 71 | } PLSR_ArchiveList; 72 | 73 | /// Archive file 74 | /** Created by opening a file, or an embedded archive at a specified offset */ 75 | typedef struct { 76 | PLSR_ArchiveFileHandle handle; ///< File handle, closed when all archives with the same handle are closed 77 | u32 offset; ///< Start offset of the archive in the file 78 | } PLSR_Archive; 79 | 80 | /// Open from a file at specified path (advanced) 81 | PLSR_RC plsrArchiveOpenEx(const char* path, PLSR_Archive* out, bool storePath); 82 | 83 | /// Open from a file at specified path 84 | /** @note Path is not stored by default unless specified using plsrArchiveOpenEx() */ 85 | NX_INLINE PLSR_RC plsrArchiveOpen(const char* path, PLSR_Archive* out) { 86 | return plsrArchiveOpenEx(path, out, false); 87 | } 88 | 89 | /// Open from inside another archive at specified offset 90 | PLSR_RC plsrArchiveOpenInside(const PLSR_Archive* ar, u32 offset, PLSR_Archive* out); 91 | 92 | /// Close archive, releasing resources if applicable 93 | void plsrArchiveClose(PLSR_Archive* ar); 94 | 95 | /// Construct path relative to the physical archive file 96 | /** 97 | * - Does not guarantee it exists (but might fail in some cases if it does not) 98 | * - Works only if the archive path was stored when opened 99 | */ 100 | NX_INLINE PLSR_RC plsrArchiveRelativePath(const PLSR_Archive* ar, const char* path, char* out, size_t size) { 101 | return plsrArchiveFileRelativePath(ar->handle, path, out, size) ? PLSR_RC_OK : PLSR_ResultType_NotFound; 102 | } 103 | 104 | /// Read archive contents at the current position 105 | NX_INLINE PLSR_RC plsrArchiveRead(const PLSR_Archive* ar, void* out, size_t size) { 106 | return plsrArchiveFileRead(ar->handle, out, size) ? PLSR_RC_OK : PLSR_ResultType_FileRead; 107 | } 108 | 109 | /// Read archive contents at the specified offset (advanced) 110 | PLSR_RC plsrArchiveReadAtEx(const PLSR_Archive* ar, u32 offset, void* out, size_t size, bool acceptZero); 111 | 112 | /// Read archive contents at the specified offset 113 | /** @note Offset zero is invalid unless you specify otherwise using plsrArchiveReadAtEx() (start of an archive should always be the header) */ 114 | NX_INLINE PLSR_RC plsrArchiveReadAt(const PLSR_Archive* ar, u32 offset, void* out, size_t size) { 115 | return plsrArchiveReadAtEx(ar, offset, out, size, false); 116 | } 117 | 118 | /// Read a null-terminated string at the specified offset 119 | PLSR_RC plsrArchiveReadString(const PLSR_Archive* ar, u32 offset, char* out, size_t size); 120 | 121 | /// Read archive header 122 | /** @param magic Expected header magic */ 123 | PLSR_RC plsrArchiveReadHeaderInfo(const PLSR_Archive* ar, const char* magic, PLSR_ArchiveHeaderInfo* out); 124 | 125 | /// Read archive section header 126 | /** @param magic Expected section magic */ 127 | PLSR_RC plsrArchiveReadSectionHeaderInfo(const PLSR_Archive* ar, u32 offset, const char* magic, PLSR_ArchiveSectionInfo* out); 128 | 129 | /// Read archive table header information 130 | PLSR_RC plsrArchiveReadTableHeaderInfo(const PLSR_Archive* ar, u32 offset, PLSR_ArchiveTableInfo* out); 131 | 132 | /// Read one archive table entry at specified index 133 | /** @param id Expected reference identifier */ 134 | PLSR_RC plsrArchiveReadTableEntry(const PLSR_Archive* ar, const PLSR_ArchiveTable* table, u16 id, u32 index, PLSR_ArchiveTableEntry* out); 135 | 136 | /// Read one table block at specified index 137 | /** @param id Expected reference identifier */ 138 | PLSR_RC plsrArchiveReadTableBlock(const PLSR_Archive* ar, const PLSR_ArchiveTable* table, u16 id, u32 index, PLSR_ArchiveTableBlock* out); 139 | 140 | /// Read a number of list entries starting from the specified index 141 | /** @note Additionally to checking RC, outReadCount might not match the requested count */ 142 | PLSR_RC plsrArchiveListReadEntries(const PLSR_Archive* ar, const PLSR_ArchiveList* list, u32 startIndex, u32 count, void* outEntries, u32* outReadCount); 143 | 144 | /// Read one list entry 145 | PLSR_RC plsrArchiveListGetEntry(const PLSR_Archive* ar, const PLSR_ArchiveList* list, u32 index, void* out); 146 | -------------------------------------------------------------------------------- /include/pulsar/archive/archive_file.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Shared archive file management 4 | * @note These should not be needed directly, use the wrapped archive functions instead 5 | */ 6 | #pragma once 7 | 8 | #include 9 | 10 | #define PLSR_INVALID_ARCHIVE_FILE_HANDLE NULL 11 | 12 | typedef struct { 13 | FILE* f; 14 | char* context; 15 | size_t refs; 16 | } PLSR_ArchiveSharedReader; 17 | 18 | typedef const PLSR_ArchiveSharedReader* PLSR_ArchiveFileHandle; 19 | 20 | PLSR_ArchiveFileHandle plsrArchiveFileOpen(const char* path, bool storePath); 21 | 22 | void plsrArchiveFileClose(PLSR_ArchiveFileHandle handle); 23 | PLSR_ArchiveFileHandle plsrArchiveFileCloneHandle(PLSR_ArchiveFileHandle handle); 24 | bool plsrArchiveFileRelativePath(PLSR_ArchiveFileHandle handle, const char* path, char* out, size_t size); 25 | 26 | NX_INLINE bool plsrArchiveFileRead(PLSR_ArchiveFileHandle handle, void* out, size_t size) { 27 | return handle && fread(out, size, 1, handle->f) == 1; 28 | } 29 | 30 | NX_INLINE bool plsrArchiveFileSetPosition(PLSR_ArchiveFileHandle handle, long offset) { 31 | return handle && fseek(handle->f, offset, SEEK_SET) != -1; 32 | } 33 | 34 | NX_INLINE bool plsrArchiveFileReadString(PLSR_ArchiveFileHandle handle, char* out, size_t size) { 35 | return handle && fgets(out, size, handle->f) != NULL; 36 | } 37 | -------------------------------------------------------------------------------- /include/pulsar/archive/archive_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define _PLSR_ARCHIVE_ENDIANNESS_LITLE 0xFEFF 6 | #define _PLSR_ARCHIVE_ENDIANNESS_BIG 0xFFFF 7 | 8 | typedef struct { 9 | char magic[4]; 10 | u16 endianness; 11 | u16 headerSize; 12 | u32 version; 13 | u32 fileSize; 14 | u16 sectionCount; 15 | char _padX12[2]; 16 | } _PLSR_ArchiveHeader; 17 | 18 | typedef struct { 19 | char magic[4]; 20 | u32 size; 21 | } _PLSR_ArchiveSectionHeader; 22 | 23 | typedef struct { 24 | u32 count; 25 | } _PLSR_ArchiveTableHeader; 26 | 27 | typedef struct { 28 | u16 id; 29 | char _padX02[2]; 30 | u32 offset; 31 | } _PLSR_ArchiveRef; 32 | 33 | typedef struct { 34 | u16 id; 35 | char _padX02[2]; 36 | u32 offset; 37 | u32 size; 38 | } _PLSR_ArchiveBlockRef; 39 | -------------------------------------------------------------------------------- /include/pulsar/bfgrp/bfgrp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Group init 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | /// Group method categories 11 | typedef enum { 12 | PLSR_BFGRPCategoryType_Init = 0, 13 | PLSR_BFGRPCategoryType_Location, 14 | } PLSR_BFGRPCategoryType; 15 | 16 | /// Group file 17 | typedef struct { 18 | PLSR_Archive ar; 19 | 20 | PLSR_ArchiveHeaderInfo headerInfo; 21 | 22 | PLSR_ArchiveSection infoSection; 23 | PLSR_ArchiveSection fileSection; 24 | 25 | PLSR_ArchiveTable locationTable; 26 | } PLSR_BFGRP; 27 | 28 | /// @copydoc plsrArchiveOpen 29 | PLSR_RC plsrBFGRPOpen(const char* path, PLSR_BFGRP* out); 30 | 31 | /// @copydoc plsrArchiveOpenInside 32 | PLSR_RC plsrBFGRPOpenInside(const PLSR_Archive* ar, u32 offset, PLSR_BFGRP* out); 33 | 34 | /// @copydoc plsrArchiveClose 35 | void plsrBFGRPClose(PLSR_BFGRP* bfgrp); 36 | -------------------------------------------------------------------------------- /include/pulsar/bfgrp/bfgrp_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // @see https://github.com/Kinnay/Nintendo-File-Formats/wiki/BFGRP-File-Format 4 | 5 | #include 6 | #include 7 | 8 | #define _PLSR_BFGRP_MAGIC "FGRP" 9 | #define _PLSR_BFGRP_INFO_MAGIC "INFO" 10 | #define _PLSR_BFGRP_FILE_MAGIC "FILE" 11 | #define _PLSR_BFGRP_INFO_EX_MAGIC "INFX" 12 | 13 | #define _PLSR_BFGRP_SECTION_IDENTIFIER_INFO 0x7800 14 | #define _PLSR_BFGRP_SECTION_IDENTIFIER_FILE 0x7801 15 | #define _PLSR_BFGRP_SECTION_IDENTIFIER_INFO_EX 0x7802 16 | 17 | #define _PLSR_BFGRP_INFO_IDENTIFIER_LOCATION_ENTRY 0x7900 18 | 19 | #define _PLSR_BFGRP_FILE_IDENTIFIER_BLOCK_ENTRY 0x1f00 20 | 21 | typedef struct { 22 | u32 fileIndex; 23 | _PLSR_ArchiveRef fileBlockRef; 24 | u32 fileSize; 25 | } _PLSR_BFGRPLocationEntry; 26 | -------------------------------------------------------------------------------- /include/pulsar/bfgrp/bfgrp_location.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Group location table access 4 | */ 5 | #pragma once 6 | 7 | #include 8 | 9 | /// Group file location information 10 | typedef struct { 11 | u32 fileIndex; 12 | u32 fileBlockOffset; 13 | u32 fileSize; 14 | } PLSR_BFGRPLocationInfo; 15 | 16 | /// Fetch location information from the specified index in the location table 17 | PLSR_RC plsrBFGRPLocationGet(const PLSR_BFGRP* bfgrp, u32 index, PLSR_BFGRPLocationInfo* out); 18 | /// Fetch location information from the specified index in the location table 19 | PLSR_RC plsrBFGRPLocationGet(const PLSR_BFGRP* bfgrp, u32 index, PLSR_BFGRPLocationInfo* out); 20 | 21 | /// Find location information matching the specified fileIndex 22 | PLSR_RC plsrBFGRPLocationFindByFileIndex(const PLSR_BFGRP* bfgrp, u32 fileIndex, PLSR_BFGRPLocationInfo* out); 23 | 24 | NX_INLINE u32 plsrBFGRPLocationCount(const PLSR_BFGRP* bfgrp) { 25 | return bfgrp->locationTable.info.count; 26 | } 27 | -------------------------------------------------------------------------------- /include/pulsar/bfsar/bfsar.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Sound archive init 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | /// Sound archive method categories 11 | typedef enum { 12 | PLSR_BFSARCategoryType_Init = 0, 13 | PLSR_BFSARCategoryType_IdList, 14 | PLSR_BFSARCategoryType_String, 15 | PLSR_BFSARCategoryType_Sound, 16 | PLSR_BFSARCategoryType_WaveArchive, 17 | PLSR_BFSARCategoryType_Group, 18 | PLSR_BFSARCategoryType_File, 19 | } PLSR_BFSARCategoryType; 20 | 21 | /// String tree header information 22 | typedef struct { 23 | u32 rootNodeIndex; 24 | u32 nodeCount; 25 | } PLSR_BFSARStringTreeInfo; 26 | 27 | /// String tree (patricia trie) 28 | typedef struct { 29 | u32 offset; ///< Section start offset in sound archive 30 | PLSR_BFSARStringTreeInfo info; ///< Cached tree information 31 | } PLSR_BFSARStringTree; 32 | 33 | /// Sound archive file 34 | typedef struct { 35 | PLSR_Archive ar; 36 | 37 | PLSR_ArchiveHeaderInfo headerInfo; 38 | 39 | PLSR_ArchiveSection strgSection; 40 | PLSR_ArchiveSection infoSection; 41 | PLSR_ArchiveSection fileSection; 42 | 43 | PLSR_ArchiveTable stringTable; 44 | PLSR_BFSARStringTree stringTree; 45 | 46 | PLSR_ArchiveTable soundTable; 47 | PLSR_ArchiveTable waveArchiveTable; 48 | PLSR_ArchiveTable groupTable; 49 | PLSR_ArchiveTable fileTable; 50 | } PLSR_BFSAR; 51 | 52 | /// @copydoc plsrArchiveOpen 53 | PLSR_RC plsrBFSAROpen(const char* path, PLSR_BFSAR* out); 54 | 55 | /// @copydoc plsrArchiveClose 56 | void plsrBFSARClose(PLSR_BFSAR* bfsar); 57 | -------------------------------------------------------------------------------- /include/pulsar/bfsar/bfsar_file.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Sound archive file table access 4 | */ 5 | #pragma once 6 | 7 | #include 8 | 9 | /// Sound archive file type 10 | typedef enum { 11 | PLSR_BFSARFileInfoType_Unknown = 0, ///< Unknown 12 | 13 | /// Sub-file is embedded in the sound archive 14 | /** @note Can be embedded directly in the FILE section, or in one of the sound archive groups */ 15 | PLSR_BFSARFileInfoType_Internal = _PLSR_BFSAR_FILE_IDENTIFIER_INTERNAL, 16 | 17 | /// Sub-file is external to the archive and referenced by path 18 | PLSR_BFSARFileInfoType_External = _PLSR_BFSAR_FILE_IDENTIFIER_EXTERNAL, 19 | } PLSR_BFSARFileInfoType; 20 | 21 | /// Sound archive external file location 22 | typedef struct { 23 | char path[FS_MAX_PATH]; 24 | } PLSR_BFSARFileInfoExternal; 25 | 26 | /// Sound archive internal file location 27 | typedef struct { 28 | /// Can be `false` in case offset and/or size are not pointing to a file 29 | /** 30 | * - `false` usually means the file is within a group (and that groups should be scanned to find the actual location) 31 | * - Always `true` if returned from plsrBFSARFileScan() 32 | */ 33 | bool valid; 34 | 35 | u16 id; 36 | u32 offset; 37 | u32 size; 38 | } PLSR_BFSARFileInfoInternal; 39 | 40 | /// Sound archive file information 41 | typedef struct { 42 | PLSR_BFSARFileInfoType type; 43 | union { 44 | PLSR_BFSARFileInfoInternal internal; 45 | PLSR_BFSARFileInfoExternal external; 46 | }; 47 | bool fromGroup; ///< `true` if file was found in a group, as opposed to directly stored in the FILE section 48 | u32 groupIndex; ///< Group index the file was found in (populated if fromGroup is set to `true`) 49 | } PLSR_BFSARFileInfo; 50 | 51 | /// Fetch file information from the specified index in the file table 52 | PLSR_RC plsrBFSARFileGet(const PLSR_BFSAR* bfsar, u32 index, PLSR_BFSARFileInfo* out); 53 | 54 | /// Similar to plsrBFSARFileGet(), but also scans groups for the requested index in case file is internal and has an invalid location 55 | PLSR_RC plsrBFSARFileScan(const PLSR_BFSAR* bfsar, u32 index, PLSR_BFSARFileInfo* out); 56 | 57 | /// Make sound file information less archive dependent (eg: external path become absolute) 58 | PLSR_RC plsrBFSARFileInfoNormalize(const PLSR_BFSAR* bfsar, PLSR_BFSARFileInfo* soundFileInfo); 59 | 60 | NX_INLINE u32 plsrBFSARFileCount(const PLSR_BFSAR* bfsar) { 61 | return bfsar->fileTable.info.count; 62 | } 63 | -------------------------------------------------------------------------------- /include/pulsar/bfsar/bfsar_group.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Sound archive group table access 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | /// Sound archive group information 11 | typedef struct { 12 | u32 fileIndex; ///< Index in the file table (see plsrBFSARFileGet()) 13 | 14 | bool hasStringIndex; 15 | u32 stringIndex; ///< Name index in the string table, populated if `hasStringIndex` is `true` (see plsrBFSARStringGet()) 16 | } PLSR_BFSARGroupInfo; 17 | 18 | /// Fetch group information from the specified index in the group table 19 | PLSR_RC plsrBFSARGroupGet(const PLSR_BFSAR* bfsar, u32 index, PLSR_BFSARGroupInfo* out); 20 | 21 | /// Open a group file from a plsrBFSARGroupGet() result (see plsrBFGRPOpenInside()) 22 | PLSR_RC plsrBFSARGroupOpen(const PLSR_BFSAR* bfsar, const PLSR_BFSARGroupInfo* groupInfo, PLSR_BFGRP* out); 23 | 24 | NX_INLINE u32 plsrBFSARGroupCount(const PLSR_BFSAR* bfsar) { 25 | return bfsar->groupTable.info.count; 26 | } 27 | -------------------------------------------------------------------------------- /include/pulsar/bfsar/bfsar_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // @see https://github.com/Kinnay/Nintendo-File-Formats/wiki/BFSAR-File-Format 7 | // @see http://mk8.tockdom.com/wiki/BFSAR_(File_Format) 8 | 9 | #define _PLSR_BFSAR_MAGIC "FSAR" 10 | #define _PLSR_BFSAR_STRG_MAGIC "STRG" 11 | #define _PLSR_BFSAR_INFO_MAGIC "INFO" 12 | #define _PLSR_BFSAR_FILE_MAGIC "FILE" 13 | 14 | #define _PLSR_BFSAR_SECTION_IDENTIFIER_STRG 0x2000 15 | #define _PLSR_BFSAR_SECTION_IDENTIFIER_INFO 0x2001 16 | #define _PLSR_BFSAR_SECTION_IDENTIFIER_FILE 0x2002 17 | 18 | #define _PLSR_BFSAR_SECTION_ID_LIST 0x0100 19 | 20 | #define _PLSR_BFSAR_STRG_IDENTIFIER_STRING_TABLE 0x2400 21 | #define _PLSR_BFSAR_STRG_IDENTIFIER_STRING_TABLE_ENTRY 0x1f01 22 | #define _PLSR_BFSAR_STRG_IDENTIFIER_STRING_TREE 0x2401 23 | 24 | #define _PLSR_BFSAR_INFO_IDENTIFIER_SOUND_TABLE 0x2100 25 | #define _PLSR_BFSAR_INFO_IDENTIFIER_BANK_TABLE 0x2101 26 | #define _PLSR_BFSAR_INFO_IDENTIFIER_PLAYER_TABLE 0x2102 27 | #define _PLSR_BFSAR_INFO_IDENTIFIER_WAVE_ARCHIVE_TABLE 0x2103 28 | #define _PLSR_BFSAR_INFO_IDENTIFIER_SOUND_SET_TABLE 0x2104 29 | #define _PLSR_BFSAR_INFO_IDENTIFIER_GROUP_TABLE 0x2105 30 | #define _PLSR_BFSAR_INFO_IDENTIFIER_FILE_TABLE 0x2106 31 | #define _PLSR_BFSAR_INFO_IDENTIFIER_SOUND_ARCHIVE_INFO 0x220B 32 | 33 | #define _PLSR_BFSAR_INFO_IDENTIFIER_SOUND_ENTRY 0x2200 34 | #define _PLSR_BFSAR_INFO_IDENTIFIER_STREAM_ENTRY 0x2201 35 | #define _PLSR_BFSAR_INFO_IDENTIFIER_WAVE_ENTRY 0x2202 36 | #define _PLSR_BFSAR_INFO_IDENTIFIER_SEQUENCE_ENTRY 0x2203 37 | #define _PLSR_BFSAR_INFO_IDENTIFIER_SOUND_SET_ENTRY 0x2204 38 | #define _PLSR_BFSAR_INFO_IDENTIFIER_WAVE_ARCHIVE_TABLE_ENTRY 0x2205 39 | #define _PLSR_BFSAR_INFO_IDENTIFIER_BANK_ENTRY 0x2206 40 | #define _PLSR_BFSAR_INFO_IDENTIFIER_WAVE_ARCHIVE_ENTRY 0x2207 41 | #define _PLSR_BFSAR_INFO_IDENTIFIER_GROUP_ENTRY 0x2208 42 | #define _PLSR_BFSAR_INFO_IDENTIFIER_PLAYER_INFO_ENTRY 0x2209 43 | #define _PLSR_BFSAR_INFO_IDENTIFIER_FILE_ENTRY 0x220A 44 | 45 | #define _PLSR_BFSAR_FILE_IDENTIFIER_INTERNAL 0x220c 46 | #define _PLSR_BFSAR_FILE_IDENTIFIER_EXTERNAL 0x220d 47 | 48 | #define _PLSR_BFSAR_FILE_TYPE_SOUND 1 49 | #define _PLSR_BFSAR_FILE_TYPE_SOUND_SET 2 50 | #define _PLSR_BFSAR_FILE_TYPE_BANK 3 51 | #define _PLSR_BFSAR_FILE_TYPE_PLAYER 4 52 | #define _PLSR_BFSAR_FILE_TYPE_WAVE_ARCHIVE 5 53 | #define _PLSR_BFSAR_FILE_TYPE_GROUP 6 54 | 55 | #define _PLSR_BFSAR_ITEM_ID_INDEX_BITS 24 56 | #define _PLSR_BFSAR_ITEM_ID_TYPE_BITS 8 57 | 58 | #define _PLSR_BFSAR_FLAG_STRING_INDEX BIT(0) 59 | #define _PLSR_BFSAR_FLAG_WAVE_ARCHIVE_COUNT BIT(1) 60 | 61 | typedef struct { 62 | u32 count; 63 | } _PLSR_BFSARIdListHeader; 64 | 65 | typedef struct { 66 | u32 rootNodeIndex; 67 | u32 nodeCount; 68 | } _PLSR_BFSARStringTreeHeader; 69 | 70 | typedef struct { 71 | u16 endpointFlag; 72 | u16 stringBit; 73 | u32 leftIndex; 74 | u32 rightIndex; 75 | u32 stringTableIndex; 76 | 77 | u32 itemId; 78 | } _PLSR_BFSARStringTreeNode; 79 | 80 | typedef struct { 81 | u32 fileIndex; 82 | u32 flags; 83 | } _PLSR_BFSARGroupEntry; 84 | 85 | typedef struct { 86 | u32 fileIndex; 87 | u32 playerItemId; 88 | u8 initialVolume; 89 | u8 remoteFilter; 90 | char _padX0A[2]; 91 | _PLSR_ArchiveRef ref; 92 | u32 flags; 93 | } _PLSR_BFSARSoundEntry; 94 | 95 | typedef struct { 96 | u32 index; 97 | u32 trackCount; 98 | u32 flags; 99 | } _PLSR_BFSARSoundWaveEntry; 100 | 101 | typedef struct { 102 | u32 firstItemId; 103 | u32 lastItemId; 104 | _PLSR_ArchiveRef fileTableRef; 105 | _PLSR_ArchiveRef waveArchiveTableRef; 106 | u32 flags; 107 | } _PLSR_BFSARSoundSetEntry; 108 | 109 | typedef struct { 110 | u32 fileIndex; 111 | char _unkX04[1]; 112 | char _padX05[3]; 113 | u32 flags; 114 | } _PLSR_BFSARWaveArchiveEntry; 115 | -------------------------------------------------------------------------------- /include/pulsar/bfsar/bfsar_item.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Sound archive item id types 4 | */ 5 | #pragma once 6 | 7 | #include 8 | 9 | /// Item types 10 | typedef enum { 11 | PLSR_BFSARItemType_Unknown = 0, 12 | 13 | /// (Sound) use plsrBFSARSoundGet() with index 14 | PLSR_BFSARItemType_Sound = _PLSR_BFSAR_FILE_TYPE_SOUND, 15 | 16 | /// (Sound Set) use plsrBFSARSoundSetGet() with index 17 | PLSR_BFSARItemType_SoundSet = _PLSR_BFSAR_FILE_TYPE_SOUND_SET, 18 | 19 | /// (Bank) Unimplemented 20 | PLSR_BFSARItemType_Bank = _PLSR_BFSAR_FILE_TYPE_BANK, 21 | 22 | /// (Player settings) Unimplemented 23 | PLSR_BFSARItemType_Player = _PLSR_BFSAR_FILE_TYPE_PLAYER, 24 | 25 | /// (Wave Archive) use plsrBFSARWaveArchiveGet() with index 26 | PLSR_BFSARItemType_WaveArchive = _PLSR_BFSAR_FILE_TYPE_WAVE_ARCHIVE, 27 | 28 | /// (Group) use plsrBFSARGroupGet() with index 29 | PLSR_BFSARItemType_Group = _PLSR_BFSAR_FILE_TYPE_GROUP, 30 | } PLSR_BFSARItemType; 31 | 32 | /// Id for one item/resource contained in the sound archive 33 | /** Item ids contain the item type to decide which table to look in, and the index of the item in that table */ 34 | typedef union { 35 | uint32_t raw; 36 | struct { 37 | uint32_t index : _PLSR_BFSAR_ITEM_ID_INDEX_BITS; 38 | PLSR_BFSARItemType type : _PLSR_BFSAR_ITEM_ID_TYPE_BITS; 39 | }; 40 | } PLSR_BFSARItemId; 41 | -------------------------------------------------------------------------------- /include/pulsar/bfsar/bfsar_sound.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Sound archive sound table access 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | /// Sound types 11 | typedef enum { 12 | PLSR_BFSARSoundType_Stream = _PLSR_BFSAR_INFO_IDENTIFIER_STREAM_ENTRY, 13 | PLSR_BFSARSoundType_Wave = _PLSR_BFSAR_INFO_IDENTIFIER_WAVE_ENTRY, 14 | PLSR_BFSARSoundType_Sequence = _PLSR_BFSAR_INFO_IDENTIFIER_SEQUENCE_ENTRY, 15 | } PLSR_BFSARSoundType; 16 | 17 | /// Sound wave information 18 | typedef struct { 19 | u32 index; ///< Wave index in wave archive 20 | u32 trackCount; 21 | } PLSR_BFSARSoundWaveInfo; 22 | 23 | /// Sound information 24 | typedef struct { 25 | u32 fileIndex; ///< Related file index (wave type = WSD file, see plsrBFSARFileGet()) 26 | PLSR_BFSARItemId playerItemId; 27 | u8 initialVolume; 28 | u8 remoteFilter; 29 | PLSR_BFSARSoundType type; 30 | 31 | PLSR_BFSARSoundWaveInfo wave; ///< Populated if type is `PLSR_BFSARSoundType_Wave` 32 | 33 | bool hasStringIndex; 34 | u32 stringIndex; ///< Name index in the string table, populated if `hasStringIndex` is `true` (see plsrBFSARStringGet()) 35 | } PLSR_BFSARSoundInfo; 36 | 37 | /// Fetch sound information from the specified index in the sound table 38 | PLSR_RC plsrBFSARSoundGet(const PLSR_BFSAR* bfsar, u32 index, PLSR_BFSARSoundInfo* out); 39 | 40 | NX_INLINE u32 plsrBFSARSoundCount(const PLSR_BFSAR* bfsar) { 41 | return bfsar->soundTable.info.count; 42 | } 43 | -------------------------------------------------------------------------------- /include/pulsar/bfsar/bfsar_string.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Sound archive string search and table access 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | /// String tree search result information 11 | typedef struct { 12 | char name[FS_MAX_PATH]; ///< Actual string matched during tree search 13 | PLSR_BFSARItemId itemId; ///< Linked string item ID 14 | } PLSR_BFSARStringSearchInfo; 15 | 16 | /// Fetch string from the specified index in the string table 17 | PLSR_RC plsrBFSARStringGet(const PLSR_BFSAR* bfsar, u32 index, char* out, size_t size); 18 | 19 | PLSR_RC plsrBFSARStringSearchEx(const PLSR_BFSAR* bfsar, const char* query, size_t queryLen, PLSR_BFSARStringSearchInfo* out); 20 | 21 | /// Search string tree for an item ID with specified name 22 | NX_INLINE PLSR_RC plsrBFSARStringSearch(const PLSR_BFSAR* bfsar, const char* query, PLSR_BFSARStringSearchInfo* out) { 23 | return plsrBFSARStringSearchEx(bfsar, query, strlen(query), out); 24 | } 25 | 26 | NX_INLINE u32 plsrBFSARStringCount(const PLSR_BFSAR* bfsar) { 27 | return bfsar->stringTable.info.count; 28 | } 29 | -------------------------------------------------------------------------------- /include/pulsar/bfsar/bfsar_wave_archive.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Sound archive wave archive table access 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | /// Wave archive information 11 | typedef struct { 12 | u32 fileIndex; ///< Index in the file table (see plsrBFSARFileGet()) 13 | 14 | bool hasStringIndex; 15 | u32 stringIndex; ///< Name index in the string table, populated if `hasStringIndex` is `true` (see plsrBFSARStringGet()) 16 | 17 | bool hasWaveCount; 18 | u32 waveCount; ///< Total wave count in archive, populated if `hasWaveCount` 19 | } PLSR_BFSARWaveArchiveInfo; 20 | 21 | /// Fetch wave archive information from the specified index in the wave archive table 22 | PLSR_RC plsrBFSARWaveArchiveGet(const PLSR_BFSAR* bfsar, u32 index, PLSR_BFSARWaveArchiveInfo* out); 23 | 24 | /// Open a wave archive from a plsrBFSARWaveArchiveGet() result (see plsrBFWAROpenInside()) 25 | PLSR_RC plsrBFSARWaveArchiveOpen(const PLSR_BFSAR* bfsar, const PLSR_BFSARWaveArchiveInfo* waveArchiveInfo, PLSR_BFWAR* out); 26 | 27 | NX_INLINE u32 plsrBFSARWaveArchiveCount(const PLSR_BFSAR* bfsar) { 28 | return bfsar->waveArchiveTable.info.count; 29 | } 30 | -------------------------------------------------------------------------------- /include/pulsar/bfstm/bfstm.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Stream file init 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | /// Stream file method categories 11 | typedef enum { 12 | PLSR_BFSTMCategoryType_Init = 0, 13 | PLSR_BFSTMCategoryType_Info, 14 | PLSR_BFSTMCategoryType_Channel, 15 | } PLSR_BFSTMCategoryType; 16 | 17 | /// Stream file sample formats 18 | typedef enum { 19 | /// 8-bit PCM encoded samples 20 | PLSR_BFSTMFormat_PCM_8 = _PLSR_BFSTM_FORMAT_PCM_8, 21 | 22 | /// 16-bit PCM encoded samples 23 | PLSR_BFSTMFormat_PCM_16 = _PLSR_BFSTM_FORMAT_PCM_16, 24 | 25 | /// DSP ADPCM encoded samples (Wave channel info should contain the needed adpcm context to decode samples, see plsrBFWAVReadChannelInfo()) 26 | PLSR_BFSTMFormat_DSP_ADPCM = _PLSR_BFSTM_FORMAT_DSP_ADPCM, 27 | } PLSR_BFSTMFormat; 28 | 29 | /// Stream file 30 | typedef struct { 31 | PLSR_Archive ar; 32 | 33 | PLSR_ArchiveHeaderInfo headerInfo; 34 | 35 | PLSR_ArchiveSection infoSection; 36 | PLSR_ArchiveSection dataSection; 37 | 38 | u32 streamInfoOffset; 39 | PLSR_ArchiveTable channelTable; 40 | } PLSR_BFSTM; 41 | 42 | /// @copydoc plsrArchiveOpen 43 | PLSR_RC plsrBFSTMOpen(const char* path, PLSR_BFSTM* out); 44 | 45 | /// @copydoc plsrArchiveOpenInside 46 | PLSR_RC plsrBFSTMOpenInside(const PLSR_Archive* ar, u32 offset, PLSR_BFSTM* out); 47 | 48 | /// @copydoc plsrArchiveClose 49 | void plsrBFSTMClose(PLSR_BFSTM* bfstm); 50 | -------------------------------------------------------------------------------- /include/pulsar/bfstm/bfstm_channel.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Stream file channel info 4 | */ 5 | #pragma once 6 | 7 | #include 8 | 9 | /// Stream channel ADPCM context information 10 | typedef struct { 11 | u16 header; ///< Initial header (predictor/scale) 12 | s16 yn1; ///< Sample history 1 13 | s16 yn2; ///< Sample history 2 14 | } PLSR_BFSTMChannelAdpcmContextInfo; 15 | 16 | /// Stream channel ADPCM information (coefficients and contexts) 17 | typedef struct { 18 | u16 coeffs[16]; ///< ADPCM coefficients 19 | PLSR_BFSTMChannelAdpcmContextInfo main; ///< ADPCM main context 20 | PLSR_BFSTMChannelAdpcmContextInfo loop; ///< ADPCM loop context (if stream info `looping` flag is set) 21 | } PLSR_BFSTMChannelAdpcmInfo; 22 | 23 | /// Stream channel information 24 | typedef struct { 25 | PLSR_BFSTMChannelAdpcmInfo adpcmInfo; ///< ADPCM information (if stream sample format is PLSR_BFSTMFormat_DSP_ADPCM) 26 | } PLSR_BFSTMChannelInfo; 27 | 28 | /// Fetch channel information from the specified index in the table 29 | PLSR_RC plsrBFSTMChannelGet(const PLSR_BFSTM* bfstm, u32 index, PLSR_BFSTMChannelInfo* out); 30 | 31 | NX_INLINE u32 plsrBFSTMChannelCount(const PLSR_BFSTM* bfstm) { 32 | return bfstm->channelTable.info.count; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /include/pulsar/bfstm/bfstm_info.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Stream file info 4 | */ 5 | #pragma once 6 | 7 | #include 8 | 9 | /// Stream information 10 | typedef struct { 11 | PLSR_BFSTMFormat format; ///< Sample format 12 | bool looping; ///< `true` if the file contains a looping section 13 | u32 sampleRate; ///< Sample rate 14 | u32 loopStartSample; ///< Sample index where the loop starts (if looping is `true`) 15 | u32 sampleCount; ///< Sample count 16 | u32 dataOffset; ///< Offset to first channel sample data relative to the start of the file (see plsrArchiveReadAt() to read) 17 | 18 | u32 blockCount; ///< Block count 19 | u32 blockSize; /// Size of one block 20 | u32 blockSampleCount; /// Sample count in one block 21 | u32 lastBlockSize; /// Size of the last block 22 | u32 lastBlockSampleCount; /// Sample count of the last block 23 | u32 lastBlockSizeWithPadding; /// Padded size of the last block 24 | } PLSR_BFSTMInfo; 25 | 26 | /// Read wave file information from header 27 | PLSR_RC plsrBFSTMReadInfo(const PLSR_BFSTM* bfstm, PLSR_BFSTMInfo* out); 28 | 29 | -------------------------------------------------------------------------------- /include/pulsar/bfstm/bfstm_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // @see https://github.com/Kinnay/Nintendo-File-Formats/wiki/BFSTM-File-Format 4 | // @see http://mk8.tockdom.com/wiki/BFSTM_(File_Format) 5 | 6 | #include 7 | #include 8 | 9 | #define _PLSR_BFSTM_MAGIC "FSTM" 10 | #define _PLSR_BFSTM_INFO_MAGIC "INFO" 11 | #define _PLSR_BFSTM_DATA_MAGIC "DATA" 12 | 13 | #define _PLSR_BFSTM_SECTION_IDENTIFIER_INFO 0x4000 14 | #define _PLSR_BFSTM_SECTION_IDENTIFIER_DATA 0x4002 15 | 16 | #define _PLSR_BFSTM_INFO_IDENTIFIER_STREAM_INFO 0x4100 17 | #define _PLSR_BFSTM_INFO_IDENTIFIER_TRACK_TABLE 0x0101 18 | #define _PLSR_BFSTM_INFO_IDENTIFIER_CHANNEL_TABLE 0x0101 19 | 20 | #define _PLSR_BFSTM_INFO_IDENTIFIER_STREAM_DATA 0x1F00 21 | #define _PLSR_BFSTM_INFO_IDENTIFIER_CHANNEL_ENTRY 0x4102 22 | #define _PLSR_BFSTM_INFO_IDENTIFIER_ADPCM_ENTRY 0x0300 23 | 24 | #define _PLSR_BFSTM_FORMAT_PCM_8 0 25 | #define _PLSR_BFSTM_FORMAT_PCM_16 1 26 | #define _PLSR_BFSTM_FORMAT_DSP_ADPCM 2 27 | 28 | typedef struct { 29 | u8 format; 30 | u8 looping; 31 | char _unkX03[2]; 32 | u32 sampleRate; 33 | u32 loopStartSample; 34 | u32 sampleCount; 35 | u32 blockCount; 36 | u32 blockSize; 37 | u32 blockSampleCount; 38 | u32 lastBlockSize; 39 | u32 lastBlockSampleCount; 40 | u32 lastBlockSizeWithPadding; 41 | u32 seekInfoSize; 42 | u32 seekSampleInterval; 43 | _PLSR_ArchiveRef dataRef; 44 | } _PLSR_BFSTMInfo; 45 | 46 | typedef struct { 47 | _PLSR_ArchiveRef adpcmInfoRef; 48 | } _PLSR_BFSTMChannelInfo; 49 | -------------------------------------------------------------------------------- /include/pulsar/bfwar/bfwar.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Wave archive init 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | /// Wave archive method categories 11 | typedef enum { 12 | PLSR_BFWARCategoryType_Init = 0, 13 | PLSR_BFWARCategoryType_File, 14 | } PLSR_BFWARCategoryType; 15 | 16 | /// Wave archive file 17 | typedef struct { 18 | PLSR_Archive ar; 19 | 20 | PLSR_ArchiveHeaderInfo headerInfo; 21 | 22 | PLSR_ArchiveSection infoSection; 23 | PLSR_ArchiveSection fileSection; 24 | 25 | PLSR_ArchiveTable fileTable; 26 | } PLSR_BFWAR; 27 | 28 | /// @copydoc plsrArchiveOpen 29 | PLSR_RC plsrBFWAROpen(const char* path, PLSR_BFWAR* out); 30 | 31 | /// @copydoc plsrArchiveOpenInside 32 | PLSR_RC plsrBFWAROpenInside(const PLSR_Archive* ar, u32 offset, PLSR_BFWAR* out); 33 | 34 | /// @copydoc plsrArchiveClose 35 | void plsrBFWARClose(PLSR_BFWAR* bfwar); 36 | -------------------------------------------------------------------------------- /include/pulsar/bfwar/bfwar_file.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Wave archive init 4 | */ 5 | #pragma once 6 | 7 | #include 8 | 9 | /// Wave archive file information 10 | typedef struct { 11 | u32 offset; 12 | u32 size; 13 | } PLSR_BFWARFileInfo; 14 | 15 | /// Fetch wave file location information from the specified index in the file table 16 | PLSR_RC plsrBFWARFileGet(const PLSR_BFWAR* bfwar, u32 index, PLSR_BFWARFileInfo* out); 17 | 18 | NX_INLINE u32 plsrBFWARFileCount(const PLSR_BFWAR* bfwar) { 19 | return bfwar->fileTable.info.count; 20 | } 21 | -------------------------------------------------------------------------------- /include/pulsar/bfwar/bfwar_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // @see https://github.com/Kinnay/Nintendo-File-Formats/wiki/BFWAR-File-Format 4 | 5 | #include 6 | #include 7 | 8 | #define _PLSR_BFWAR_MAGIC "FWAR" 9 | #define _PLSR_BFWAR_INFO_MAGIC "INFO" 10 | #define _PLSR_BFWAR_FILE_MAGIC "FILE" 11 | 12 | #define _PLSR_BFWAR_SECTION_IDENTIFIER_INFO 0x6800 13 | #define _PLSR_BFWAR_SECTION_IDENTIFIER_FILE 0x6801 14 | 15 | #define _PLSR_BFWAR_FILE_IDENTIFIER_BLOCK_ENTRY 0x1F00 16 | 17 | -------------------------------------------------------------------------------- /include/pulsar/bfwav/bfwav.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Wave file init 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | /// Wave file method categories 11 | typedef enum { 12 | PLSR_BFWAVCategoryType_Init = 0, 13 | PLSR_BFWAVCategoryType_Info, 14 | PLSR_BFWAVCategoryType_Data, 15 | } PLSR_BFWAVCategoryType; 16 | 17 | /// Wave file sample formats 18 | typedef enum { 19 | /// 8-bit PCM encoded samples 20 | PLSR_BFWAVFormat_PCM_8 = _PLSR_BFWAV_FORMAT_PCM_8, 21 | 22 | /// 16-bit PCM encoded samples 23 | PLSR_BFWAVFormat_PCM_16 = _PLSR_BFWAV_FORMAT_PCM_16, 24 | 25 | /// DSP ADPCM encoded samples (Wave channel info should contain the needed ADPCM context to decode samples, see plsrBFWAVReadChannelInfo()) 26 | PLSR_BFWAVFormat_DSP_ADPCM = _PLSR_BFWAV_FORMAT_DSP_ADPCM, 27 | } PLSR_BFWAVFormat; 28 | 29 | /// Wave file 30 | typedef struct { 31 | PLSR_Archive ar; 32 | 33 | PLSR_ArchiveHeaderInfo headerInfo; 34 | 35 | PLSR_ArchiveSection infoSection; 36 | PLSR_ArchiveSection dataSection; 37 | } PLSR_BFWAV; 38 | 39 | /// @copydoc plsrArchiveOpen 40 | PLSR_RC plsrBFWAVOpen(const char* path, PLSR_BFWAV* out); 41 | 42 | /// @copydoc plsrArchiveOpenInside 43 | PLSR_RC plsrBFWAVOpenInside(const PLSR_Archive* ar, u32 offset, PLSR_BFWAV* out); 44 | 45 | /// @copydoc plsrArchiveClose 46 | void plsrBFWAVClose(PLSR_BFWAV* bfwav); 47 | -------------------------------------------------------------------------------- /include/pulsar/bfwav/bfwav_info.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Wave file info 4 | */ 5 | #pragma once 6 | 7 | #include 8 | 9 | /// Wave channel ADPCM context information 10 | typedef struct { 11 | u16 header; ///< Initial header (predictor/scale) 12 | s16 yn1; ///< Sample history 1 13 | s16 yn2; ///< Sample history 2 14 | } PLSR_BFWAVAdpcmContextInfo; 15 | 16 | /// Wave channel ADPCM information (coefficients and contexts) 17 | typedef struct { 18 | u16 coeffs[16]; ///< ADPCM coefficients 19 | PLSR_BFWAVAdpcmContextInfo main; ///< ADPCM main context 20 | PLSR_BFWAVAdpcmContextInfo loop; ///< ADPCM loop context (if wave info `looping` flag is set) 21 | } PLSR_BFWAVAdpcmInfo; 22 | 23 | /// Wave channel information 24 | typedef struct { 25 | u32 dataOffset; ///< Offset to channel sample data relative to the start of the file (see plsrArchiveReadAt() to read) 26 | PLSR_BFWAVAdpcmInfo adpcmInfo; ///< ADPCM information (if wave sample format is PLSR_BFWAVFormat_DSP_ADPCM) 27 | } PLSR_BFWAVChannelInfo; 28 | 29 | /// Wave channel information table 30 | typedef PLSR_ArchiveTable PLSR_BFWAVChannelInfoTable; 31 | 32 | /// Wave information 33 | typedef struct { 34 | PLSR_BFWAVFormat format; ///< Sample format 35 | bool looping; ///< `true` if the file contains a looping section 36 | u32 sampleRate; ///< Sample rate 37 | u32 loopStartSample; ///< Sample index where the loop starts (if looping is `true`) 38 | u32 sampleCount; ///< Sample count 39 | PLSR_BFWAVChannelInfoTable channelInfoTable; ///< Channel Information table 40 | } PLSR_BFWAVInfo; 41 | 42 | /// Read wave file information from header 43 | PLSR_RC plsrBFWAVReadInfo(const PLSR_BFWAV* bfwav, PLSR_BFWAVInfo* out); 44 | 45 | /// Read wave channel information from the specified index in the channel info table 46 | PLSR_RC plsrBFWAVReadChannelInfo(const PLSR_BFWAV* bfwav, const PLSR_BFWAVChannelInfoTable* table, u32 index, PLSR_BFWAVChannelInfo* out); 47 | -------------------------------------------------------------------------------- /include/pulsar/bfwav/bfwav_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // @see https://github.com/Kinnay/Nintendo-File-Formats/wiki/BFWAR-File-Format 4 | 5 | #include 6 | #include 7 | 8 | #define _PLSR_BFWAV_MAGIC "FWAV" 9 | #define _PLSR_BFWAV_INFO_MAGIC "INFO" 10 | #define _PLSR_BFWAV_DATA_MAGIC "DATA" 11 | 12 | #define _PLSR_BFWAV_SECTION_IDENTIFIER_INFO 0x7000 13 | #define _PLSR_BFWAV_SECTION_IDENTIFIER_DATA 0x7001 14 | 15 | #define _PLSR_BFWAV_INFO_IDENTIFIER_CHANNEL_ENTRY 0x7100 16 | 17 | #define _PLSR_BFWAV_INFO_IDENTIFIER_DATA_ENTRY 0x1f00 18 | #define _PLSR_BFWAV_INFO_IDENTIFIER_ADPCM_ENTRY 0x0300 19 | 20 | #define _PLSR_BFWAV_FORMAT_PCM_8 0 21 | #define _PLSR_BFWAV_FORMAT_PCM_16 1 22 | #define _PLSR_BFWAV_FORMAT_DSP_ADPCM 2 23 | 24 | typedef struct { 25 | u8 format; 26 | u8 looping; 27 | char _padX03[2]; 28 | u32 sampleRate; 29 | u32 loopStartSample; 30 | u32 sampleCount; 31 | char _padX11[4]; 32 | } _PLSR_BFWAVInfo; 33 | 34 | typedef struct { 35 | _PLSR_ArchiveRef dataRef; 36 | _PLSR_ArchiveRef adpcmInfoRef; 37 | } _PLSR_BFWAVChannelInfo; 38 | -------------------------------------------------------------------------------- /include/pulsar/bfwsd/bfwsd.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Wave sound data archive init 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | /// Wave sound data archive method categories 11 | typedef enum { 12 | PLSR_BFWSDCategoryType_Init = 0, 13 | PLSR_BFWSDCategoryType_WaveId, 14 | PLSR_BFWSDCategoryType_SoundData, 15 | } PLSR_BFWSDCategoryType; 16 | 17 | /// Wave sound data archive 18 | typedef struct { 19 | PLSR_Archive ar; 20 | 21 | PLSR_ArchiveHeaderInfo headerInfo; 22 | 23 | PLSR_ArchiveSection infoSection; 24 | 25 | PLSR_ArchiveList waveIdList; 26 | PLSR_ArchiveTable soundDataTable; 27 | } PLSR_BFWSD; 28 | 29 | /// @copydoc plsrArchiveOpen 30 | PLSR_RC plsrBFWSDOpen(const char* path, PLSR_BFWSD* out); 31 | 32 | /// @copydoc plsrArchiveOpenInside 33 | PLSR_RC plsrBFWSDOpenInside(const PLSR_Archive* ar, u32 offset, PLSR_BFWSD* out); 34 | 35 | /// @copydoc plsrArchiveClose 36 | void plsrBFWSDClose(PLSR_BFWSD* bfwsd); 37 | -------------------------------------------------------------------------------- /include/pulsar/bfwsd/bfwsd_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // @see https://github.com/Kinnay/Nintendo-File-Formats/wiki/BFWSD-File-Format 7 | // @see https://github.com/Gota7/Citric-Composer/blob/master/Citric%20Composer/Citric%20Composer/High%20Level/Wave%20Sound%20Data/WaveSoundData.cs 8 | 9 | #define _PLSR_BFWSD_MAGIC "FWSD" 10 | #define _PLSR_BFWSD_INFO_MAGIC "INFO" 11 | 12 | #define _PLSR_BFWSD_SECTION_IDENTIFIER_INFO 0x6800 13 | 14 | #define _PLSR_BFWSD_INFO_IDENTIFIER_WAVE_ID_TABLE 0x0100 15 | #define _PLSR_BFWSD_INFO_IDENTIFIER_SOUND_DATA_TABLE 0x0101 16 | #define _PLSR_BFWSD_INFO_IDENTIFIER_NOTE_TABLE 0x0101 17 | 18 | #define _PLSR_BFWSD_INFO_IDENTIFIER_SOUND_DATA_ENTRY 0x4900 19 | #define _PLSR_BFWSD_INFO_IDENTIFIER_SOUND_INFO_ENTRY 0x4901 20 | #define _PLSR_BFWSD_INFO_IDENTIFIER_NOTE_ENTRY 0x4902 21 | #define _PLSR_BFWSD_INFO_IDENTIFIER_ADSHR_ENTRY 0x0000 22 | 23 | #define _PLSR_BFWSD_FLAG_SOUND_INFO_PAN BIT(0) 24 | #define _PLSR_BFWSD_FLAG_SOUND_INFO_PITCH BIT(1) 25 | #define _PLSR_BFWSD_FLAG_SOUND_INFO_SEND BIT(8) 26 | #define _PLSR_BFWSD_FLAG_SOUND_INFO_ADSHR BIT(9) 27 | 28 | typedef struct { 29 | u32 archiveItemId; 30 | u32 index; 31 | } _PLSR_BFWSDWaveId; 32 | 33 | typedef struct { 34 | _PLSR_ArchiveRef soundInfoRef; 35 | _PLSR_ArchiveRef trackInfoRef; 36 | _PLSR_ArchiveRef noteInfoTableRef; 37 | } _PLSR_BFWSDSoundDataEntry; 38 | 39 | typedef struct { 40 | u32 flags; 41 | } _PLSR_BFWSDSoundInfoEntry; 42 | 43 | typedef struct { 44 | u32 waveIdIndex; 45 | u32 flags; 46 | } _PLSR_BFWSDNoteEntry; 47 | -------------------------------------------------------------------------------- /include/pulsar/bfwsd/bfwsd_sound_data.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Wave sound data access 4 | */ 5 | #pragma once 6 | 7 | #include 8 | 9 | /// Wave sound data note information table 10 | typedef PLSR_ArchiveTable PLSR_BFWSDNoteInfoTable; 11 | 12 | /// Wave sound data ADSHR curve 13 | typedef struct { 14 | u8 attack; 15 | u8 decay; 16 | u8 sustain; 17 | u8 hold; 18 | u8 release; 19 | } PLSR_BFWSDAdshrCurve; 20 | 21 | /// Wave sound data send values 22 | typedef struct { 23 | u8 main; 24 | u8 auxCount; 25 | u8 aux[3]; 26 | } PLSR_BFWSDSendValues; 27 | 28 | /// Wave sound data note information 29 | typedef struct { 30 | u32 waveIdIndex; ///< Related index in the wave id table 31 | } PLSR_BFWSDNoteInfo; 32 | 33 | /// Wave sound data information 34 | typedef struct { 35 | PLSR_BFWSDNoteInfoTable noteInfoTable; 36 | 37 | bool hasPan; 38 | u8 pan; ///< Populated if `hasPan` is `true` 39 | u8 surroundPan; ///< Populated if `hasPan` is `true` 40 | 41 | bool hasPitch; 42 | float pitch; ///< Populated if `hasPitch` is `true` 43 | 44 | bool hasSend; 45 | PLSR_BFWSDSendValues send; ///< Populated if `hasSend` is `true` 46 | 47 | bool hasAdshr; 48 | PLSR_BFWSDAdshrCurve adshr; ///< Populated if `hasAdshr` is `true` 49 | } PLSR_BFWSDSoundDataInfo; 50 | 51 | /// Fetch wave sound data information from the specified index in the table 52 | PLSR_RC plsrBFWSDSoundDataGet(const PLSR_BFWSD* bfwsd, u32 index, PLSR_BFWSDSoundDataInfo* out); 53 | 54 | /// Fetch note information from the specified index in the wave sound data note table 55 | PLSR_RC plsrBFWSDSoundDataNoteGet(const PLSR_BFWSD* bfwsd, const PLSR_BFWSDNoteInfoTable* table, u32 index, PLSR_BFWSDNoteInfo* out); 56 | 57 | NX_INLINE u32 plsrBFWSDSoundDataCount(const PLSR_BFWSD* bfwsd) { 58 | return bfwsd->soundDataTable.info.count; 59 | } 60 | -------------------------------------------------------------------------------- /include/pulsar/bfwsd/bfwsd_wave_id.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Wave sound data wave id table access 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | /// Wave sound data wave id 11 | typedef struct { 12 | PLSR_BFSARItemId archiveItemId; 13 | u32 index; 14 | } PLSR_BFWSDWaveId; 15 | 16 | /// @copydoc plsrArchiveListReadEntries 17 | PLSR_RC plsrBFWSDWaveIdListReadEntries(const PLSR_BFWSD* bfwsd, u32 startIndex, u32 count, PLSR_BFWSDWaveId* outIds, u32* outReadCount); 18 | 19 | /// @copydoc plsrArchiveListGetEntry 20 | PLSR_RC plsrBFWSDWaveIdListGetEntry(const PLSR_BFWSD* bfwsd, u32 index, PLSR_BFWSDWaveId* out); 21 | 22 | NX_INLINE u32 plsrBFWSDWaveIdCount(const PLSR_BFWSD* bfwsd) { 23 | return bfwsd->waveIdList.info.count; 24 | } 25 | -------------------------------------------------------------------------------- /include/pulsar/player/player.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Player init 4 | */ 5 | #pragma once 6 | 7 | #include 8 | 9 | #define PLSR_PLAYER_MAX_CHANNELS 2 10 | #define PLSR_PLAYER_INVALID_SOUND NULL 11 | 12 | /// Player method categories 13 | typedef enum { 14 | PLSR_PlayerCategoryType_Init = 0, 15 | PLSR_PlayerCategoryType_Load, 16 | PLSR_PlayerCategoryType_LoadFormats, 17 | PLSR_PlayerCategoryType_LoadLookup, 18 | } PLSR_PlayerCategoryType; 19 | 20 | /// Player config 21 | typedef struct { 22 | bool initRenderer; ///< `true` if audren service init should be handled by the player 23 | 24 | AudioRendererConfig audrenConfig; ///< Audio renderer config (output rate, voices count, ...) 25 | int startVoiceId; ///< First renderer voice index to be used by the player (should be `>= 0 && < audrenConfig.num_voices`) 26 | int endVoiceId; ///< Last renderer voice index to be used by the player (should be `>= 0 && <= startVoiceId`) 27 | const u8 sinkChannels[PLSR_PLAYER_MAX_CHANNELS]; ///< Sink channels the player should use 28 | } PLSR_PlayerConfig; 29 | 30 | /// Player sound channel 31 | typedef struct { 32 | void* mempool; ///< Pointer to the aligned memory containing audio samples (and ADPCM parameters when applicable) 33 | AudioDriverWaveBuf wavebufs[2]; ///< Audio driver audio buffer struct (0 = intro/main; 1 = loop if looping) 34 | int mempoolId; ///< Audio driver assigned mempool index 35 | int voiceId; ///< Audio driver assigned voice index 36 | } PLSR_PlayerSoundChannel; 37 | 38 | /// Player sound 39 | typedef struct { 40 | unsigned int wavebufCount; 41 | unsigned int channelCount; 42 | PLSR_PlayerSoundChannel channels[PLSR_PLAYER_MAX_CHANNELS]; 43 | } PLSR_PlayerSound; 44 | 45 | /// Player sound ID 46 | typedef const PLSR_PlayerSound* PLSR_PlayerSoundId; 47 | 48 | /// Player 49 | typedef struct { 50 | AudioDriver driver; ///< Audio driver internal state 51 | PLSR_PlayerConfig config; ///< Effective player configuration 52 | } PLSR_Player; 53 | 54 | /// Get default player configuration 55 | const PLSR_PlayerConfig* plsrPlayerGetDefaultConfig(void); 56 | 57 | /// Get player instance (NULL if not initialized) 58 | PLSR_Player* plsrPlayerGetInstance(void); 59 | 60 | /// Initialize player with a custom configuration 61 | PLSR_RC plsrPlayerInitEx(const PLSR_PlayerConfig* config); 62 | 63 | /// Initialize player with the default configuration 64 | NX_INLINE PLSR_RC plsrPlayerInit(void) { 65 | return plsrPlayerInitEx(plsrPlayerGetDefaultConfig()); 66 | } 67 | 68 | /// Convenience shortcut to wait until next audio renderer frame 69 | NX_INLINE void plsrPlayerWaitNextFrame(void) { 70 | audrenWaitFrame(); 71 | } 72 | 73 | /// De-initialize player 74 | void plsrPlayerExit(void); 75 | 76 | /// Play a loaded sound from the beginning 77 | PLSR_RC plsrPlayerPlay(PLSR_PlayerSoundId id); 78 | 79 | /// Stop a sound if it is currently playing 80 | PLSR_RC plsrPlayerStop(PLSR_PlayerSoundId id); 81 | 82 | /// Free ressources used by a loaded sound 83 | void plsrPlayerFree(PLSR_PlayerSoundId id); 84 | 85 | /// Set sound pitch factor (effective next time it's played) 86 | PLSR_RC plsrPlayerSetPitch(PLSR_PlayerSoundId id, float pitch); 87 | 88 | /// Set sound volume factor (effective next time it's played) 89 | PLSR_RC plsrPlayerSetVolume(PLSR_PlayerSoundId id, float volume); 90 | -------------------------------------------------------------------------------- /include/pulsar/player/player_load.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Player sound loading 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | /// Player sound load layout 12 | typedef enum { 13 | PLSR_PlayerSoundLoadLayout_Channel = 0, ///< One offset per channel where is stored one contiguous chunk of data 14 | PLSR_PlayerSoundLoadLayout_Blocks, ///< One offset where is stored fixed-size blocks in channel order until the end is reached 15 | } PLSR_PlayerSoundLoadLayout; 16 | 17 | /// Player sound load channel layout specific information 18 | typedef struct { 19 | u32 offsets[PLSR_PLAYER_MAX_CHANNELS]; ///< Offset for each channel where data begins 20 | } PLSR_PlayerSoundLoadChannelLayoutInfo; 21 | 22 | /// Player sound load blocks layout specific information 23 | typedef struct { 24 | u32 firstBlockOffset; ///< Offset where the data of first block of the first channel begins 25 | u32 blockSize; 26 | u32 lastBlockPadding; 27 | } PLSR_PlayerSoundLoadBlocksLayoutInfo; 28 | 29 | /// Player sound load layout information 30 | typedef struct { 31 | PLSR_PlayerSoundLoadLayout type; 32 | 33 | union { 34 | PLSR_PlayerSoundLoadChannelLayoutInfo channel; 35 | PLSR_PlayerSoundLoadBlocksLayoutInfo blocks; 36 | }; 37 | } PLSR_PlayerSoundLoadLayoutInfo; 38 | 39 | typedef struct { 40 | AudioRendererAdpcmContext context; 41 | AudioRendererAdpcmParameters parameters; 42 | } PLSR_PlayerSoundAdpcmChannelInfo; 43 | 44 | /// Player sound load information 45 | typedef struct { 46 | const PLSR_Archive* ar; ///< Archive file to read from 47 | PLSR_PlayerSoundLoadLayoutInfo layout; 48 | 49 | PcmFormat pcmFormat; 50 | bool looping; 51 | unsigned int sampleRate; 52 | unsigned int sampleCount; 53 | unsigned int loopStartSample; ///< Loop starting sample (if looping is true) 54 | unsigned int channelCount; 55 | size_t dataSize; ///< total data size of one channel (if 0 this will be computed according to format and sample count) 56 | 57 | PLSR_PlayerSoundAdpcmChannelInfo adpcm[PLSR_PLAYER_MAX_CHANNELS]; ///< Populated if pcmFormat is PcmFormat_Adpcm 58 | } PLSR_PlayerSoundLoadInfo; 59 | 60 | /// Load a sound from a normalized sound load information 61 | PLSR_RC plsrPlayerLoad(const PLSR_PlayerSoundLoadInfo* loadInfo, PLSR_PlayerSoundId* out); 62 | -------------------------------------------------------------------------------- /include/pulsar/player/player_load_formats.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Player sound archive formats support 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | /// Load a sound from a Wave file 12 | PLSR_RC plsrPlayerLoadWave(const PLSR_BFWAV* bfwav, PLSR_PlayerSoundId* out); 13 | 14 | /// Load a sound from a Stream file 15 | PLSR_RC plsrPlayerLoadStream(const PLSR_BFSTM* bfstm, PLSR_PlayerSoundId* out); 16 | -------------------------------------------------------------------------------- /include/pulsar/player/player_load_lookup.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Player sound archive lookup and loading 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | /// Search sound archive string table for a sound with the specified name and load it 12 | PLSR_RC plsrPlayerLoadSoundByName(const PLSR_BFSAR* bfsar, const char* name, PLSR_PlayerSoundId* out); 13 | 14 | /// Find and load a sound from a sound archive with the specified item ID 15 | PLSR_RC plsrPlayerLoadSoundByItemId(const PLSR_BFSAR* bfsar, PLSR_BFSARItemId itemId, PLSR_PlayerSoundId* out); 16 | -------------------------------------------------------------------------------- /include/pulsar/types.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Base types 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /// Shortcut to get a result type enum from its name 15 | #define PLSR_RC_MAKE_RESULT_TYPE(RESULT_TYPE) (PLSR_ResultType_ ## RESULT_TYPE) 16 | /// Shortcut to get a category enum from its name 17 | #define PLSR_RC_MAKE_CATEGORY_TYPE(ARCHIVE_TYPE, CATEGORY_TYPE) (PLSR_ ## ARCHIVE_TYPE ## CategoryType_ ## CATEGORY_TYPE) 18 | /// Shortcut to get an archive enum from its name 19 | #define PLSR_RC_MAKE_ARCHIVE_TYPE(ARCHIVE_TYPE) (PLSR_ArchiveType_ ## ARCHIVE_TYPE) 20 | 21 | /// Make a result code from raw integers 22 | #define PLSR_RC_MAKE_RAW(ARCHIVE, CATEGORY, RESULT) (RESULT + (CATEGORY << 8) + (ARCHIVE << 16)) 23 | 24 | /// Make a result code from archive, category and result type names 25 | #define PLSR_RC_MAKE(ARCHIVE_TYPE, CATEGORY_TYPE, RESULT_TYPE) PLSR_RC_MAKE_RAW( \ 26 | PLSR_RC_MAKE_ARCHIVE_TYPE(ARCHIVE_TYPE), \ 27 | PLSR_RC_MAKE_CATEGORY_TYPE(ARCHIVE_TYPE, CATEGORY_TYPE), \ 28 | PLSR_RC_MAKE_RESULT_TYPE(RESULT_TYPE) \ 29 | ) 30 | 31 | /// Result code returned on success 32 | #define PLSR_RC_OK 0 33 | /// Test that the result code means failure 34 | #define PLSR_RC_FAILED(RESULT) (RESULT != PLSR_RC_OK) 35 | /// Test that the result code means success 36 | #define PLSR_RC_SUCCEEDED(RESULT) (RESULT == PLSR_RC_OK) 37 | 38 | /// Extract the archive part of a result code 39 | #define PLSR_RC_ARCHIVE(RESULT) ((RESULT >> 16) & 0xFF) 40 | /// Extract the category part of a result code 41 | #define PLSR_RC_CATEGORY(RESULT) ((RESULT >> 8) & 0xFF) 42 | /// Extract the result type part of a result code 43 | #define PLSR_RC_RESULT(RESULT) (RESULT & 0xFF) 44 | 45 | /// Convert a result code to another archive and category, keeping the result type 46 | /** @note This is mainly meant to convert errors from generic archive read calls into the callee archive and category type */ 47 | #define PLSR_RC_CONVERT(RESULT, NEW_ARCHIVE_TYPE, NEW_CATEGORY_TYPE) PLSR_RC_MAKE_RAW( \ 48 | PLSR_RC_MAKE_ARCHIVE_TYPE(NEW_ARCHIVE_TYPE), \ 49 | PLSR_RC_MAKE_CATEGORY_TYPE(NEW_ARCHIVE_TYPE, NEW_CATEGORY_TYPE), \ 50 | PLSR_RC_RESULT(RESULT) \ 51 | ) 52 | 53 | /// Return result code if failed 54 | #define PLSR_RC_TRY(X) do { \ 55 | const PLSR_RC _rc = (X); \ 56 | if(PLSR_RC_FAILED(_rc)) { return _rc; } \ 57 | } while(false) 58 | 59 | /// Return result code if failed, also converts result to a new archive and category 60 | /** @note This is mainly meant to convert errors from generic archive read calls into the callee archive and category type */ 61 | #define PLSR_RC_LTRY(ARCHIVE_TYPE, CATEGORY_TYPE, X) do { \ 62 | const PLSR_RC _rc = (X); \ 63 | if(PLSR_RC_FAILED(_rc)) { return PLSR_RC_CONVERT(_rc, ARCHIVE_TYPE, CATEGORY_TYPE); } \ 64 | } while(false) 65 | 66 | /// Return a System type result code if the libnx result failed 67 | /** @note This is mainly used in the player when calling the audio renderer */ 68 | #define PLSR_RC_NX_LTRY(ARCHIVE_TYPE, CATEGORY_TYPE, NX_RESULT) do { \ 69 | if(R_FAILED(NX_RESULT)) { return PLSR_RC_MAKE(ARCHIVE_TYPE, CATEGORY_TYPE, System); } \ 70 | } while(false) 71 | 72 | /// Result code returned by Pulsar functions 73 | typedef u32 PLSR_RC; 74 | 75 | /// Result code types 76 | typedef enum { 77 | PLSR_ResultType_OK = 0, 78 | 79 | PLSR_ResultType_FileRead, ///< File could not be opened or read 80 | PLSR_ResultType_BadMagic, ///< Magic mismatch 81 | PLSR_ResultType_BadEndianness, ///< Archive endianness did not match host endianness 82 | PLSR_ResultType_BadInput, ///< Function was called with improper arguments 83 | PLSR_ResultType_NotFound, ///< Requested data could not be retrieved 84 | PLSR_ResultType_NotReady, ///< Function was called before required initialization 85 | PLSR_ResultType_Unsupported, ///< Execution encountered an unexpected case 86 | PLSR_ResultType_Memory, ///< Memory allocation failed 87 | 88 | PLSR_ResultType_System = 0xFF ///< Can indicate an underlying libnx fn call failure 89 | } PLSR_ResultType; 90 | 91 | /// Supported archive types 92 | typedef enum { 93 | PLSR_ArchiveType_Unknown = 0, 94 | 95 | PLSR_ArchiveType_BFSAR, ///< Sound archive 96 | PLSR_ArchiveType_BFGRP, ///< Sound group 97 | PLSR_ArchiveType_BFWSD, ///< Wave sound data 98 | PLSR_ArchiveType_BFWAR, ///< Wave archive 99 | PLSR_ArchiveType_BFWAV, ///< Wave file 100 | PLSR_ArchiveType_BFSTM, ///< Stream file 101 | 102 | PLSR_ArchiveType_Player = 0xFF, ///< Not an archive, type used by Player functions 103 | } PLSR_ArchiveType; 104 | -------------------------------------------------------------------------------- /src/archive/archive.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | PLSR_RC plsrArchiveOpenEx(const char* path, PLSR_Archive* out, bool storePath) { 4 | PLSR_ArchiveFileHandle handle = plsrArchiveFileOpen(path, storePath); 5 | if(handle == PLSR_INVALID_ARCHIVE_FILE_HANDLE) { 6 | return PLSR_ResultType_FileRead; 7 | } 8 | 9 | out->handle = handle; 10 | out->offset = 0; 11 | 12 | return PLSR_RC_OK; 13 | } 14 | 15 | PLSR_RC plsrArchiveOpenInside(const PLSR_Archive* ar, u32 offset, PLSR_Archive* out) { 16 | if(ar->handle == PLSR_INVALID_ARCHIVE_FILE_HANDLE) { 17 | return PLSR_ResultType_FileRead; 18 | } 19 | 20 | out->handle = plsrArchiveFileCloneHandle(ar->handle); 21 | out->offset = ar->offset + offset; 22 | 23 | return PLSR_RC_OK; 24 | } 25 | 26 | void plsrArchiveClose(PLSR_Archive* ar) { 27 | if(ar->handle) { 28 | plsrArchiveFileClose(ar->handle); 29 | } 30 | ar->handle = PLSR_INVALID_ARCHIVE_FILE_HANDLE; 31 | } 32 | 33 | PLSR_RC plsrArchiveReadAtEx(const PLSR_Archive* ar, u32 offset, void* out, size_t size, bool acceptZero) { 34 | if( 35 | (offset == 0 && !acceptZero) 36 | || !plsrArchiveFileSetPosition(ar->handle, ar->offset + offset) 37 | || !plsrArchiveFileRead(ar->handle, out, size) 38 | ) { 39 | return PLSR_ResultType_FileRead; 40 | } 41 | 42 | return PLSR_RC_OK; 43 | } 44 | 45 | PLSR_RC plsrArchiveReadString(const PLSR_Archive* ar, u32 offset, char* out, size_t size) { 46 | if( 47 | offset == 0 48 | || !plsrArchiveFileSetPosition(ar->handle, ar->offset + offset) 49 | || !plsrArchiveFileReadString(ar->handle, out, size) 50 | ) { 51 | return PLSR_ResultType_FileRead; 52 | } 53 | 54 | out[size-1] = '\0'; 55 | return PLSR_RC_OK; 56 | } 57 | 58 | PLSR_RC plsrArchiveReadHeaderInfo(const PLSR_Archive* ar, const char* magic, PLSR_ArchiveHeaderInfo* out) { 59 | _PLSR_ArchiveHeader _header; 60 | 61 | PLSR_RC_TRY(plsrArchiveReadAtEx(ar, 0, &_header, sizeof(_header), true)); 62 | 63 | if(memcmp(magic, &_header.magic, sizeof(_header.magic)) != 0) { 64 | return PLSR_ResultType_BadMagic; 65 | } 66 | 67 | if(_header.endianness != _PLSR_ARCHIVE_ENDIANNESS_LITLE) { 68 | return PLSR_ResultType_BadEndianness; 69 | } 70 | 71 | out->version.raw = _header.version; 72 | out->fileSize = _header.fileSize; 73 | out->sectionCount = _header.sectionCount; 74 | 75 | return PLSR_RC_OK; 76 | } 77 | 78 | PLSR_RC plsrArchiveReadSectionHeaderInfo(const PLSR_Archive* ar, u32 offset, const char* magic, PLSR_ArchiveSectionInfo* out) { 79 | _PLSR_ArchiveSectionHeader _header; 80 | 81 | PLSR_RC_TRY(plsrArchiveReadAt(ar, offset, &_header, sizeof(_header))); 82 | 83 | if(memcmp(magic, &_header.magic, sizeof(_header.magic)) != 0) { 84 | return PLSR_ResultType_BadMagic; 85 | } 86 | 87 | out->blockSize = _header.size; 88 | 89 | return PLSR_RC_OK; 90 | } 91 | 92 | PLSR_RC plsrArchiveReadTableHeaderInfo(const PLSR_Archive* ar, u32 offset, PLSR_ArchiveTableInfo* out) { 93 | _PLSR_ArchiveTableHeader _header; 94 | 95 | PLSR_RC_TRY(plsrArchiveReadAt(ar, offset, &_header, sizeof(_header))); 96 | 97 | out->count = _header.count; 98 | 99 | return PLSR_RC_OK; 100 | } 101 | 102 | PLSR_RC plsrArchiveReadTableEntry(const PLSR_Archive* ar, const PLSR_ArchiveTable* table, u16 id, u32 index, PLSR_ArchiveTableEntry* out) { 103 | if(table->offset == 0 || index >= table->info.count) { 104 | return PLSR_ResultType_NotFound; 105 | } 106 | 107 | _PLSR_ArchiveRef _ref; 108 | PLSR_RC_TRY(plsrArchiveReadAt(ar, table->offset + sizeof(_PLSR_ArchiveTableHeader) + index * sizeof(_ref), &_ref, sizeof(_ref))); 109 | 110 | if(_ref.id != id) { 111 | return PLSR_ResultType_NotFound; 112 | } 113 | 114 | out->offset = table->offset + _ref.offset; 115 | return PLSR_RC_OK; 116 | } 117 | 118 | PLSR_RC plsrArchiveReadTableBlock(const PLSR_Archive* ar, const PLSR_ArchiveTable* table, u16 id, u32 index, PLSR_ArchiveTableBlock* out) { 119 | if(table->offset == 0 || index >= table->info.count) { 120 | return PLSR_ResultType_NotFound; 121 | } 122 | 123 | _PLSR_ArchiveBlockRef _ref; 124 | PLSR_RC_TRY(plsrArchiveReadAt(ar, table->offset + sizeof(_PLSR_ArchiveTableHeader) + index * sizeof(_ref), &_ref, sizeof(_ref))); 125 | 126 | if(_ref.id != id) { 127 | return PLSR_ResultType_NotFound; 128 | } 129 | 130 | out->offset = table->offset + _ref.offset; 131 | out->size = _ref.size; 132 | 133 | return PLSR_RC_OK; 134 | } 135 | 136 | PLSR_RC plsrArchiveListReadEntries(const PLSR_Archive* ar, const PLSR_ArchiveList* list, u32 startIndex, u32 count, void* outEntries, u32* outReadCount) { 137 | u32 toRead = 0; 138 | if(startIndex < list->info.count) { 139 | u32 maxCount = list->info.count - startIndex; 140 | toRead = maxCount < count ? maxCount : count; 141 | } 142 | 143 | if(toRead > 0) { 144 | PLSR_RC_TRY(plsrArchiveReadAt(ar, list->offset + startIndex * list->info.entrySize, outEntries, toRead * list->info.entrySize)); 145 | } 146 | 147 | if(outReadCount != NULL) { 148 | *outReadCount = toRead; 149 | } 150 | 151 | return PLSR_RC_OK; 152 | } 153 | 154 | PLSR_RC plsrArchiveListGetEntry(const PLSR_Archive* ar, const PLSR_ArchiveList* list, u32 index, void* out) { 155 | u32 read; 156 | PLSR_RC_TRY(plsrArchiveListReadEntries(ar, list, index, 1, out, &read)); 157 | 158 | if(read == 0) { 159 | return PLSR_ResultType_NotFound; 160 | } 161 | 162 | return PLSR_RC_OK; 163 | } 164 | -------------------------------------------------------------------------------- /src/archive/archive_file.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __SWITCH__ 4 | // Path concatenation borrowed from newlib 5 | int _concatenate_path(int* errno, char *path, const char *extra, int maxLength); 6 | #endif 7 | 8 | static bool _concatPath(char* base, const char* dest, size_t size) { 9 | if(base == NULL || size == 0 || dest == NULL) { 10 | return false; 11 | } 12 | 13 | #ifdef __SWITCH__ 14 | // If the dest path contains a device name 15 | // copy it to the output first, and concat it with the rest of the path 16 | const char* destDevice = strchr(dest, ':'); 17 | if(destDevice != NULL) { 18 | destDevice++; 19 | if(*destDevice != '/' || destDevice - dest >= size) { 20 | return false; 21 | } 22 | 23 | size_t deviceRootLen = destDevice - dest + 1; 24 | if(deviceRootLen >= size) { 25 | return false; 26 | } 27 | 28 | strncpy(base, dest, deviceRootLen); 29 | base[deviceRootLen] = '\0'; 30 | dest = destDevice; 31 | } else if (*dest == '/' && size >= 2) { 32 | // Root path on the switch can be on a different device than the CWD 33 | // avoid it appending the wrong device by removing it in the base 34 | strcpy(base, "/"); 35 | } 36 | 37 | int localErrno; 38 | return _concatenate_path(&localErrno, base, dest, size) == 0; 39 | #else 40 | const char* resolved; 41 | if(*dest == '/') { 42 | resolved = realpath(dest, NULL); 43 | } else { 44 | size_t baseLen = strlen(base); 45 | if(baseLen >= size) { 46 | return false; 47 | } 48 | 49 | snprintf(base+baseLen, size-baseLen, "/%s", dest); 50 | resolved = realpath(base, NULL); 51 | } 52 | 53 | if(resolved == NULL) { 54 | return false; 55 | } 56 | 57 | base[size-1] = '\0'; 58 | strncpy(base, resolved, size); 59 | free(resolved); 60 | return base[size-1] == '\0'; 61 | #endif 62 | } 63 | 64 | static char* _getContextPath(const char* path) { 65 | char* context = (char*)malloc(FS_MAX_PATH); 66 | 67 | if(context == NULL) { 68 | return NULL; 69 | } 70 | 71 | if( 72 | getcwd(context, FS_MAX_PATH) == NULL 73 | || !_concatPath(context, path, FS_MAX_PATH) 74 | || !_concatPath(context, "..", FS_MAX_PATH) 75 | ) { 76 | free(context); 77 | return NULL; 78 | } 79 | 80 | return context; 81 | } 82 | 83 | PLSR_ArchiveFileHandle plsrArchiveFileOpen(const char* path, bool storePath) { 84 | FILE* f = fopen(path, "r"); 85 | if(f == NULL) { 86 | return PLSR_INVALID_ARCHIVE_FILE_HANDLE; 87 | } 88 | 89 | PLSR_ArchiveSharedReader* reader = (PLSR_ArchiveSharedReader*)malloc(sizeof(PLSR_ArchiveSharedReader)); 90 | 91 | if(reader == NULL) { 92 | fclose(f); 93 | return PLSR_INVALID_ARCHIVE_FILE_HANDLE; 94 | } 95 | 96 | reader->context = storePath ? _getContextPath(path) : NULL; 97 | reader->f = f; 98 | reader->refs = 1; 99 | 100 | return reader; 101 | } 102 | 103 | void plsrArchiveFileClose(PLSR_ArchiveFileHandle handle) { 104 | if(!handle) { 105 | return; 106 | } 107 | 108 | PLSR_ArchiveSharedReader* reader = (PLSR_ArchiveSharedReader*)handle; 109 | reader->refs--; 110 | if(reader->refs == 0) { 111 | fclose(reader->f); 112 | 113 | if(reader->context != NULL) { 114 | free(reader->context); 115 | } 116 | 117 | free(reader); 118 | } 119 | } 120 | 121 | PLSR_ArchiveFileHandle plsrArchiveFileCloneHandle(PLSR_ArchiveFileHandle handle) { 122 | if(handle) { 123 | PLSR_ArchiveSharedReader* reader = (PLSR_ArchiveSharedReader*)handle; 124 | reader->refs++; 125 | } 126 | 127 | return handle; 128 | } 129 | 130 | bool plsrArchiveFileRelativePath(PLSR_ArchiveFileHandle handle, const char* path, char* out, size_t size) { 131 | if(!handle || out == NULL || size == 0) { 132 | return false; 133 | } 134 | 135 | PLSR_ArchiveSharedReader* reader = (PLSR_ArchiveSharedReader*)handle; 136 | 137 | strncpy(out, reader->context, size); 138 | out[size-1] = '\0'; 139 | 140 | return _concatPath(out, path, size); 141 | } 142 | -------------------------------------------------------------------------------- /src/bfgrp/bfgrp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFGRP, Init, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFGRP, Init, X) 5 | 6 | static PLSR_RC _BFGRPInit(PLSR_BFGRP* bfgrp) { 7 | _LOCAL_TRY(plsrArchiveReadHeaderInfo(&bfgrp->ar, _PLSR_BFGRP_MAGIC, &bfgrp->headerInfo)); 8 | 9 | _PLSR_ArchiveBlockRef _ref; 10 | unsigned identifierCount = 2; 11 | for(unsigned i = 0; i < bfgrp->headerInfo.sectionCount && identifierCount > 0; i++) { 12 | _LOCAL_TRY(plsrArchiveRead(&bfgrp->ar, &_ref, sizeof(_ref))); 13 | 14 | switch(_ref.id) { 15 | case _PLSR_BFGRP_SECTION_IDENTIFIER_INFO: 16 | bfgrp->infoSection.offset = _ref.offset; 17 | identifierCount--; 18 | break; 19 | case _PLSR_BFGRP_SECTION_IDENTIFIER_FILE: 20 | bfgrp->fileSection.offset = _ref.offset; 21 | identifierCount--; 22 | break; 23 | } 24 | } 25 | 26 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfgrp->ar, bfgrp->infoSection.offset, _PLSR_BFGRP_INFO_MAGIC, &bfgrp->infoSection.info)); 27 | 28 | bfgrp->locationTable.offset = bfgrp->infoSection.offset + sizeof(_PLSR_ArchiveSectionHeader); 29 | _LOCAL_TRY(plsrArchiveReadTableHeaderInfo(&bfgrp->ar, bfgrp->locationTable.offset, &bfgrp->locationTable.info)); 30 | 31 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfgrp->ar, bfgrp->fileSection.offset, _PLSR_BFGRP_FILE_MAGIC, &bfgrp->fileSection.info)); 32 | 33 | return PLSR_RC_OK; 34 | } 35 | 36 | PLSR_RC plsrBFGRPOpen(const char* path, PLSR_BFGRP* out) { 37 | memset(out, 0, sizeof(PLSR_BFGRP)); 38 | 39 | PLSR_RC rc = plsrArchiveOpen(path, &out->ar); 40 | 41 | if(PLSR_RC_SUCCEEDED(rc)) { 42 | rc = _BFGRPInit(out); 43 | } 44 | 45 | if(PLSR_RC_FAILED(rc)) { 46 | plsrBFGRPClose(out); 47 | } 48 | 49 | return rc; 50 | } 51 | 52 | PLSR_RC plsrBFGRPOpenInside(const PLSR_Archive* ar, u32 offset, PLSR_BFGRP* out) { 53 | memset(out, 0, sizeof(PLSR_BFGRP)); 54 | 55 | PLSR_RC rc = plsrArchiveOpenInside(ar, offset, &out->ar); 56 | 57 | if(PLSR_RC_SUCCEEDED(rc)) { 58 | rc = _BFGRPInit(out); 59 | } 60 | 61 | if(PLSR_RC_FAILED(rc)) { 62 | plsrBFGRPClose(out); 63 | } 64 | 65 | return rc; 66 | } 67 | 68 | void plsrBFGRPClose(PLSR_BFGRP* bfgrp) { 69 | plsrArchiveClose(&bfgrp->ar); 70 | } 71 | -------------------------------------------------------------------------------- /src/bfgrp/bfgrp_location.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFGRP, Location, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFGRP, Location, X) 5 | 6 | static PLSR_RC _BFGRPLocationRead(const PLSR_BFGRP* bfgrp, u32 offset, const _PLSR_BFGRPLocationEntry* _locationEntry, PLSR_BFGRPLocationInfo* out) { 7 | out->fileIndex = _locationEntry->fileIndex; 8 | out->fileSize = _locationEntry->fileSize; 9 | 10 | if(_locationEntry->fileBlockRef.id != _PLSR_BFGRP_FILE_IDENTIFIER_BLOCK_ENTRY) { 11 | return _LOCAL_RC_MAKE(NotFound); 12 | } 13 | 14 | out->fileBlockOffset = bfgrp->fileSection.offset + sizeof(_PLSR_ArchiveSectionHeader) + _locationEntry->fileBlockRef.offset; 15 | 16 | return PLSR_RC_OK; 17 | } 18 | 19 | PLSR_RC plsrBFGRPLocationGet(const PLSR_BFGRP* bfgrp, u32 index, PLSR_BFGRPLocationInfo* out) { 20 | PLSR_ArchiveTableEntry tableEntry; 21 | 22 | _LOCAL_TRY(plsrArchiveReadTableEntry(&bfgrp->ar, &bfgrp->locationTable, _PLSR_BFGRP_INFO_IDENTIFIER_LOCATION_ENTRY, index, &tableEntry)); 23 | 24 | _PLSR_BFGRPLocationEntry _locationEntry; 25 | _LOCAL_TRY(plsrArchiveReadAt(&bfgrp->ar, tableEntry.offset, &_locationEntry, sizeof(_locationEntry))); 26 | 27 | return _BFGRPLocationRead(bfgrp, tableEntry.offset, &_locationEntry, out); 28 | } 29 | 30 | PLSR_RC plsrBFGRPLocationFindByFileIndex(const PLSR_BFGRP* bfgrp, u32 fileIndex, PLSR_BFGRPLocationInfo* out) { 31 | PLSR_ArchiveTableEntry tableEntry; 32 | 33 | for(u32 i = 0; i < bfgrp->locationTable.info.count; i++) { 34 | _LOCAL_TRY(plsrArchiveReadTableEntry(&bfgrp->ar, &bfgrp->locationTable, _PLSR_BFGRP_INFO_IDENTIFIER_LOCATION_ENTRY, i, &tableEntry)); 35 | 36 | _PLSR_BFGRPLocationEntry _locationEntry; 37 | _LOCAL_TRY(plsrArchiveReadAt(&bfgrp->ar, tableEntry.offset, &_locationEntry, sizeof(_locationEntry))); 38 | 39 | if(_locationEntry.fileIndex != fileIndex) { 40 | continue; 41 | } 42 | 43 | return _BFGRPLocationRead(bfgrp, tableEntry.offset, &_locationEntry, out); 44 | } 45 | 46 | return _LOCAL_RC_MAKE(NotFound); 47 | } 48 | -------------------------------------------------------------------------------- /src/bfsar/bfsar.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFSAR, Init, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFSAR, Init, X) 5 | 6 | static PLSR_RC _BFSARReadStringTreeHeaderInfo(PLSR_BFSAR* bfsar) { 7 | _PLSR_BFSARStringTreeHeader _header; 8 | _LOCAL_TRY(plsrArchiveReadAt(&bfsar->ar, bfsar->stringTree.offset, &_header, sizeof(_header))); 9 | 10 | bfsar->stringTree.info.rootNodeIndex = _header.rootNodeIndex; 11 | bfsar->stringTree.info.nodeCount = _header.nodeCount; 12 | 13 | return PLSR_RC_OK; 14 | } 15 | 16 | static PLSR_RC _BFSARReadHeaderStrg(PLSR_BFSAR* bfsar) { 17 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfsar->ar, bfsar->strgSection.offset, _PLSR_BFSAR_STRG_MAGIC, &bfsar->strgSection.info)); 18 | 19 | _PLSR_ArchiveRef _ref; 20 | unsigned identifierCount = 2; 21 | for(unsigned read = 0; (read < bfsar->strgSection.info.blockSize && identifierCount > 0); read += sizeof(_ref)) { 22 | _LOCAL_TRY(plsrArchiveRead(&bfsar->ar, &_ref, sizeof(_ref))); 23 | 24 | switch(_ref.id) { 25 | case _PLSR_BFSAR_STRG_IDENTIFIER_STRING_TABLE: 26 | bfsar->stringTable.offset = bfsar->strgSection.offset + sizeof(_PLSR_ArchiveSectionHeader) + _ref.offset; 27 | identifierCount--; 28 | break; 29 | case _PLSR_BFSAR_STRG_IDENTIFIER_STRING_TREE: 30 | bfsar->stringTree.offset = bfsar->strgSection.offset + sizeof(_PLSR_ArchiveSectionHeader) + _ref.offset; 31 | identifierCount--; 32 | break; 33 | } 34 | } 35 | 36 | _LOCAL_TRY(plsrArchiveReadTableHeaderInfo(&bfsar->ar, bfsar->stringTable.offset, &bfsar->stringTable.info)); 37 | _LOCAL_TRY(_BFSARReadStringTreeHeaderInfo(bfsar)); 38 | 39 | return PLSR_RC_OK; 40 | } 41 | 42 | static PLSR_RC _BFSARReadHeaderInfo(PLSR_BFSAR* bfsar) { 43 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfsar->ar, bfsar->infoSection.offset, _PLSR_BFSAR_INFO_MAGIC, &bfsar->infoSection.info)); 44 | 45 | _PLSR_ArchiveRef _ref; 46 | unsigned identifierCount = 5; 47 | for(unsigned read = 0; read < bfsar->infoSection.info.blockSize && identifierCount > 0; read += sizeof(_ref)) { 48 | _LOCAL_TRY(plsrArchiveRead(&bfsar->ar, &_ref, sizeof(_ref))); 49 | 50 | switch(_ref.id) { 51 | case _PLSR_BFSAR_INFO_IDENTIFIER_SOUND_TABLE: 52 | bfsar->soundTable.offset = bfsar->infoSection.offset + sizeof(_PLSR_ArchiveSectionHeader) + _ref.offset; 53 | identifierCount--; 54 | break; 55 | case _PLSR_BFSAR_INFO_IDENTIFIER_WAVE_ARCHIVE_TABLE: 56 | bfsar->waveArchiveTable.offset = bfsar->infoSection.offset + sizeof(_PLSR_ArchiveSectionHeader) + _ref.offset; 57 | identifierCount--; 58 | break; 59 | case _PLSR_BFSAR_INFO_IDENTIFIER_GROUP_TABLE: 60 | bfsar->groupTable.offset = bfsar->infoSection.offset + sizeof(_PLSR_ArchiveSectionHeader) + _ref.offset; 61 | identifierCount--; 62 | break; 63 | case _PLSR_BFSAR_INFO_IDENTIFIER_FILE_TABLE: 64 | bfsar->fileTable.offset = bfsar->infoSection.offset + sizeof(_PLSR_ArchiveSectionHeader) + _ref.offset; 65 | identifierCount--; 66 | break; 67 | } 68 | // UNIMPLEMENTED: Remaining resource tables (bank, player, sar player) 69 | } 70 | 71 | _LOCAL_TRY(plsrArchiveReadTableHeaderInfo(&bfsar->ar, bfsar->soundTable.offset, &bfsar->soundTable.info)); 72 | _LOCAL_TRY(plsrArchiveReadTableHeaderInfo(&bfsar->ar, bfsar->waveArchiveTable.offset, &bfsar->waveArchiveTable.info)); 73 | _LOCAL_TRY(plsrArchiveReadTableHeaderInfo(&bfsar->ar, bfsar->groupTable.offset, &bfsar->groupTable.info)); 74 | _LOCAL_TRY(plsrArchiveReadTableHeaderInfo(&bfsar->ar, bfsar->fileTable.offset, &bfsar->fileTable.info)); 75 | 76 | return PLSR_RC_OK; 77 | } 78 | 79 | static PLSR_RC _BFSARInit(PLSR_BFSAR* bfsar) { 80 | _LOCAL_TRY(plsrArchiveReadHeaderInfo(&bfsar->ar, _PLSR_BFSAR_MAGIC, &bfsar->headerInfo)); 81 | 82 | _PLSR_ArchiveBlockRef _ref; 83 | unsigned identifierCount = 3; 84 | for(unsigned i = 0; i < bfsar->headerInfo.sectionCount && identifierCount > 0; i++) { 85 | _LOCAL_TRY(plsrArchiveRead(&bfsar->ar, &_ref, sizeof(_ref))); 86 | 87 | switch(_ref.id) { 88 | case _PLSR_BFSAR_SECTION_IDENTIFIER_STRG: 89 | bfsar->strgSection.offset = _ref.offset; 90 | identifierCount--; 91 | break; 92 | case _PLSR_BFSAR_SECTION_IDENTIFIER_INFO: 93 | bfsar->infoSection.offset = _ref.offset; 94 | identifierCount--; 95 | break; 96 | case _PLSR_BFSAR_SECTION_IDENTIFIER_FILE: 97 | bfsar->fileSection.offset = _ref.offset; 98 | identifierCount--; 99 | break; 100 | } 101 | } 102 | 103 | PLSR_RC_TRY(_BFSARReadHeaderStrg(bfsar)); 104 | PLSR_RC_TRY(_BFSARReadHeaderInfo(bfsar)); 105 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfsar->ar, bfsar->fileSection.offset, _PLSR_BFSAR_FILE_MAGIC, &bfsar->fileSection.info)); 106 | 107 | return PLSR_RC_OK; 108 | } 109 | 110 | PLSR_RC plsrBFSAROpen(const char* path, PLSR_BFSAR* out) { 111 | memset(out, 0, sizeof(PLSR_BFSAR)); 112 | 113 | PLSR_RC rc = plsrArchiveOpenEx(path, &out->ar, true); 114 | 115 | if(PLSR_RC_SUCCEEDED(rc)) { 116 | rc = _BFSARInit(out); 117 | } 118 | 119 | if(PLSR_RC_FAILED(rc)) { 120 | plsrBFSARClose(out); 121 | } 122 | 123 | return rc; 124 | } 125 | 126 | void plsrBFSARClose(PLSR_BFSAR* bfwav) { 127 | plsrArchiveClose(&bfwav->ar); 128 | } 129 | -------------------------------------------------------------------------------- /src/bfsar/bfsar_file.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFSAR, File, X) 8 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFSAR, File, X) 9 | 10 | static PLSR_RC _BFSARFileReadInternal(const PLSR_BFSAR* bfsar, u32 offset, PLSR_BFSARFileInfoInternal* out) { 11 | _PLSR_ArchiveBlockRef _block; 12 | if(plsrArchiveReadAt(&bfsar->ar, offset, &_block, sizeof(_block))) { 13 | return _LOCAL_RC_MAKE(FileRead); 14 | } 15 | 16 | out->id = _block.id; 17 | out->offset = _block.offset; 18 | out->size = _block.size; 19 | out->valid = _block.id != 0xFFFF && _block.offset != 0xFFFFFFFF && _block.size != 0xFFFFFFFF; 20 | 21 | if(out->valid) { 22 | out->offset += bfsar->fileSection.offset + sizeof(_PLSR_ArchiveSectionHeader); 23 | } 24 | 25 | return PLSR_RC_OK; 26 | } 27 | 28 | static PLSR_RC _BFSARFileReadExternal(const PLSR_BFSAR* bfsar, u32 offset, PLSR_BFSARFileInfoExternal* out) { 29 | _LOCAL_TRY(plsrArchiveReadString(&bfsar->ar, offset, &out->path[0], sizeof(out->path))); 30 | 31 | return PLSR_RC_OK; 32 | } 33 | 34 | PLSR_RC plsrBFSARFileGet(const PLSR_BFSAR* bfsar, u32 index, PLSR_BFSARFileInfo* out) { 35 | PLSR_ArchiveTableEntry tableEntry; 36 | _LOCAL_TRY(plsrArchiveReadTableEntry(&bfsar->ar, &bfsar->fileTable, _PLSR_BFSAR_INFO_IDENTIFIER_FILE_ENTRY, index, &tableEntry)); 37 | 38 | _PLSR_ArchiveRef _ref; 39 | _LOCAL_TRY(plsrArchiveReadAt(&bfsar->ar, tableEntry.offset, &_ref, sizeof(_ref))); 40 | out->fromGroup = false; 41 | 42 | switch(_ref.id) { 43 | case _PLSR_BFSAR_FILE_IDENTIFIER_INTERNAL: 44 | out->type = PLSR_BFSARFileInfoType_Internal; 45 | return _BFSARFileReadInternal(bfsar, tableEntry.offset + _ref.offset, &out->internal); 46 | case _PLSR_BFSAR_FILE_IDENTIFIER_EXTERNAL: 47 | out->type = PLSR_BFSARFileInfoType_External; 48 | return _BFSARFileReadExternal(bfsar, tableEntry.offset + _ref.offset, &out->external); 49 | default: 50 | return _LOCAL_RC_MAKE(NotFound); 51 | } 52 | } 53 | 54 | PLSR_RC plsrBFSARFileScan(const PLSR_BFSAR* bfsar, u32 index, PLSR_BFSARFileInfo* out) { 55 | PLSR_RC_TRY(plsrBFSARFileGet(bfsar, index, out)); 56 | 57 | if(out->type != PLSR_BFSARFileInfoType_Internal || out->internal.valid) { 58 | return PLSR_RC_OK; 59 | } 60 | 61 | PLSR_BFSARGroupInfo groupInfo; 62 | PLSR_BFGRPLocationInfo locationInfo; 63 | PLSR_BFGRP bfgrp; 64 | for(u32 i = 0; i < plsrBFSARGroupCount(bfsar); i++) { 65 | PLSR_RC_TRY(plsrBFSARGroupGet(bfsar, i, &groupInfo)); 66 | PLSR_RC_TRY(plsrBFSARGroupOpen(bfsar, &groupInfo, &bfgrp)); 67 | 68 | PLSR_RC rc = plsrBFGRPLocationFindByFileIndex(&bfgrp, index, &locationInfo); 69 | plsrBFGRPClose(&bfgrp); 70 | 71 | if(PLSR_RC_RESULT(rc) == PLSR_ResultType_NotFound) { 72 | continue; 73 | } else if(PLSR_RC_FAILED(rc)) { 74 | return rc; 75 | } 76 | 77 | out->fromGroup = true; 78 | out->groupIndex = i; 79 | out->internal.offset = bfgrp.ar.offset + locationInfo.fileBlockOffset - bfsar->ar.offset; 80 | out->internal.size = locationInfo.fileSize; 81 | break; 82 | } 83 | 84 | return PLSR_RC_OK; 85 | } 86 | 87 | PLSR_RC plsrBFSARFileInfoNormalize(const PLSR_BFSAR* bfsar, PLSR_BFSARFileInfo* soundFileInfo) { 88 | if(soundFileInfo->type != PLSR_BFSARFileInfoType_External) { 89 | return PLSR_RC_OK; 90 | } 91 | 92 | char tmp[sizeof(soundFileInfo->external.path)]; 93 | strncpy(tmp, soundFileInfo->external.path, sizeof(tmp)); 94 | 95 | _LOCAL_TRY(plsrArchiveRelativePath(&bfsar->ar, tmp, soundFileInfo->external.path, sizeof(soundFileInfo->external.path))); 96 | 97 | return PLSR_RC_OK; 98 | } -------------------------------------------------------------------------------- /src/bfsar/bfsar_group.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFSAR, Group, X) 6 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFSAR, Group, X) 7 | 8 | PLSR_RC plsrBFSARGroupGet(const PLSR_BFSAR* bfsar, u32 index, PLSR_BFSARGroupInfo* out) { 9 | PLSR_ArchiveTableEntry tableEntry; 10 | 11 | _LOCAL_TRY(plsrArchiveReadTableEntry(&bfsar->ar, &bfsar->groupTable, _PLSR_BFSAR_INFO_IDENTIFIER_GROUP_ENTRY, index, &tableEntry)); 12 | 13 | _PLSR_BFSARGroupEntry _groupEntry; 14 | _LOCAL_TRY(plsrArchiveReadAt(&bfsar->ar, tableEntry.offset, &_groupEntry, sizeof(_groupEntry))); 15 | 16 | out->fileIndex = _groupEntry.fileIndex; 17 | out->hasStringIndex = _groupEntry.flags & _PLSR_BFSAR_FLAG_STRING_INDEX; 18 | 19 | if(out->hasStringIndex) { 20 | _LOCAL_TRY(plsrArchiveRead(&bfsar->ar, &out->stringIndex, sizeof(out->stringIndex))); 21 | } 22 | 23 | return PLSR_RC_OK; 24 | } 25 | 26 | PLSR_RC plsrBFSARGroupOpen(const PLSR_BFSAR* bfsar, const PLSR_BFSARGroupInfo* groupInfo, PLSR_BFGRP* out) { 27 | PLSR_BFSARFileInfo fileInfo; 28 | PLSR_RC_TRY(plsrBFSARFileGet(bfsar, groupInfo->fileIndex, &fileInfo)); 29 | 30 | switch(fileInfo.type) { 31 | case PLSR_BFSARFileInfoType_External: 32 | return plsrBFGRPOpen(fileInfo.external.path, out); 33 | case PLSR_BFSARFileInfoType_Internal: 34 | return plsrBFGRPOpenInside(&bfsar->ar, fileInfo.internal.offset, out); 35 | default: 36 | return _LOCAL_RC_MAKE(Unsupported); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/bfsar/bfsar_sound.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFSAR, Sound, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFSAR, Sound, X) 5 | 6 | static PLSR_RC _BFSARReadSoundWaveEntry(const PLSR_BFSAR* bfsar, u32 offset, PLSR_BFSARSoundWaveInfo* out) { 7 | _PLSR_BFSARSoundWaveEntry _waveEntry; 8 | _LOCAL_TRY(plsrArchiveReadAt(&bfsar->ar, offset, &_waveEntry, sizeof(_waveEntry))); 9 | 10 | out->index = _waveEntry.index; 11 | out->trackCount = _waveEntry.trackCount; 12 | 13 | return PLSR_RC_OK; 14 | } 15 | 16 | PLSR_RC plsrBFSARSoundGet(const PLSR_BFSAR* bfsar, u32 index, PLSR_BFSARSoundInfo* out) { 17 | PLSR_ArchiveTableEntry tableEntry; 18 | 19 | _LOCAL_TRY(plsrArchiveReadTableEntry(&bfsar->ar, &bfsar->soundTable, _PLSR_BFSAR_INFO_IDENTIFIER_SOUND_ENTRY, index, &tableEntry)); 20 | 21 | _PLSR_BFSARSoundEntry _soundEntry; 22 | _LOCAL_TRY(plsrArchiveReadAt(&bfsar->ar, tableEntry.offset, &_soundEntry, sizeof(_soundEntry))); 23 | 24 | out->fileIndex = _soundEntry.fileIndex; 25 | out->playerItemId.raw = _soundEntry.playerItemId; 26 | out->initialVolume = _soundEntry.initialVolume; 27 | out->remoteFilter = _soundEntry.remoteFilter; 28 | out->hasStringIndex = _soundEntry.flags & _PLSR_BFSAR_FLAG_STRING_INDEX; 29 | // UNIMPLEMENTED: Remaining flags 30 | 31 | if(out->hasStringIndex) { 32 | _LOCAL_TRY(plsrArchiveRead(&bfsar->ar, &out->stringIndex, sizeof(out->stringIndex))); 33 | } 34 | 35 | switch(_soundEntry.ref.id) { 36 | case _PLSR_BFSAR_INFO_IDENTIFIER_STREAM_ENTRY: 37 | out->type = PLSR_BFSARSoundType_Stream; 38 | // UNIMPLEMENTED: Stream specific infos 39 | break; 40 | case _PLSR_BFSAR_INFO_IDENTIFIER_WAVE_ENTRY: 41 | out->type = PLSR_BFSARSoundType_Wave; 42 | return _BFSARReadSoundWaveEntry(bfsar, tableEntry.offset + _soundEntry.ref.offset, &out->wave); 43 | case _PLSR_BFSAR_INFO_IDENTIFIER_SEQUENCE_ENTRY: 44 | out->type = PLSR_BFSARSoundType_Sequence; 45 | // UNIMPLEMENTED: Sequence specific infos 46 | break; 47 | default: 48 | return _LOCAL_RC_MAKE(Unsupported); 49 | } 50 | 51 | return PLSR_RC_OK; 52 | } 53 | -------------------------------------------------------------------------------- /src/bfsar/bfsar_string.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFSAR, String, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFSAR, String, X) 5 | 6 | PLSR_RC plsrBFSARStringGet(const PLSR_BFSAR* bfsar, u32 index, char* out, size_t size) { 7 | PLSR_ArchiveTableBlock block; 8 | 9 | _LOCAL_TRY(plsrArchiveReadTableBlock(&bfsar->ar, &bfsar->stringTable, _PLSR_BFSAR_STRG_IDENTIFIER_STRING_TABLE_ENTRY, index, &block)); 10 | 11 | size_t stringOutSize = size < block.size ? size : block.size; 12 | _LOCAL_TRY(plsrArchiveReadAt(&bfsar->ar, block.offset, out, stringOutSize)); 13 | 14 | out[size-1] = '\0'; 15 | return PLSR_RC_OK; 16 | } 17 | 18 | PLSR_RC plsrBFSARStringSearchEx(const PLSR_BFSAR* bfsar, const char* query, size_t queryLen, PLSR_BFSARStringSearchInfo* out) { 19 | _PLSR_BFSARStringTreeNode _node; 20 | char c; 21 | u32 index = bfsar->stringTree.info.rootNodeIndex; 22 | 23 | while(index < bfsar->stringTree.info.nodeCount) { 24 | _LOCAL_TRY(plsrArchiveReadAt(&bfsar->ar, bfsar->stringTree.offset + sizeof(_PLSR_BFSARStringTreeHeader) + index * sizeof(_PLSR_BFSARStringTreeNode), &_node, sizeof(_node))); 25 | 26 | if(_node.endpointFlag) { 27 | break; 28 | } 29 | 30 | c = (_node.stringBit / 8) >= queryLen ? 0 : query[_node.stringBit / 8]; 31 | index = (c >> (7 - _node.stringBit % 8)) & 1 ? _node.rightIndex : _node.leftIndex; 32 | } 33 | 34 | PLSR_RC_TRY(plsrBFSARStringGet(bfsar, _node.stringTableIndex, &out->name[0], sizeof(out->name))); 35 | 36 | if(strncmp(out->name, query, sizeof(out->name))) { 37 | return _LOCAL_RC_MAKE(NotFound); 38 | } 39 | 40 | out->itemId.raw = _node.itemId; 41 | 42 | return PLSR_RC_OK; 43 | } 44 | -------------------------------------------------------------------------------- /src/bfsar/bfsar_wave_archive.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFSAR, WaveArchive, X) 6 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFSAR, WaveArchive, X) 7 | 8 | PLSR_RC plsrBFSARWaveArchiveGet(const PLSR_BFSAR* bfsar, u32 index, PLSR_BFSARWaveArchiveInfo* out) { 9 | PLSR_ArchiveTableEntry tableEntry; 10 | 11 | _LOCAL_TRY(plsrArchiveReadTableEntry(&bfsar->ar, &bfsar->waveArchiveTable, _PLSR_BFSAR_INFO_IDENTIFIER_WAVE_ARCHIVE_ENTRY, index, &tableEntry)); 12 | 13 | _PLSR_BFSARWaveArchiveEntry _waveArchiveEntry; 14 | _LOCAL_TRY(plsrArchiveReadAt(&bfsar->ar, tableEntry.offset, &_waveArchiveEntry, sizeof(_waveArchiveEntry))); 15 | 16 | out->fileIndex = _waveArchiveEntry.fileIndex; 17 | out->hasStringIndex = _waveArchiveEntry.flags & _PLSR_BFSAR_FLAG_STRING_INDEX; 18 | out->hasWaveCount = _waveArchiveEntry.flags & _PLSR_BFSAR_FLAG_WAVE_ARCHIVE_COUNT; 19 | 20 | if(out->hasStringIndex) { 21 | _LOCAL_TRY(plsrArchiveRead(&bfsar->ar, &out->stringIndex, sizeof(out->stringIndex))); 22 | } 23 | 24 | if(out->hasWaveCount) { 25 | _LOCAL_TRY(plsrArchiveRead(&bfsar->ar, &out->waveCount, sizeof(out->waveCount))); 26 | } 27 | 28 | return PLSR_RC_OK; 29 | } 30 | 31 | PLSR_RC plsrBFSARWaveArchiveOpen(const PLSR_BFSAR* bfsar, const PLSR_BFSARWaveArchiveInfo* waveArchiveInfo, PLSR_BFWAR* out) { 32 | PLSR_BFSARFileInfo fileInfo; 33 | PLSR_RC_TRY(plsrBFSARFileScan(bfsar, waveArchiveInfo->fileIndex, &fileInfo)); 34 | 35 | switch(fileInfo.type) { 36 | case PLSR_BFSARFileInfoType_External: 37 | return plsrBFWAROpen(fileInfo.external.path, out); 38 | case PLSR_BFSARFileInfoType_Internal: 39 | return plsrBFWAROpenInside(&bfsar->ar, fileInfo.internal.offset, out); 40 | default: 41 | return _LOCAL_RC_MAKE(Unsupported); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/bfstm/bfstm.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFSTM, Init, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFSTM, Init, X) 5 | 6 | static PLSR_RC _BFSTMReadHeaderInfo(PLSR_BFSTM* bfstm) { 7 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfstm->ar, bfstm->infoSection.offset, _PLSR_BFSTM_INFO_MAGIC, &bfstm->infoSection.info)); 8 | 9 | // Track info table and channel info table references have the same identifier 10 | // So we read them in order instead of the traditionnal loop 11 | _PLSR_ArchiveRef _ref; 12 | 13 | _LOCAL_TRY(plsrArchiveRead(&bfstm->ar, &_ref, sizeof(_ref))); 14 | if(_ref.id != _PLSR_BFSTM_INFO_IDENTIFIER_STREAM_INFO) { 15 | return _LOCAL_RC_MAKE(Unsupported); 16 | } 17 | bfstm->streamInfoOffset = bfstm->infoSection.offset + sizeof(_PLSR_ArchiveSectionHeader) + _ref.offset; 18 | 19 | // UNIMPLEMENTED: track infos 20 | _LOCAL_TRY(plsrArchiveRead(&bfstm->ar, &_ref, sizeof(_ref))); 21 | if(_ref.id != _PLSR_BFSTM_INFO_IDENTIFIER_TRACK_TABLE && _ref.id != 0) { 22 | return _LOCAL_RC_MAKE(Unsupported); 23 | } 24 | 25 | _LOCAL_TRY(plsrArchiveRead(&bfstm->ar, &_ref, sizeof(_ref))); 26 | if(_ref.id == _PLSR_BFSTM_INFO_IDENTIFIER_CHANNEL_TABLE) { 27 | bfstm->channelTable.offset = bfstm->infoSection.offset + sizeof(_PLSR_ArchiveSectionHeader) + _ref.offset; 28 | } else if(_ref.id != 0) { 29 | return _LOCAL_RC_MAKE(Unsupported); 30 | } 31 | 32 | _LOCAL_TRY(plsrArchiveReadTableHeaderInfo(&bfstm->ar, bfstm->channelTable.offset, &bfstm->channelTable.info)); 33 | 34 | return PLSR_RC_OK; 35 | } 36 | 37 | static PLSR_RC _BFSTMInit(PLSR_BFSTM* bfstm) { 38 | _LOCAL_TRY(plsrArchiveReadHeaderInfo(&bfstm->ar, _PLSR_BFSTM_MAGIC, &bfstm->headerInfo)); 39 | 40 | _PLSR_ArchiveBlockRef _ref; 41 | unsigned identifierCount = 2; 42 | for(unsigned i = 0; i < bfstm->headerInfo.sectionCount && identifierCount > 0; i++) { 43 | _LOCAL_TRY(plsrArchiveRead(&bfstm->ar, &_ref, sizeof(_ref))); 44 | 45 | switch(_ref.id) { 46 | case _PLSR_BFSTM_SECTION_IDENTIFIER_INFO: 47 | bfstm->infoSection.offset = _ref.offset; 48 | identifierCount--; 49 | break; 50 | case _PLSR_BFSTM_SECTION_IDENTIFIER_DATA: 51 | bfstm->dataSection.offset = _ref.offset; 52 | identifierCount--; 53 | break; 54 | } 55 | } 56 | 57 | // UNIMPLEMENTED: SEEK infos 58 | 59 | _LOCAL_TRY(_BFSTMReadHeaderInfo(bfstm)); 60 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfstm->ar, bfstm->dataSection.offset, _PLSR_BFSTM_DATA_MAGIC, &bfstm->dataSection.info)); 61 | 62 | return PLSR_RC_OK; 63 | } 64 | 65 | PLSR_RC plsrBFSTMOpen(const char* path, PLSR_BFSTM* out) { 66 | memset(out, 0, sizeof(PLSR_BFSTM)); 67 | 68 | PLSR_RC rc = plsrArchiveOpen(path, &out->ar); 69 | 70 | if(PLSR_RC_SUCCEEDED(rc)) { 71 | rc = _BFSTMInit(out); 72 | } 73 | 74 | if(PLSR_RC_FAILED(rc)) { 75 | plsrBFSTMClose(out); 76 | } 77 | 78 | return rc; 79 | } 80 | 81 | PLSR_RC plsrBFSTMOpenInside(const PLSR_Archive* ar, u32 offset, PLSR_BFSTM* out) { 82 | memset(out, 0, sizeof(PLSR_BFSTM)); 83 | 84 | PLSR_RC rc = plsrArchiveOpenInside(ar, offset, &out->ar); 85 | 86 | if(PLSR_RC_SUCCEEDED(rc)) { 87 | rc = _BFSTMInit(out); 88 | } 89 | 90 | if(PLSR_RC_FAILED(rc)) { 91 | plsrBFSTMClose(out); 92 | } 93 | 94 | return rc; 95 | } 96 | 97 | void plsrBFSTMClose(PLSR_BFSTM* bfstm) { 98 | plsrArchiveClose(&bfstm->ar); 99 | } 100 | -------------------------------------------------------------------------------- /src/bfstm/bfstm_channel.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFSTM, Channel, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFSTM, Channel, X) 5 | 6 | PLSR_RC plsrBFSTMChannelGet(const PLSR_BFSTM* bfstm, u32 index, PLSR_BFSTMChannelInfo* out) { 7 | PLSR_ArchiveTableEntry tableEntry; 8 | _LOCAL_TRY(plsrArchiveReadTableEntry(&bfstm->ar, &bfstm->channelTable, _PLSR_BFSTM_INFO_IDENTIFIER_CHANNEL_ENTRY, index, &tableEntry)); 9 | 10 | _PLSR_BFSTMChannelInfo _channelInfo; 11 | _LOCAL_TRY(plsrArchiveReadAt(&bfstm->ar, tableEntry.offset, &_channelInfo, sizeof(_channelInfo))); 12 | 13 | if(_channelInfo.adpcmInfoRef.id == _PLSR_BFSTM_INFO_IDENTIFIER_ADPCM_ENTRY) { 14 | _LOCAL_TRY(plsrArchiveReadAt(&bfstm->ar, tableEntry.offset + _channelInfo.adpcmInfoRef.offset, &out->adpcmInfo, sizeof(out->adpcmInfo))); 15 | } 16 | 17 | return PLSR_RC_OK; 18 | } 19 | -------------------------------------------------------------------------------- /src/bfstm/bfstm_info.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFSTM, Info, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFSTM, Info, X) 5 | 6 | PLSR_RC plsrBFSTMReadInfo(const PLSR_BFSTM* bfstm, PLSR_BFSTMInfo* out) { 7 | _PLSR_BFSTMInfo _info; 8 | 9 | _LOCAL_TRY(plsrArchiveReadAt(&bfstm->ar, bfstm->streamInfoOffset, &_info, sizeof(_info))); 10 | 11 | if(_info.dataRef.id != _PLSR_BFSTM_INFO_IDENTIFIER_STREAM_DATA) { 12 | return _LOCAL_RC_MAKE(NotFound); 13 | } 14 | 15 | out->format = _info.format; 16 | out->looping = _info.looping; 17 | out->sampleRate = _info.sampleRate; 18 | out->loopStartSample = _info.loopStartSample; 19 | out->sampleCount = _info.sampleCount; 20 | out->blockCount = _info.blockCount; 21 | out->blockSize = _info.blockSize; 22 | out->blockSampleCount = _info.blockSampleCount; 23 | out->lastBlockSize = _info.lastBlockSize; 24 | out->lastBlockSampleCount = _info.lastBlockSampleCount; 25 | out->lastBlockSizeWithPadding = _info.lastBlockSizeWithPadding; 26 | 27 | out->dataOffset = _info.dataRef.offset + bfstm->dataSection.offset + sizeof(_PLSR_ArchiveSectionHeader); 28 | 29 | // UNIMPLEMENTED: SEEK infos 30 | return PLSR_RC_OK; 31 | } 32 | -------------------------------------------------------------------------------- /src/bfwar/bfwar.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFWAR, Init, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFWAR, Init, X) 5 | 6 | static PLSR_RC _BFWARInit(PLSR_BFWAR* bfwar) { 7 | _LOCAL_TRY(plsrArchiveReadHeaderInfo(&bfwar->ar, _PLSR_BFWAR_MAGIC, &bfwar->headerInfo)); 8 | 9 | _PLSR_ArchiveBlockRef _ref; 10 | unsigned identifierCount = 2; 11 | for(unsigned i = 0; i < bfwar->headerInfo.sectionCount && identifierCount > 0; i++) { 12 | _LOCAL_TRY(plsrArchiveRead(&bfwar->ar, &_ref, sizeof(_ref))); 13 | 14 | switch(_ref.id) { 15 | case _PLSR_BFWAR_SECTION_IDENTIFIER_INFO: 16 | bfwar->infoSection.offset = _ref.offset; 17 | identifierCount--; 18 | break; 19 | case _PLSR_BFWAR_SECTION_IDENTIFIER_FILE: 20 | bfwar->fileSection.offset = _ref.offset; 21 | identifierCount--; 22 | break; 23 | } 24 | } 25 | 26 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfwar->ar, bfwar->infoSection.offset, _PLSR_BFWAR_INFO_MAGIC, &bfwar->infoSection.info)); 27 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfwar->ar, bfwar->fileSection.offset, _PLSR_BFWAR_FILE_MAGIC, &bfwar->fileSection.info)); 28 | 29 | bfwar->fileTable.offset = bfwar->infoSection.offset + sizeof(_PLSR_ArchiveSectionHeader); 30 | _LOCAL_TRY(plsrArchiveReadTableHeaderInfo(&bfwar->ar, bfwar->fileTable.offset, &bfwar->fileTable.info)); 31 | 32 | return PLSR_RC_OK; 33 | } 34 | 35 | PLSR_RC plsrBFWAROpen(const char* path, PLSR_BFWAR* out) { 36 | memset(out, 0, sizeof(PLSR_BFWAR)); 37 | 38 | PLSR_RC rc = plsrArchiveOpen(path, &out->ar); 39 | 40 | if(PLSR_RC_SUCCEEDED(rc)) { 41 | rc = _BFWARInit(out); 42 | } 43 | 44 | if(PLSR_RC_FAILED(rc)) { 45 | plsrBFWARClose(out); 46 | } 47 | 48 | return rc; 49 | } 50 | 51 | PLSR_RC plsrBFWAROpenInside(const PLSR_Archive* ar, u32 offset, PLSR_BFWAR* out) { 52 | memset(out, 0, sizeof(PLSR_BFWAR)); 53 | 54 | PLSR_RC rc = plsrArchiveOpenInside(ar, offset, &out->ar); 55 | 56 | if(PLSR_RC_SUCCEEDED(rc)) { 57 | rc = _BFWARInit(out); 58 | } 59 | 60 | if(PLSR_RC_FAILED(rc)) { 61 | plsrBFWARClose(out); 62 | } 63 | 64 | return rc; 65 | } 66 | 67 | void plsrBFWARClose(PLSR_BFWAR* bfwar) { 68 | plsrArchiveClose(&bfwar->ar); 69 | } 70 | -------------------------------------------------------------------------------- /src/bfwar/bfwar_file.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFWAR, File, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFWAR, File, X) 5 | 6 | PLSR_RC plsrBFWARFileGet(const PLSR_BFWAR* bfwar, u32 index, PLSR_BFWARFileInfo* out) { 7 | PLSR_ArchiveTableBlock tableBlock; 8 | _LOCAL_TRY(plsrArchiveReadTableBlock(&bfwar->ar, &bfwar->fileTable, _PLSR_BFWAR_FILE_IDENTIFIER_BLOCK_ENTRY, index, &tableBlock)); 9 | 10 | out->offset = tableBlock.offset + bfwar->fileSection.offset + sizeof(_PLSR_ArchiveSectionHeader) - bfwar->fileTable.offset; 11 | out->size = tableBlock.size; 12 | return PLSR_RC_OK; 13 | } 14 | -------------------------------------------------------------------------------- /src/bfwav/bfwav.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFWAV, Init, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFWAV, Init, X) 5 | 6 | static PLSR_RC _BFWAVInit(PLSR_BFWAV* bfwav) { 7 | _LOCAL_TRY(plsrArchiveReadHeaderInfo(&bfwav->ar, _PLSR_BFWAV_MAGIC, &bfwav->headerInfo)); 8 | 9 | _PLSR_ArchiveBlockRef _ref; 10 | unsigned identifierCount = 2; 11 | for(unsigned i = 0; i < bfwav->headerInfo.sectionCount && identifierCount > 0; i++) { 12 | _LOCAL_TRY(plsrArchiveRead(&bfwav->ar, &_ref, sizeof(_ref))); 13 | 14 | switch(_ref.id) { 15 | case _PLSR_BFWAV_SECTION_IDENTIFIER_INFO: 16 | bfwav->infoSection.offset = _ref.offset; 17 | identifierCount--; 18 | break; 19 | case _PLSR_BFWAV_SECTION_IDENTIFIER_DATA: 20 | bfwav->dataSection.offset = _ref.offset; 21 | identifierCount--; 22 | break; 23 | } 24 | } 25 | 26 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfwav->ar, bfwav->infoSection.offset, _PLSR_BFWAV_INFO_MAGIC, &bfwav->infoSection.info)); 27 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfwav->ar, bfwav->dataSection.offset, _PLSR_BFWAV_DATA_MAGIC, &bfwav->dataSection.info)); 28 | 29 | return PLSR_RC_OK; 30 | } 31 | 32 | PLSR_RC plsrBFWAVOpen(const char* path, PLSR_BFWAV* out) { 33 | memset(out, 0, sizeof(PLSR_BFWAV)); 34 | 35 | PLSR_RC rc = plsrArchiveOpen(path, &out->ar); 36 | 37 | if(PLSR_RC_SUCCEEDED(rc)) { 38 | rc = _BFWAVInit(out); 39 | } 40 | 41 | if(PLSR_RC_FAILED(rc)) { 42 | plsrBFWAVClose(out); 43 | } 44 | 45 | return rc; 46 | } 47 | 48 | PLSR_RC plsrBFWAVOpenInside(const PLSR_Archive* ar, u32 offset, PLSR_BFWAV* out) { 49 | memset(out, 0, sizeof(PLSR_BFWAV)); 50 | 51 | PLSR_RC rc = plsrArchiveOpenInside(ar, offset, &out->ar); 52 | 53 | if(PLSR_RC_SUCCEEDED(rc)) { 54 | rc = _BFWAVInit(out); 55 | } 56 | 57 | if(PLSR_RC_FAILED(rc)) { 58 | plsrBFWAVClose(out); 59 | } 60 | 61 | return rc; 62 | } 63 | 64 | void plsrBFWAVClose(PLSR_BFWAV* bfwav) { 65 | plsrArchiveClose(&bfwav->ar); 66 | } 67 | -------------------------------------------------------------------------------- /src/bfwav/bfwav_info.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFWAV, Info, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFWAV, Info, X) 5 | 6 | PLSR_RC plsrBFWAVReadInfo(const PLSR_BFWAV* bfwav, PLSR_BFWAVInfo* out) { 7 | if(bfwav->infoSection.info.blockSize < sizeof(_PLSR_BFWAVInfo)) { 8 | return _LOCAL_RC_MAKE(NotFound); 9 | } 10 | 11 | _PLSR_BFWAVInfo _info; 12 | u32 infoOffset = bfwav->infoSection.offset + sizeof(_PLSR_ArchiveSectionHeader); 13 | _LOCAL_TRY(plsrArchiveReadAt(&bfwav->ar, infoOffset, &_info, sizeof(_info))); 14 | 15 | out->format = _info.format; 16 | out->looping = _info.looping; 17 | out->sampleRate = _info.sampleRate; 18 | out->loopStartSample = _info.loopStartSample; 19 | out->sampleCount = _info.sampleCount; 20 | 21 | out->channelInfoTable.offset = infoOffset + sizeof(_PLSR_BFWAVInfo); 22 | _LOCAL_TRY(plsrArchiveReadTableHeaderInfo(&bfwav->ar, out->channelInfoTable.offset, &out->channelInfoTable.info)); 23 | 24 | return PLSR_RC_OK; 25 | } 26 | 27 | PLSR_RC plsrBFWAVReadChannelInfo(const PLSR_BFWAV* bfwav, const PLSR_BFWAVChannelInfoTable* table, u32 index, PLSR_BFWAVChannelInfo* out) { 28 | PLSR_ArchiveTableEntry tableEntry; 29 | _LOCAL_TRY(plsrArchiveReadTableEntry(&bfwav->ar, table, _PLSR_BFWAV_INFO_IDENTIFIER_CHANNEL_ENTRY, index, &tableEntry)); 30 | 31 | _PLSR_BFWAVChannelInfo _channelInfo; 32 | _LOCAL_TRY(plsrArchiveReadAt(&bfwav->ar, tableEntry.offset, &_channelInfo, sizeof(_channelInfo))); 33 | 34 | if(_channelInfo.dataRef.id != _PLSR_BFWAV_INFO_IDENTIFIER_DATA_ENTRY) { 35 | return _LOCAL_RC_MAKE(NotFound); 36 | } 37 | 38 | out->dataOffset = _channelInfo.dataRef.offset + bfwav->dataSection.offset + sizeof(_PLSR_ArchiveSectionHeader); 39 | 40 | if(_channelInfo.adpcmInfoRef.id == _PLSR_BFWAV_INFO_IDENTIFIER_ADPCM_ENTRY) { 41 | _LOCAL_TRY(plsrArchiveReadAt(&bfwav->ar, tableEntry.offset + _channelInfo.adpcmInfoRef.offset, &out->adpcmInfo, sizeof(out->adpcmInfo))); 42 | } 43 | 44 | return PLSR_RC_OK; 45 | } 46 | -------------------------------------------------------------------------------- /src/bfwsd/bfwsd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFWSD, Init, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFWSD, Init, X) 5 | 6 | static PLSR_RC _BFWSDInit(PLSR_BFWSD* bfwsd) { 7 | _LOCAL_TRY(plsrArchiveReadHeaderInfo(&bfwsd->ar, _PLSR_BFWSD_MAGIC, &bfwsd->headerInfo)); 8 | 9 | _PLSR_ArchiveBlockRef _blockRef; 10 | for(unsigned i = 0; i < bfwsd->headerInfo.sectionCount; i++) { 11 | _LOCAL_TRY(plsrArchiveRead(&bfwsd->ar, &_blockRef, sizeof(_blockRef))); 12 | 13 | if(_blockRef.id == _PLSR_BFWSD_SECTION_IDENTIFIER_INFO) { 14 | bfwsd->infoSection.offset = _blockRef.offset; 15 | break; 16 | } 17 | } 18 | 19 | _LOCAL_TRY(plsrArchiveReadSectionHeaderInfo(&bfwsd->ar, bfwsd->infoSection.offset, _PLSR_BFWSD_INFO_MAGIC, &bfwsd->infoSection.info)); 20 | 21 | _PLSR_ArchiveRef _ref; 22 | unsigned identifierCount = 2; 23 | for(unsigned read = 0; read < bfwsd->infoSection.info.blockSize && identifierCount > 0; read += sizeof(_ref)) { 24 | _LOCAL_TRY(plsrArchiveRead(&bfwsd->ar, &_ref, sizeof(_ref))); 25 | 26 | switch(_ref.id) { 27 | case _PLSR_BFWSD_INFO_IDENTIFIER_WAVE_ID_TABLE: 28 | bfwsd->waveIdList.offset = bfwsd->infoSection.offset + sizeof(_PLSR_ArchiveSectionHeader) + _ref.offset; 29 | identifierCount--; 30 | break; 31 | case _PLSR_BFWSD_INFO_IDENTIFIER_SOUND_DATA_TABLE: 32 | bfwsd->soundDataTable.offset = bfwsd->infoSection.offset + sizeof(_PLSR_ArchiveSectionHeader) + _ref.offset; 33 | identifierCount--; 34 | break; 35 | } 36 | } 37 | 38 | _LOCAL_TRY(plsrArchiveReadAt(&bfwsd->ar, bfwsd->waveIdList.offset, &bfwsd->waveIdList.info.count, sizeof(bfwsd->waveIdList.info.count))); 39 | bfwsd->waveIdList.offset += sizeof(bfwsd->waveIdList.info.count); 40 | bfwsd->waveIdList.info.entrySize = sizeof(_PLSR_BFWSDWaveId); 41 | 42 | _LOCAL_TRY(plsrArchiveReadTableHeaderInfo(&bfwsd->ar, bfwsd->soundDataTable.offset, &bfwsd->soundDataTable.info)); 43 | 44 | return PLSR_RC_OK; 45 | } 46 | 47 | PLSR_RC plsrBFWSDOpen(const char* path, PLSR_BFWSD* out) { 48 | memset(out, 0, sizeof(PLSR_BFWSD)); 49 | 50 | PLSR_RC rc = plsrArchiveOpen(path, &out->ar); 51 | 52 | if(PLSR_RC_SUCCEEDED(rc)) { 53 | rc = _BFWSDInit(out); 54 | } 55 | 56 | if(PLSR_RC_FAILED(rc)) { 57 | plsrBFWSDClose(out); 58 | } 59 | 60 | return rc; 61 | } 62 | 63 | PLSR_RC plsrBFWSDOpenInside(const PLSR_Archive* ar, u32 offset, PLSR_BFWSD* out) { 64 | memset(out, 0, sizeof(PLSR_BFWSD)); 65 | 66 | PLSR_RC rc = plsrArchiveOpenInside(ar, offset, &out->ar); 67 | 68 | if(PLSR_RC_SUCCEEDED(rc)) { 69 | rc = _BFWSDInit(out); 70 | } 71 | 72 | if(PLSR_RC_FAILED(rc)) { 73 | plsrBFWSDClose(out); 74 | } 75 | 76 | return rc; 77 | } 78 | 79 | void plsrBFWSDClose(PLSR_BFWSD* bfwav) { 80 | plsrArchiveClose(&bfwav->ar); 81 | } 82 | -------------------------------------------------------------------------------- /src/bfwsd/bfwsd_sound_data.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFWSD, SoundData, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFWSD, SoundData, X) 5 | 6 | static PLSR_RC _BFWSDReadSoundInfo(const PLSR_BFWSD* bfwsd, u32 offset, PLSR_BFWSDSoundDataInfo* out) { 7 | _PLSR_BFWSDSoundInfoEntry _soundInfoEntry; 8 | u32 tmp; 9 | u32 sendOffset = 0; 10 | u32 adshrOffset = 0; 11 | 12 | _LOCAL_TRY(plsrArchiveReadAt(&bfwsd->ar, offset, &_soundInfoEntry, sizeof(_soundInfoEntry))); 13 | 14 | out->hasPan = _soundInfoEntry.flags & _PLSR_BFWSD_FLAG_SOUND_INFO_PAN; 15 | out->hasPitch = _soundInfoEntry.flags & _PLSR_BFWSD_FLAG_SOUND_INFO_PITCH; 16 | 17 | if(out->hasPan) { 18 | _LOCAL_TRY(plsrArchiveRead(&bfwsd->ar, &tmp, sizeof(tmp))); 19 | out->pan = tmp & 0xFF; 20 | out->surroundPan = (tmp >> 8) & 0xFF; 21 | } 22 | 23 | if(out->hasPitch) { 24 | _LOCAL_TRY(plsrArchiveRead(&bfwsd->ar, &out->pitch, sizeof(out->pitch))); 25 | } 26 | 27 | if(_soundInfoEntry.flags & _PLSR_BFWSD_FLAG_SOUND_INFO_SEND) { 28 | _LOCAL_TRY(plsrArchiveRead(&bfwsd->ar, &sendOffset, sizeof(sendOffset))); 29 | } 30 | 31 | if(_soundInfoEntry.flags & _PLSR_BFWSD_FLAG_SOUND_INFO_ADSHR) { 32 | _LOCAL_TRY(plsrArchiveRead(&bfwsd->ar, &adshrOffset, sizeof(adshrOffset))); 33 | } 34 | 35 | out->hasSend = sendOffset != 0 && sendOffset != 0xFFFFFFFF; 36 | out->hasAdshr = adshrOffset != 0 && adshrOffset != 0xFFFFFFFF; 37 | 38 | if(out->hasSend) { 39 | // While we could read main + count first, and then read the rest using the count we just got, 40 | // one read is enough, and should always work because of the padding to 8 bytes 41 | _LOCAL_TRY(plsrArchiveReadAt(&bfwsd->ar, sendOffset + offset, &out->send, sizeof(out->send))); 42 | 43 | if(out->send.auxCount > (sizeof(out->send.aux) / sizeof(out->send.aux[0]))) { 44 | return _LOCAL_RC_MAKE(Unsupported); 45 | } 46 | } 47 | 48 | if(out->hasAdshr) { 49 | _LOCAL_TRY(plsrArchiveReadAt(&bfwsd->ar, adshrOffset + offset, &out->adshr, sizeof(out->adshr))); 50 | } 51 | 52 | return PLSR_RC_OK; 53 | } 54 | 55 | PLSR_RC plsrBFWSDSoundDataGet(const PLSR_BFWSD* bfwsd, u32 index, PLSR_BFWSDSoundDataInfo* out) { 56 | PLSR_ArchiveTableEntry tableEntry; 57 | _LOCAL_TRY(plsrArchiveReadTableEntry(&bfwsd->ar, &bfwsd->soundDataTable, _PLSR_BFWSD_INFO_IDENTIFIER_SOUND_DATA_ENTRY, index, &tableEntry)); 58 | 59 | _PLSR_BFWSDSoundDataEntry _soundDataEntry; 60 | _LOCAL_TRY(plsrArchiveReadAt(&bfwsd->ar, tableEntry.offset, &_soundDataEntry, sizeof(_soundDataEntry))); 61 | 62 | if(_soundDataEntry.noteInfoTableRef.id != _PLSR_BFWSD_INFO_IDENTIFIER_NOTE_TABLE) { 63 | return _LOCAL_RC_MAKE(NotFound); 64 | } 65 | 66 | out->noteInfoTable.offset = tableEntry.offset + _soundDataEntry.noteInfoTableRef.offset; 67 | _LOCAL_TRY(plsrArchiveReadTableHeaderInfo(&bfwsd->ar, out->noteInfoTable.offset, &out->noteInfoTable.info)); 68 | 69 | if(_soundDataEntry.soundInfoRef.id != _PLSR_BFWSD_INFO_IDENTIFIER_SOUND_INFO_ENTRY) { 70 | return _LOCAL_RC_MAKE(NotFound); 71 | } 72 | PLSR_RC_TRY(_BFWSDReadSoundInfo(bfwsd, _soundDataEntry.soundInfoRef.offset, out)); 73 | // UNIMPLEMENTED: Track Info Reference 74 | 75 | return PLSR_RC_OK; 76 | } 77 | 78 | PLSR_RC plsrBFWSDSoundDataNoteGet(const PLSR_BFWSD* bfwsd, const PLSR_BFWSDNoteInfoTable* table, u32 index, PLSR_BFWSDNoteInfo* out) { 79 | PLSR_ArchiveTableEntry tableEntry; 80 | _LOCAL_TRY(plsrArchiveReadTableEntry(&bfwsd->ar, table, _PLSR_BFWSD_INFO_IDENTIFIER_NOTE_ENTRY, index, &tableEntry)); 81 | 82 | _PLSR_BFWSDNoteEntry _noteEntry; 83 | _LOCAL_TRY(plsrArchiveReadAt(&bfwsd->ar, tableEntry.offset, &_noteEntry, sizeof(_noteEntry))); 84 | 85 | out->waveIdIndex = _noteEntry.waveIdIndex; 86 | // UNIMPLEMENTED: flags 87 | 88 | return PLSR_RC_OK; 89 | } 90 | -------------------------------------------------------------------------------- /src/bfwsd/bfwsd_wave_id.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(BFWSD, WaveId, X) 4 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(BFWSD, WaveId, X) 5 | 6 | _Static_assert(sizeof(_PLSR_BFWSDWaveId) == sizeof(PLSR_BFWSDWaveId), "Structs should be the same to permit casting from raw reads"); 7 | 8 | PLSR_RC plsrBFWSDWaveIdListReadEntries(const PLSR_BFWSD* bfwsd, u32 startIndex, u32 count, PLSR_BFWSDWaveId* outIds, u32* outReadCount) { 9 | _LOCAL_TRY(plsrArchiveListReadEntries(&bfwsd->ar, &bfwsd->waveIdList, startIndex, count, outIds, outReadCount)); 10 | return PLSR_RC_OK; 11 | } 12 | 13 | PLSR_RC plsrBFWSDWaveIdListGetEntry(const PLSR_BFWSD* bfwsd, u32 index, PLSR_BFWSDWaveId* out) { 14 | _LOCAL_TRY(plsrArchiveListGetEntry(&bfwsd->ar, &bfwsd->waveIdList, index, out)); 15 | return PLSR_RC_OK; 16 | } 17 | -------------------------------------------------------------------------------- /src/player/player.c: -------------------------------------------------------------------------------- 1 | #ifdef __SWITCH__ 2 | 3 | #include 4 | 5 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(Player, Init, X) 6 | #define _LOCAL_NX_TRY(X) PLSR_RC_NX_LTRY(Player, Init, X) 7 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(Player, Init, X) 8 | 9 | static const PLSR_PlayerConfig g_default_config = { 10 | .initRenderer = true, 11 | .audrenConfig = { 12 | .output_rate = AudioRendererOutputRate_48kHz, 13 | .num_voices = 24, 14 | .num_effects = 0, 15 | .num_sinks = 1, 16 | .num_mix_objs = 1, 17 | .num_mix_buffers = 2, 18 | }, 19 | .startVoiceId = 0, 20 | .endVoiceId = 23, 21 | .sinkChannels = {0, 1} 22 | }; 23 | 24 | static PLSR_Player* g_instance = NULL; 25 | 26 | static PLSR_RC _playerCreate(PLSR_Player* out, const PLSR_PlayerConfig* config) { 27 | _LOCAL_NX_TRY(audrvCreate(&out->driver, &config->audrenConfig, PLSR_PLAYER_MAX_CHANNELS)); 28 | 29 | memcpy(&out->config, config, sizeof(out->config)); 30 | 31 | audrvDeviceSinkAdd(&out->driver, AUDREN_DEFAULT_DEVICE_NAME, PLSR_PLAYER_MAX_CHANNELS, config->sinkChannels); 32 | audrvUpdate(&out->driver); 33 | 34 | if(config->initRenderer) { 35 | audrenStartAudioRenderer(); 36 | } 37 | 38 | return PLSR_RC_OK; 39 | } 40 | 41 | PLSR_Player* plsrPlayerGetInstance(void) { 42 | return g_instance; 43 | } 44 | 45 | const PLSR_PlayerConfig* plsrPlayerGetDefaultConfig(void) { 46 | return &g_default_config; 47 | } 48 | 49 | PLSR_RC plsrPlayerInitEx(const PLSR_PlayerConfig* config) { 50 | if(g_instance != NULL) { 51 | return PLSR_RC_OK; 52 | } 53 | 54 | if(config->startVoiceId < 0 || config->endVoiceId < 0 55 | || config->startVoiceId > config->endVoiceId 56 | || config->endVoiceId >= config->audrenConfig.num_voices) { 57 | return _LOCAL_RC_MAKE(BadInput); 58 | } 59 | 60 | if(config->initRenderer) { 61 | _LOCAL_NX_TRY(audrenInitialize(&config->audrenConfig)); 62 | } 63 | 64 | g_instance = (PLSR_Player*)malloc(sizeof(PLSR_Player)); 65 | if(g_instance == NULL) { 66 | return _LOCAL_RC_MAKE(Memory); 67 | } 68 | 69 | PLSR_RC rc = _playerCreate(g_instance, config); 70 | if(PLSR_RC_FAILED(rc)) { 71 | free(g_instance); 72 | g_instance = NULL; 73 | return rc; 74 | } 75 | 76 | return PLSR_RC_OK; 77 | } 78 | 79 | void plsrPlayerExit(void) { 80 | if(g_instance != NULL) { 81 | audrvClose(&g_instance->driver); 82 | 83 | if(g_instance->config.initRenderer) { 84 | audrenExit(); 85 | } 86 | 87 | free(g_instance); 88 | g_instance = NULL; 89 | } 90 | } 91 | 92 | PLSR_RC plsrPlayerPlay(PLSR_PlayerSoundId id) { 93 | if(id == PLSR_PLAYER_INVALID_SOUND) { 94 | return _LOCAL_RC_MAKE(BadInput); 95 | } 96 | 97 | if(g_instance == NULL) { 98 | return _LOCAL_RC_MAKE(NotReady); 99 | } 100 | 101 | PLSR_PlayerSound* sound = (PLSR_PlayerSound*)id; 102 | for(unsigned int channel = 0; channel < sound->channelCount; channel++) { 103 | audrvVoiceStop(&g_instance->driver, sound->channels[channel].voiceId); 104 | 105 | for(unsigned int i = 0; i < sound->wavebufCount; i++) { 106 | audrvVoiceAddWaveBuf(&g_instance->driver, sound->channels[channel].voiceId, &sound->channels[channel].wavebufs[i]); 107 | } 108 | 109 | audrvVoiceStart(&g_instance->driver, sound->channels[channel].voiceId); 110 | } 111 | 112 | audrvUpdate(&g_instance->driver); 113 | 114 | return PLSR_RC_OK; 115 | } 116 | 117 | 118 | PLSR_RC plsrPlayerStop(PLSR_PlayerSoundId id) { 119 | if(id == PLSR_PLAYER_INVALID_SOUND) { 120 | return _LOCAL_RC_MAKE(BadInput); 121 | } 122 | 123 | PLSR_PlayerSound* sound = (PLSR_PlayerSound*)id; 124 | for(unsigned int i = 0; i < sound->channelCount; i++) { 125 | if(sound->channels[i].voiceId != -1) { 126 | audrvVoiceStop(&g_instance->driver, sound->channels[i].voiceId); 127 | } 128 | } 129 | 130 | audrvUpdate(&g_instance->driver); 131 | 132 | return PLSR_RC_OK; 133 | } 134 | 135 | void plsrPlayerFree(PLSR_PlayerSoundId id) { 136 | if(id == PLSR_PLAYER_INVALID_SOUND) { 137 | return; 138 | } 139 | 140 | PLSR_PlayerSound* sound = (PLSR_PlayerSound*)id; 141 | for(unsigned int i = 0; i < sound->channelCount; i++) { 142 | if(sound->channels[i].voiceId != -1) { 143 | audrvVoiceDrop(&g_instance->driver, sound->channels[i].voiceId); 144 | } 145 | if(sound->channels[i].mempoolId != -1) { 146 | audrvMemPoolDetach(&g_instance->driver, sound->channels[i].mempoolId); 147 | } 148 | } 149 | 150 | audrvUpdate(&g_instance->driver); 151 | for(unsigned int i = 0; i < sound->channelCount; i++) { 152 | if(sound->channels[i].mempoolId != -1) { 153 | audrvMemPoolRemove(&g_instance->driver, sound->channels[i].mempoolId); 154 | } 155 | if(sound->channels[i].mempool != NULL) { 156 | free((void*)sound->channels[i].mempool); 157 | } 158 | } 159 | 160 | free(sound); 161 | } 162 | 163 | PLSR_RC plsrPlayerSetPitch(PLSR_PlayerSoundId id, float pitch) { 164 | if(id == PLSR_PLAYER_INVALID_SOUND) { 165 | return _LOCAL_RC_MAKE(BadInput); 166 | } 167 | 168 | PLSR_PlayerSound* sound = (PLSR_PlayerSound*)id; 169 | for(unsigned int i = 0; i < sound->channelCount; i++) { 170 | audrvVoiceSetPitch(&g_instance->driver, sound->channels[i].voiceId, pitch); 171 | } 172 | 173 | return PLSR_RC_OK; 174 | } 175 | 176 | PLSR_RC plsrPlayerSetVolume(PLSR_PlayerSoundId id, float volume) { 177 | if(id == PLSR_PLAYER_INVALID_SOUND) { 178 | return _LOCAL_RC_MAKE(BadInput); 179 | } 180 | 181 | PLSR_PlayerSound* sound = (PLSR_PlayerSound*)id; 182 | for(unsigned int i = 0; i < sound->channelCount; i++) { 183 | audrvVoiceSetVolume(&g_instance->driver, sound->channels[i].voiceId, volume); 184 | } 185 | 186 | return PLSR_RC_OK; 187 | } 188 | 189 | #endif 190 | -------------------------------------------------------------------------------- /src/player/player_load.c: -------------------------------------------------------------------------------- 1 | #ifdef __SWITCH__ 2 | 3 | #include 4 | 5 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(Player, Load, X) 6 | #define _LOCAL_NX_TRY(X) PLSR_RC_NX_LTRY(Player, Load, X) 7 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(Player, Load, X) 8 | #define _AUDREN_ALIGN(sz) ((sz + (AUDREN_MEMPOOL_ALIGNMENT-1)) &~ (AUDREN_MEMPOOL_ALIGNMENT-1)) 9 | 10 | static int _getFreeVoiceId(const PLSR_Player* player) { 11 | for(int id = player->config.startVoiceId; id <= player->config.endVoiceId; id++) { 12 | if(!player->driver.in_voices[id].is_used) { 13 | return id; 14 | } 15 | } 16 | 17 | return -1; 18 | } 19 | 20 | static PLSR_RC _readChannel(const PLSR_PlayerSoundLoadInfo* loadInfo, const PLSR_PlayerSoundLoadChannelLayoutInfo* layoutInfo, size_t dataSize, PLSR_PlayerSound* sound) { 21 | for(unsigned int channel = 0; channel < sound->channelCount; channel++) { 22 | _LOCAL_TRY(plsrArchiveReadAt(loadInfo->ar, layoutInfo->offsets[channel], sound->channels[channel].mempool, dataSize)); 23 | } 24 | 25 | return PLSR_RC_OK; 26 | } 27 | 28 | static PLSR_RC _readBlocks(const PLSR_PlayerSoundLoadInfo* loadInfo, const PLSR_PlayerSoundLoadBlocksLayoutInfo* layoutInfo, size_t dataSize, PLSR_PlayerSound* sound) { 29 | u8* channelData[PLSR_PLAYER_MAX_CHANNELS]; 30 | size_t blockCount = dataSize / layoutInfo->blockSize; 31 | size_t lastBlockSize = dataSize - blockCount * layoutInfo->blockSize; 32 | 33 | for(size_t blockReadCount = 0; blockReadCount < blockCount; blockReadCount++) { 34 | for(unsigned int channel = 0; channel < loadInfo->channelCount; channel++) { 35 | if(blockReadCount == 0) { 36 | channelData[channel] = (u8*)sound->channels[channel].mempool; 37 | } 38 | 39 | _LOCAL_TRY(plsrArchiveReadAt( 40 | loadInfo->ar, 41 | layoutInfo->firstBlockOffset + layoutInfo->blockSize * (blockReadCount * loadInfo->channelCount + channel), 42 | channelData[channel], 43 | layoutInfo->blockSize 44 | )); 45 | channelData[channel] += layoutInfo->blockSize; 46 | } 47 | } 48 | 49 | if(lastBlockSize != 0) { 50 | u32 base = layoutInfo->firstBlockOffset + layoutInfo->blockSize * (blockCount * loadInfo->channelCount); 51 | for(unsigned int channel = 0; channel < sound->channelCount; channel++) { 52 | _LOCAL_TRY(plsrArchiveReadAt( 53 | loadInfo->ar, 54 | base + ((lastBlockSize + layoutInfo->lastBlockPadding) * channel), 55 | channelData[channel], 56 | lastBlockSize 57 | )); 58 | } 59 | } 60 | 61 | return PLSR_RC_OK; 62 | } 63 | 64 | static PLSR_RC _loadSoundFromInfo(PLSR_Player* player, const PLSR_PlayerSoundLoadInfo* loadInfo, PLSR_PlayerSoundId* out) { 65 | size_t dataSize = loadInfo->dataSize; 66 | 67 | if(dataSize == 0) { 68 | switch(loadInfo->pcmFormat) { 69 | case PcmFormat_Int8: 70 | dataSize = loadInfo->sampleCount; 71 | break; 72 | case PcmFormat_Int16: 73 | dataSize = loadInfo->sampleCount * 2; 74 | break; 75 | case PcmFormat_Adpcm: 76 | dataSize = loadInfo->sampleCount * 8 + 1; 77 | dataSize = dataSize / 14 + dataSize % 14; 78 | break; 79 | default: 80 | return _LOCAL_RC_MAKE(Unsupported); 81 | } 82 | } 83 | 84 | PLSR_PlayerSound* sound = (PLSR_PlayerSound*)malloc(sizeof(PLSR_PlayerSound)); 85 | if(sound == NULL) { 86 | return _LOCAL_RC_MAKE(Memory); 87 | } 88 | *out = sound; 89 | 90 | bool useSecondLoopWavebuf = loadInfo->looping && loadInfo->loopStartSample != 0; 91 | size_t alignedDataSize = _AUDREN_ALIGN(dataSize); 92 | size_t alignedAdpcmParametersSize = loadInfo->pcmFormat == PcmFormat_Adpcm ? _AUDREN_ALIGN(sizeof(AudioRendererAdpcmParameters)) : 0; 93 | size_t alignedAdpcmContextSize = loadInfo->pcmFormat == PcmFormat_Adpcm ? _AUDREN_ALIGN(sizeof(AudioRendererAdpcmContext)) : 0; 94 | size_t mempoolSize = alignedDataSize + alignedAdpcmParametersSize + alignedAdpcmContextSize; 95 | 96 | sound->channelCount = 0; 97 | sound->wavebufCount = useSecondLoopWavebuf ? 2 : 1; 98 | for(unsigned int channel = 0; channel < loadInfo->channelCount && channel < PLSR_PLAYER_MAX_CHANNELS; channel++) { 99 | sound->channelCount++; 100 | 101 | memset(&sound->channels[channel].wavebufs[0], 0, sizeof(AudioDriverWaveBuf)); 102 | sound->channels[channel].mempool = NULL; 103 | sound->channels[channel].mempoolId = -1; 104 | sound->channels[channel].voiceId = _getFreeVoiceId(player); 105 | 106 | if(sound->channels[channel].voiceId == -1) { 107 | return _LOCAL_RC_MAKE(Memory); 108 | } 109 | 110 | if(!audrvVoiceInit(&player->driver, sound->channels[channel].voiceId, 1, loadInfo->pcmFormat, loadInfo->sampleRate)) { 111 | return _LOCAL_RC_MAKE(System); 112 | } 113 | 114 | audrvVoiceSetDestinationMix(&player->driver, sound->channels[channel].voiceId, AUDREN_FINAL_MIX_ID); 115 | for(unsigned int i = 0; i < PLSR_PLAYER_MAX_CHANNELS; i++) { 116 | audrvVoiceSetMixFactor( 117 | &player->driver, 118 | sound->channels[channel].voiceId, 119 | loadInfo->channelCount == 1 || i == channel ? 0.5f : 0.0f, 120 | 0, 121 | player->config.sinkChannels[i] 122 | ); 123 | } 124 | 125 | sound->channels[channel].mempool = memalign(AUDREN_MEMPOOL_ALIGNMENT, mempoolSize); 126 | 127 | if(sound->channels[channel].mempool == NULL) { 128 | return _LOCAL_RC_MAKE(Memory); 129 | } 130 | 131 | void* dataPtr = sound->channels[channel].mempool; 132 | AudioRendererAdpcmParameters* adpcmParameters = (AudioRendererAdpcmParameters*)(dataPtr + alignedDataSize); 133 | AudioRendererAdpcmContext* adpcmContext = (AudioRendererAdpcmContext*)(dataPtr + alignedDataSize + alignedAdpcmParametersSize); 134 | 135 | sound->channels[channel].wavebufs[0].data_raw = dataPtr; 136 | sound->channels[channel].wavebufs[0].size = dataSize; 137 | sound->channels[channel].wavebufs[0].end_sample_offset = loadInfo->sampleCount; 138 | sound->channels[channel].wavebufs[0].is_looping = loadInfo->looping; 139 | 140 | if(loadInfo->pcmFormat == PcmFormat_Adpcm) { 141 | memcpy(adpcmContext, &loadInfo->adpcm[channel].context, sizeof(AudioRendererAdpcmContext)); 142 | memcpy(adpcmParameters, &loadInfo->adpcm[channel].parameters, sizeof(AudioRendererAdpcmParameters)); 143 | 144 | sound->channels[channel].wavebufs[0].context_addr = adpcmContext; 145 | sound->channels[channel].wavebufs[0].context_sz = sizeof(AudioRendererAdpcmContext); 146 | audrvVoiceSetExtraParams(&player->driver, sound->channels[channel].voiceId, adpcmParameters, sizeof(AudioRendererAdpcmParameters)); 147 | } 148 | 149 | sound->channels[channel].mempoolId = audrvMemPoolAdd(&player->driver, sound->channels[channel].mempool, mempoolSize); 150 | audrvMemPoolAttach(&player->driver, sound->channels[channel].mempoolId); 151 | 152 | if(useSecondLoopWavebuf) { 153 | memcpy(&sound->channels[channel].wavebufs[1], &sound->channels[channel].wavebufs[0], sizeof(AudioDriverWaveBuf)); 154 | sound->channels[channel].wavebufs[0].end_sample_offset = loadInfo->loopStartSample - 1; 155 | sound->channels[channel].wavebufs[0].is_looping = false; 156 | sound->channels[channel].wavebufs[1].start_sample_offset = loadInfo->loopStartSample; 157 | } 158 | } 159 | 160 | switch(loadInfo->layout.type) { 161 | case PLSR_PlayerSoundLoadLayout_Channel: 162 | _LOCAL_TRY(_readChannel(loadInfo, &loadInfo->layout.channel, dataSize, sound)); 163 | break; 164 | case PLSR_PlayerSoundLoadLayout_Blocks: 165 | _LOCAL_TRY(_readBlocks(loadInfo, &loadInfo->layout.blocks, dataSize, sound)); 166 | break; 167 | default: 168 | return _LOCAL_RC_MAKE(Unsupported); 169 | } 170 | 171 | for(unsigned int channel = 0; channel < sound->channelCount; channel++) { 172 | armDCacheFlush(sound->channels[channel].mempool, mempoolSize); 173 | } 174 | 175 | audrvUpdate(&player->driver); 176 | 177 | return PLSR_RC_OK; 178 | } 179 | 180 | PLSR_RC plsrPlayerLoad(const PLSR_PlayerSoundLoadInfo* loadInfo, PLSR_PlayerSoundId* out) { 181 | PLSR_Player* player = plsrPlayerGetInstance(); 182 | if(player == NULL) { 183 | return _LOCAL_RC_MAKE(NotReady); 184 | } 185 | 186 | PLSR_PlayerSoundId id = NULL; 187 | PLSR_RC rc = _loadSoundFromInfo(player, loadInfo, &id); 188 | 189 | if(PLSR_RC_SUCCEEDED(rc)) { 190 | *out = id; 191 | } else { 192 | *out = NULL; 193 | plsrPlayerFree(id); 194 | } 195 | 196 | return rc; 197 | } 198 | 199 | #endif 200 | -------------------------------------------------------------------------------- /src/player/player_load_formats.c: -------------------------------------------------------------------------------- 1 | #ifdef __SWITCH__ 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define _LOCAL_TRY(X) PLSR_RC_LTRY(Player, LoadFormats, X) 11 | #define _LOCAL_NX_TRY(X) PLSR_RC_NX_LTRY(Player, LoadFormats, X) 12 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(Player, LoadFormats, X) 13 | 14 | PLSR_RC plsrPlayerLoadWave(const PLSR_BFWAV* bfwav, PLSR_PlayerSoundId* out) { 15 | PLSR_PlayerSoundLoadInfo loadInfo; 16 | PLSR_BFWAVInfo waveInfo; 17 | 18 | PLSR_RC_TRY(plsrBFWAVReadInfo(bfwav, &waveInfo)); 19 | 20 | switch(waveInfo.format) { 21 | case PLSR_BFWAVFormat_PCM_8: 22 | loadInfo.pcmFormat = PcmFormat_Int8; 23 | break; 24 | case PLSR_BFWAVFormat_PCM_16: 25 | loadInfo.pcmFormat = PcmFormat_Int16; 26 | break; 27 | case PLSR_BFWAVFormat_DSP_ADPCM: 28 | loadInfo.pcmFormat = PcmFormat_Adpcm; 29 | break; 30 | default: 31 | return _LOCAL_RC_MAKE(Unsupported); 32 | } 33 | 34 | loadInfo.layout.type = PLSR_PlayerSoundLoadLayout_Channel; 35 | 36 | loadInfo.ar = &bfwav->ar; 37 | loadInfo.looping = waveInfo.looping; 38 | loadInfo.sampleRate = waveInfo.sampleRate; 39 | loadInfo.sampleCount = waveInfo.sampleCount; 40 | loadInfo.channelCount = waveInfo.channelInfoTable.info.count > PLSR_PLAYER_MAX_CHANNELS ? PLSR_PLAYER_MAX_CHANNELS : waveInfo.channelInfoTable.info.count; 41 | loadInfo.dataSize = 0; 42 | 43 | if(waveInfo.looping) { 44 | loadInfo.loopStartSample = waveInfo.loopStartSample; 45 | } 46 | 47 | for(u32 channel = 0; channel < loadInfo.channelCount; channel++) { 48 | PLSR_BFWAVChannelInfo channelInfo; 49 | _LOCAL_TRY(plsrBFWAVReadChannelInfo(bfwav, &waveInfo.channelInfoTable, channel, &channelInfo)); 50 | 51 | loadInfo.layout.channel.offsets[channel] = channelInfo.dataOffset; 52 | 53 | if(waveInfo.format == PLSR_BFWAVFormat_DSP_ADPCM) { 54 | loadInfo.adpcm[channel].context.index = channelInfo.adpcmInfo.loop.header; 55 | loadInfo.adpcm[channel].context.history0 = channelInfo.adpcmInfo.loop.yn1; 56 | loadInfo.adpcm[channel].context.history1 = channelInfo.adpcmInfo.loop.yn2; 57 | 58 | // TODO: static assert libnx params = plsr adpcm coeffs 59 | memcpy(&loadInfo.adpcm[channel].parameters, &channelInfo.adpcmInfo.coeffs[0], sizeof(AudioRendererAdpcmParameters)); 60 | } 61 | } 62 | 63 | return plsrPlayerLoad(&loadInfo, out); 64 | } 65 | 66 | PLSR_RC plsrPlayerLoadStream(const PLSR_BFSTM* bfstm, PLSR_PlayerSoundId* out) { 67 | PLSR_PlayerSoundLoadInfo loadInfo; 68 | PLSR_BFSTMInfo streamInfo; 69 | 70 | PLSR_RC_TRY(plsrBFSTMReadInfo(bfstm, &streamInfo)); 71 | switch(streamInfo.format) { 72 | case PLSR_BFSTMFormat_PCM_8: 73 | loadInfo.pcmFormat = PcmFormat_Int8; 74 | break; 75 | case PLSR_BFSTMFormat_PCM_16: 76 | loadInfo.pcmFormat = PcmFormat_Int16; 77 | break; 78 | case PLSR_BFSTMFormat_DSP_ADPCM: 79 | loadInfo.pcmFormat = PcmFormat_Adpcm; 80 | break; 81 | default: 82 | return _LOCAL_RC_MAKE(Unsupported); 83 | } 84 | 85 | loadInfo.layout.type = PLSR_PlayerSoundLoadLayout_Blocks; 86 | loadInfo.layout.blocks.firstBlockOffset = streamInfo.dataOffset; 87 | loadInfo.layout.blocks.blockSize = streamInfo.blockSize; 88 | loadInfo.layout.blocks.lastBlockPadding = streamInfo.lastBlockSizeWithPadding - streamInfo.lastBlockSize; 89 | 90 | loadInfo.ar = &bfstm->ar; 91 | loadInfo.looping = streamInfo.looping; 92 | loadInfo.sampleRate = streamInfo.sampleRate; 93 | loadInfo.sampleCount = streamInfo.sampleCount; 94 | loadInfo.channelCount = plsrBFSTMChannelCount(bfstm); 95 | 96 | u32 fullBlockCount = streamInfo.blockCount > 1 ? streamInfo.blockCount - 1 : 1; 97 | loadInfo.dataSize = fullBlockCount * streamInfo.blockSize + streamInfo.lastBlockSize; 98 | 99 | if(loadInfo.looping) { 100 | loadInfo.loopStartSample = streamInfo.loopStartSample; 101 | } 102 | 103 | if(streamInfo.format == PLSR_BFSTMFormat_DSP_ADPCM) { 104 | for(u32 channel = 0; channel < plsrBFSTMChannelCount(bfstm) && channel < PLSR_PLAYER_MAX_CHANNELS; channel++) { 105 | PLSR_BFSTMChannelInfo channelInfo; 106 | _LOCAL_TRY(plsrBFSTMChannelGet(bfstm, channel, &channelInfo)); 107 | 108 | loadInfo.adpcm[channel].context.index = channelInfo.adpcmInfo.loop.header; 109 | loadInfo.adpcm[channel].context.history0 = channelInfo.adpcmInfo.loop.yn1; 110 | loadInfo.adpcm[channel].context.history1 = channelInfo.adpcmInfo.loop.yn2; 111 | 112 | // TODO: static assert libnx params = plsr adpcm coeffs 113 | memcpy(&loadInfo.adpcm[channel].parameters, &channelInfo.adpcmInfo.coeffs[0], sizeof(AudioRendererAdpcmParameters)); 114 | } 115 | } 116 | 117 | return plsrPlayerLoad(&loadInfo, out); 118 | } 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /src/player/player_load_lookup.c: -------------------------------------------------------------------------------- 1 | #ifdef __SWITCH__ 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define _LOCAL_RC_MAKE(X) PLSR_RC_MAKE(Player, LoadLookup, X) 18 | 19 | static PLSR_RC _loadWaveFromWAR(const PLSR_BFWAR* bfwar, u32 waveIndex, PLSR_PlayerSoundId* out) { 20 | PLSR_BFWARFileInfo waveFileInfo; 21 | PLSR_RC_TRY(plsrBFWARFileGet(bfwar, waveIndex, &waveFileInfo)); 22 | 23 | PLSR_BFWAV bfwav; 24 | PLSR_RC_TRY(plsrBFWAVOpenInside(&bfwar->ar, waveFileInfo.offset, &bfwav)); 25 | PLSR_RC rc = plsrPlayerLoadWave(&bfwav, out); 26 | plsrBFWAVClose(&bfwav); 27 | 28 | return rc; 29 | } 30 | 31 | static PLSR_RC _loadWaveFromWSD(const PLSR_BFSAR* bfsar, const PLSR_BFWSD* bfwsd, u32 waveIndex, PLSR_PlayerSoundId* out) { 32 | PLSR_BFWSDWaveId waveId; 33 | 34 | PLSR_BFWSDSoundDataInfo soundDataInfo; 35 | PLSR_RC_TRY(plsrBFWSDSoundDataGet(bfwsd, waveIndex, &soundDataInfo)); 36 | 37 | PLSR_BFWSDNoteInfo noteInfo; 38 | PLSR_RC_TRY(plsrBFWSDSoundDataNoteGet(bfwsd, &soundDataInfo.noteInfoTable, 0, ¬eInfo)); 39 | 40 | PLSR_RC_TRY(plsrBFWSDWaveIdListGetEntry(bfwsd, noteInfo.waveIdIndex, &waveId)); 41 | if(waveId.archiveItemId.type != PLSR_BFSARItemType_WaveArchive) { 42 | return _LOCAL_RC_MAKE(NotFound); 43 | } 44 | 45 | PLSR_BFSARWaveArchiveInfo waveArchiveInfo; 46 | PLSR_RC_TRY(plsrBFSARWaveArchiveGet(bfsar, waveId.archiveItemId.index, &waveArchiveInfo)); 47 | 48 | PLSR_BFWAR bfwar; 49 | PLSR_RC_TRY(plsrBFSARWaveArchiveOpen(bfsar, &waveArchiveInfo, &bfwar)); 50 | 51 | PLSR_RC rc = _loadWaveFromWAR(&bfwar, waveId.index, out); 52 | plsrBFWARClose(&bfwar); 53 | 54 | return rc; 55 | } 56 | 57 | static PLSR_RC _loadWaveFromArchive(const PLSR_BFSAR* bfsar, const PLSR_BFSARSoundInfo* soundInfo, const PLSR_BFSARFileInfo* soundFileInfo, PLSR_PlayerSoundId* out) { 58 | PLSR_BFWSD bfwsd; 59 | 60 | switch(soundFileInfo->type) { 61 | case PLSR_BFSARFileInfoType_External: 62 | PLSR_RC_TRY(plsrBFWSDOpen(soundFileInfo->external.path, &bfwsd)); 63 | break; 64 | case PLSR_BFSARFileInfoType_Internal: 65 | PLSR_RC_TRY(plsrBFWSDOpenInside(&bfsar->ar, soundFileInfo->internal.offset, &bfwsd)); 66 | break; 67 | default: 68 | return _LOCAL_RC_MAKE(Unsupported); 69 | } 70 | 71 | PLSR_RC rc = _loadWaveFromWSD(bfsar, &bfwsd, soundInfo->wave.index, out); 72 | plsrBFWSDClose(&bfwsd); 73 | 74 | return rc; 75 | } 76 | 77 | static PLSR_RC _loadStreamFromArchive(const PLSR_BFSAR* bfsar, const PLSR_BFSARSoundInfo* soundInfo, const PLSR_BFSARFileInfo* soundFileInfo, PLSR_PlayerSoundId* out) { 78 | PLSR_BFSTM bfstm; 79 | 80 | switch(soundFileInfo->type) { 81 | case PLSR_BFSARFileInfoType_External: 82 | PLSR_RC_TRY(plsrBFSTMOpen(soundFileInfo->external.path, &bfstm)); 83 | break; 84 | case PLSR_BFSARFileInfoType_Internal: 85 | PLSR_RC_TRY(plsrBFSTMOpenInside(&bfsar->ar, soundFileInfo->internal.offset, &bfstm)); 86 | break; 87 | default: 88 | return _LOCAL_RC_MAKE(Unsupported); 89 | } 90 | 91 | PLSR_RC rc = plsrPlayerLoadStream(&bfstm, out); 92 | plsrBFSTMClose(&bfstm); 93 | 94 | return rc; 95 | } 96 | 97 | PLSR_RC plsrPlayerLoadSoundByItemId(const PLSR_BFSAR* bfsar, PLSR_BFSARItemId itemId, PLSR_PlayerSoundId* out) { 98 | if(itemId.type != PLSR_BFSARItemType_Sound) { 99 | return _LOCAL_RC_MAKE(Unsupported); 100 | } 101 | 102 | PLSR_BFSARSoundInfo soundInfo; 103 | PLSR_RC_TRY(plsrBFSARSoundGet(bfsar, itemId.index, &soundInfo)); 104 | 105 | PLSR_BFSARFileInfo soundFileInfo; 106 | PLSR_RC_TRY(plsrBFSARFileScan(bfsar, soundInfo.fileIndex, &soundFileInfo)); 107 | PLSR_RC_TRY(plsrBFSARFileInfoNormalize(bfsar, &soundFileInfo)); 108 | 109 | switch(soundInfo.type) { 110 | case PLSR_BFSARSoundType_Wave: 111 | return _loadWaveFromArchive(bfsar, &soundInfo, &soundFileInfo, out); 112 | case PLSR_BFSARSoundType_Stream: 113 | return _loadStreamFromArchive(bfsar, &soundInfo, &soundFileInfo, out); 114 | default: 115 | return _LOCAL_RC_MAKE(Unsupported); 116 | } 117 | } 118 | 119 | PLSR_RC plsrPlayerLoadSoundByName(const PLSR_BFSAR* bfsar, const char* name, PLSR_PlayerSoundId* out) { 120 | PLSR_BFSARStringSearchInfo searchInfo; 121 | 122 | PLSR_RC_TRY(plsrBFSARStringSearch(bfsar, name, &searchInfo)); 123 | 124 | return plsrPlayerLoadSoundByItemId(bfsar, searchInfo.itemId, out); 125 | } 126 | 127 | #endif 128 | --------------------------------------------------------------------------------