├── .clang-format ├── .github └── workflows │ ├── nightly.yml │ └── release.yml ├── .gitignore ├── Makefile ├── arm7 ├── Makefile ├── fv_ds_arm7.ld ├── fv_ds_arm7.specs └── source │ ├── adpcm.h │ ├── adpcm.s │ ├── dldi.s │ ├── fat.c │ ├── fat.h │ ├── fat │ ├── diskio.c │ ├── diskio.h │ ├── ff.c │ ├── ff.h │ ├── ffconf.h │ ├── ffsystem.c │ └── ffunicode.c │ ├── fpsAdjust.c │ ├── fpsAdjust.h │ ├── fvPlayer7.c │ ├── fvPlayer7.h │ ├── irqWait.h │ ├── irqWait.s │ ├── isdprint.h │ └── main.c ├── arm9 ├── Makefile ├── data │ ├── RobotoMedium13.ntft │ └── RobotoRegular10.ntft ├── gfx │ ├── circle0.grit │ ├── circle0.png │ ├── circle1.grit │ ├── circle1.png │ ├── iconPause.grit │ ├── iconPause.png │ ├── iconPlay.grit │ ├── iconPlay.png │ ├── playBg.grit │ └── playBg.png └── source │ ├── FastVideo │ ├── fvConst.c │ ├── fvConst.h │ ├── fvConst_asm.s │ ├── fvDecoder.c │ ├── fvDecoder.h │ ├── fvDecoder_asm.s │ ├── fvMcData.h │ ├── fvMcData.s │ ├── fvPlayer.c │ └── fvPlayer.h │ ├── dldi_stub.s │ ├── gui │ ├── PlayerController.cpp │ ├── PlayerController.h │ ├── PlayerView.cpp │ ├── PlayerView.h │ └── core │ │ ├── InputProvider.h │ │ ├── InputRepeater.cpp │ │ ├── InputRepeater.h │ │ ├── NtftFont.cpp │ │ ├── NtftFont.h │ │ ├── OamManager.h │ │ ├── PadInputProvider.h │ │ ├── VramManager.h │ │ ├── uiUtil.cpp │ │ └── uiUtil.h │ ├── main.cpp │ ├── mpu.h │ └── mpu.s ├── common ├── fastVideo.h ├── ipc.h ├── twlwram.h └── twlwram.twl.c ├── icon.bmp └── readme.md /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Microsoft 2 | AlignConsecutiveMacros: 'true' 3 | AlignEscapedNewlines: Left 4 | AlwaysBreakTemplateDeclarations: 'Yes' 5 | BreakBeforeBraces: Allman 6 | Cpp11BracedListStyle: 'false' 7 | IndentCaseLabels: 'true' 8 | PointerAlignment: Left 9 | SortIncludes: 'false' -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | name: Build nightly 2 | 3 | on: 4 | push: 5 | branches: ["*"] 6 | paths-ignore: 7 | - 'README.md' 8 | pull_request: 9 | branches: ["*"] 10 | paths-ignore: 11 | - 'README.md' 12 | workflow_dispatch: 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | container: devkitpro/devkitarm 18 | name: Build with Docker using devkitARM 19 | steps: 20 | - name: Checkout repo 21 | uses: actions/checkout@v4 22 | - name: Make application 23 | run: | 24 | make 25 | - name: Publish build to GH Actions 26 | uses: actions/upload-artifact@v4 27 | with: 28 | path: FastVideoDS.nds 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | container: devkitpro/devkitarm 11 | name: Build with Docker using devkitARM 12 | steps: 13 | - name: Checkout repo 14 | uses: actions/checkout@v4 15 | - name: Make application 16 | run: | 17 | make 18 | - name: Publish build to GH Actions 19 | uses: actions/upload-artifact@v4 20 | with: 21 | path: FastVideoDS.nds 22 | 23 | - name: Release 24 | uses: softprops/action-gh-release@v1 25 | if: startsWith(github.ref, 'refs/tags/') 26 | with: 27 | files: | 28 | FastVideoDS.nds 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | tmp/ 3 | nitrofiles/ 4 | *.nds 5 | arm7/build/ 6 | arm9/build/ 7 | *.elf 8 | FatInNitroFS.dldi 9 | arm7/source/isdprint.c 10 | Makefile_isnitro -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITARM)),) 5 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 6 | endif 7 | 8 | export TARGET := FastVideoDS 9 | export TOPDIR := $(CURDIR) 10 | 11 | # specify a directory which contains the nitro filesystem 12 | # this is relative to the Makefile 13 | NITRO_FILES := #nitrofiles 14 | 15 | # These set the information text in the nds file 16 | GAME_TITLE := FastVideoDS Player 17 | GAME_SUBTITLE1 := By Gericom 18 | 19 | GAME_ICON := icon.bmp 20 | 21 | include $(DEVKITARM)/ds_rules 22 | 23 | .PHONY: checkarm7 checkarm9 clean 24 | 25 | #--------------------------------------------------------------------------------- 26 | # main targets 27 | #--------------------------------------------------------------------------------- 28 | all: checkarm7 checkarm9 $(TARGET).nds 29 | 30 | #--------------------------------------------------------------------------------- 31 | checkarm7: 32 | $(MAKE) -C arm7 33 | 34 | #--------------------------------------------------------------------------------- 35 | checkarm9: 36 | $(MAKE) -C arm9 37 | 38 | #--------------------------------------------------------------------------------- 39 | $(TARGET).nds : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf 40 | ndstool -c $(TARGET).nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \ 41 | -b $(GAME_ICON) "$(GAME_TITLE);$(GAME_SUBTITLE1)" \ 42 | $(_ADDFILES) 43 | 44 | #--------------------------------------------------------------------------------- 45 | arm7/$(TARGET).elf: 46 | $(MAKE) -C arm7 47 | 48 | #--------------------------------------------------------------------------------- 49 | arm9/$(TARGET).elf: 50 | $(MAKE) -C arm9 51 | 52 | #--------------------------------------------------------------------------------- 53 | clean: 54 | $(MAKE) -C arm9 clean 55 | $(MAKE) -C arm7 clean 56 | rm -f $(TARGET).nds $(TARGET).arm7 $(TARGET).arm9 57 | -------------------------------------------------------------------------------- /arm7/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITARM)),) 5 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 6 | endif 7 | 8 | include $(DEVKITARM)/ds_rules 9 | 10 | #--------------------------------------------------------------------------------- 11 | # BUILD is the directory where object files & intermediate files will be placed 12 | # SOURCES is a list of directories containing source code 13 | # INCLUDES is a list of directories containing extra header files 14 | # DATA is a list of directories containing binary files 15 | # all directories are relative to this makefile 16 | #--------------------------------------------------------------------------------- 17 | BUILD := build 18 | SOURCES := source source/fat ../common 19 | INCLUDES := include build 20 | DATA := 21 | 22 | #--------------------------------------------------------------------------------- 23 | # options for code generation 24 | #--------------------------------------------------------------------------------- 25 | ARCH := -mthumb-interwork -mcpu=arm7tdmi -mtune=arm7tdmi 26 | 27 | CFLAGS := -g -Wall -O2 -fomit-frame-pointer -ffast-math -ffunction-sections -fdata-sections $(ARCH) 28 | 29 | CFLAGS += $(INCLUDE) -DARM7 30 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -fno-rtti 31 | 32 | 33 | ASFLAGS := -g $(ARCH) 34 | LDFLAGS = -specs=../fv_ds_arm7.specs -g $(ARCH) -Wl,--nmagic -Wl,-Map,$(notdir $*).map 35 | 36 | LIBS := -lnds7 37 | 38 | #--------------------------------------------------------------------------------- 39 | # list of directories containing libraries, this must be the top level containing 40 | # include and lib 41 | #--------------------------------------------------------------------------------- 42 | LIBDIRS := $(LIBNDS) 43 | 44 | 45 | #--------------------------------------------------------------------------------- 46 | # no real need to edit anything past this point unless you need to add additional 47 | # rules for different file extensions 48 | #--------------------------------------------------------------------------------- 49 | ifneq ($(BUILD),$(notdir $(CURDIR))) 50 | #--------------------------------------------------------------------------------- 51 | 52 | export ARM7ELF := $(CURDIR)/$(TARGET).elf 53 | export DEPSDIR := $(CURDIR)/$(BUILD) 54 | 55 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) 56 | 57 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 58 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 59 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 60 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 61 | 62 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 63 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 64 | 65 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 66 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 67 | -I$(CURDIR)/$(BUILD) 68 | 69 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 70 | 71 | #--------------------------------------------------------------------------------- 72 | # use CXX for linking C++ projects, CC for standard C 73 | #--------------------------------------------------------------------------------- 74 | ifeq ($(strip $(CPPFILES)),) 75 | #--------------------------------------------------------------------------------- 76 | export LD := $(CC) 77 | #--------------------------------------------------------------------------------- 78 | else 79 | #--------------------------------------------------------------------------------- 80 | export LD := $(CXX) 81 | #--------------------------------------------------------------------------------- 82 | endif 83 | #--------------------------------------------------------------------------------- 84 | 85 | .PHONY: $(BUILD) clean 86 | 87 | #--------------------------------------------------------------------------------- 88 | $(BUILD): 89 | @[ -d $@ ] || mkdir -p $@ 90 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 91 | 92 | #--------------------------------------------------------------------------------- 93 | clean: 94 | @echo clean ... 95 | @rm -fr $(BUILD) *.elf 96 | 97 | 98 | #--------------------------------------------------------------------------------- 99 | else 100 | 101 | DEPENDS := $(OFILES:.o=.d) 102 | 103 | #--------------------------------------------------------------------------------- 104 | # main targets 105 | #--------------------------------------------------------------------------------- 106 | $(ARM7ELF) : $(OFILES) 107 | @echo linking $(notdir $@) 108 | @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ 109 | 110 | 111 | #--------------------------------------------------------------------------------- 112 | # you need a rule like this for each extension you use as binary data 113 | #--------------------------------------------------------------------------------- 114 | %.bin.o : %.bin 115 | #--------------------------------------------------------------------------------- 116 | @echo $(notdir $<) 117 | @$(bin2o) 118 | 119 | -include $(DEPENDS) 120 | 121 | #--------------------------------------------------------------------------------------- 122 | endif 123 | #--------------------------------------------------------------------------------------- 124 | -------------------------------------------------------------------------------- /arm7/fv_ds_arm7.ld: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------- 2 | This Source Code Form is subject to the terms of the Mozilla Public License, 3 | v. 2.0. If a copy of the MPL was not distributed with this file, You can 4 | obtain one at https://mozilla.org/MPL/2.0/. 5 | --------------------------------------------------------------------------------*/ 6 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 7 | OUTPUT_ARCH(arm) 8 | ENTRY(_start) 9 | 10 | 11 | PHDRS { 12 | crt0 PT_LOAD FLAGS(7); 13 | arm7 PT_LOAD FLAGS(7); 14 | arm7i PT_LOAD FLAGS(0x100007); 15 | } 16 | 17 | 18 | MEMORY { 19 | ewram : ORIGIN = 0x02380000, LENGTH = 12M - 512K 20 | rom : ORIGIN = 0x08000000, LENGTH = 32M 21 | /* iwram : ORIGIN = 0x037f8000, LENGTH = 96K */ 22 | iwram : ORIGIN = 0x037fc000, LENGTH = (96K - 16K) 23 | 24 | twl_ewram : ORIGIN = 0x02e80000, LENGTH = 512K - 64K 25 | twl_iwram : ORIGIN = 0x03000000, LENGTH = 256K 26 | } 27 | 28 | __iwram_start = ORIGIN(iwram); 29 | __iwram_top = ORIGIN(iwram)+ LENGTH(iwram); 30 | 31 | __sp_irq = __iwram_top - 0x100; 32 | __sp_svc = __sp_irq - 0x100; 33 | __sp_usr = __sp_svc - 0x100; 34 | 35 | __irq_flags = 0x04000000 - 8; 36 | __irq_flagsaux = 0x04000000 - 0x40; 37 | __irq_vector = 0x04000000 - 4; 38 | 39 | SECTIONS 40 | { 41 | 42 | .twl : 43 | { 44 | __arm7i_lma__ = LOADADDR(.twl); 45 | __arm7i_start__ = .; 46 | *(.twl) 47 | *.twl*(.text .stub .text.* .gnu.linkonce.t.*) 48 | *.twl*(.rodata) 49 | *.twl*(.roda) 50 | *.twl*(.rodata.*) 51 | *.twl*(.data) 52 | *.twl*(.data.*) 53 | *.twl*(.gnu.linkonce.d*) 54 | . = ALIGN(4); 55 | __arm7i_end__ = .; 56 | } >twl_iwram AT>twl_ewram :arm7i 57 | 58 | .twl_bss ALIGN(4) (NOLOAD) : 59 | { 60 | __twl_bss_start__ = .; 61 | *(.twl_bss) 62 | *.twl.*(.dynbss) 63 | *.twl.*(.gnu.linkonce.b*) 64 | *.twl.*(.bss*) 65 | *.twl.*(COMMON) 66 | . = ALIGN(4); 67 | __twl_bss_end__ = .; 68 | } >twl_iwram :NONE 69 | 70 | .crt0 : 71 | { 72 | KEEP (*(.crt0)) 73 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 74 | } >ewram :crt0 75 | 76 | .text : 77 | { 78 | __arm7_lma__ = LOADADDR(.text); 79 | __arm7_start__ = .; 80 | KEEP (*(SORT_NONE(.init))) 81 | *(.plt) 82 | *(.text .stub .text.* .gnu.linkonce.t.*) 83 | KEEP (*(.text.*personality*)) 84 | /* .gnu.warning sections are handled specially by elf32.em. */ 85 | *(.gnu.warning) 86 | *(.glue_7t) *(.glue_7) *(.vfp11_veneer) 87 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 88 | } >iwram AT>ewram :arm7 89 | 90 | .fini : 91 | { 92 | KEEP (*(.fini)) 93 | } >iwram AT>ewram 94 | 95 | .rodata : 96 | { 97 | *(.rodata) 98 | *all.rodata*(*) 99 | *(.roda) 100 | *(.rodata.*) 101 | *(.gnu.linkonce.r*) 102 | SORT(CONSTRUCTORS) 103 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 104 | } >iwram AT>ewram 105 | 106 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >iwram AT>ewram 107 | 108 | .ARM.exidx : { 109 | __exidx_start = .; 110 | *(.ARM.exidx* .gnu.linkonce.armexidx.*) 111 | __exidx_end = .; 112 | } >iwram AT>ewram 113 | 114 | /* Ensure the __preinit_array_start label is properly aligned. We 115 | could instead move the label definition inside the section, but 116 | the linker would then create the section even if it turns out to 117 | be empty, which isn't pretty. */ 118 | .preinit_array : { 119 | . = ALIGN(32 / 8); 120 | PROVIDE (__preinit_array_start = .); 121 | KEEP (*(.preinit_array)) 122 | PROVIDE (__preinit_array_end = .); 123 | } >iwram AT>ewram 124 | 125 | .init_array : { 126 | PROVIDE (__init_array_start = .); 127 | KEEP (*(.init_array)) 128 | PROVIDE (__init_array_end = .); 129 | } >iwram AT>ewram 130 | 131 | .fini_array : { 132 | PROVIDE (__fini_array_start = .); 133 | KEEP (*(.fini_array)) 134 | PROVIDE (__fini_array_end = .); 135 | } >iwram AT>ewram 136 | 137 | .ctors : 138 | { 139 | /* gcc uses crtbegin.o to find the start of the constructors, so 140 | we make sure it is first. Because this is a wildcard, it 141 | doesn't matter if the user does not actually link against 142 | crtbegin.o; the linker won't look for a file to match a 143 | wildcard. The wildcard also means that it doesn't matter which 144 | directory crtbegin.o is in. */ 145 | KEEP (*crtbegin.o(.ctors)) 146 | KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) 147 | KEEP (*(SORT(.ctors.*))) 148 | KEEP (*(.ctors)) 149 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 150 | } >iwram AT>ewram 151 | 152 | .dtors : 153 | { 154 | KEEP (*crtbegin.o(.dtors)) 155 | KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) 156 | KEEP (*(SORT(.dtors.*))) 157 | KEEP (*(.dtors)) 158 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 159 | } >iwram AT>ewram 160 | 161 | .eh_frame : 162 | { 163 | KEEP (*(.eh_frame)) 164 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 165 | } >iwram AT>ewram 166 | 167 | .gcc_except_table : 168 | { 169 | *(.gcc_except_table) 170 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 171 | } >iwram AT>ewram 172 | .jcr : { KEEP (*(.jcr)) } >iwram AT>ewram 173 | .got : { *(.got.plt) *(.got) } >iwram AT>ewram 174 | 175 | .data ALIGN(4) : { 176 | __data_start = ABSOLUTE(.); 177 | *(.data) 178 | *(.data.*) 179 | *(.gnu.linkonce.d*) 180 | CONSTRUCTORS 181 | . = ALIGN(4); 182 | __data_end = ABSOLUTE(.) ; 183 | } >iwram AT>ewram 184 | 185 | .bss ALIGN(4) (NOLOAD) : 186 | { 187 | __arm7_end__ = .; 188 | __bss_start = ABSOLUTE(.); 189 | __bss_start__ = ABSOLUTE(.); 190 | *(.dynbss) 191 | *(.gnu.linkonce.b*) 192 | *(.bss*) 193 | *(COMMON) 194 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 195 | __bss_end__ = ABSOLUTE(.); 196 | __end__ = ABSOLUTE(.); 197 | } >iwram 198 | 199 | /* Stabs debugging sections. */ 200 | .stab 0 : { *(.stab) } 201 | .stabstr 0 : { *(.stabstr) } 202 | .stab.excl 0 : { *(.stab.excl) } 203 | .stab.exclstr 0 : { *(.stab.exclstr) } 204 | .stab.index 0 : { *(.stab.index) } 205 | .stab.indexstr 0 : { *(.stab.indexstr) } 206 | .comment 0 : { *(.comment) } 207 | /* DWARF debug sections. 208 | Symbols in the DWARF debugging sections are relative to the beginning 209 | of the section so we begin them at 0. */ 210 | /* DWARF 1 */ 211 | .debug 0 : { *(.debug) } 212 | .line 0 : { *(.line) } 213 | /* GNU DWARF 1 extensions */ 214 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 215 | .debug_sfnames 0 : { *(.debug_sfnames) } 216 | /* DWARF 1.1 and DWARF 2 */ 217 | .debug_aranges 0 : { *(.debug_aranges) } 218 | .debug_pubnames 0 : { *(.debug_pubnames) } 219 | /* DWARF 2 */ 220 | .debug_info 0 : { *(.debug_info) } 221 | .debug_abbrev 0 : { *(.debug_abbrev) } 222 | .debug_line 0 : { *(.debug_line) } 223 | .debug_frame 0 : { *(.debug_frame) } 224 | .debug_str 0 : { *(.debug_str) } 225 | .debug_loc 0 : { *(.debug_loc) } 226 | .debug_macinfo 0 : { *(.debug_macinfo) } 227 | /* SGI/MIPS DWARF 2 extensions */ 228 | .debug_weaknames 0 : { *(.debug_weaknames) } 229 | .debug_funcnames 0 : { *(.debug_funcnames) } 230 | .debug_typenames 0 : { *(.debug_typenames) } 231 | .debug_varnames 0 : { *(.debug_varnames) } 232 | /* These must appear regardless of . */ 233 | } -------------------------------------------------------------------------------- /arm7/fv_ds_arm7.specs: -------------------------------------------------------------------------------- 1 | %rename link old_link 2 | 3 | *link: 4 | %(old_link) -T ../fv_ds_arm7.ld%s --gc-sections 5 | 6 | *startfile: 7 | ds_arm7_crt0%O%s crti%O%s crtbegin%O%s 8 | 9 | -------------------------------------------------------------------------------- /arm7/source/adpcm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern void adpcm_decompress(const void* src, u32 len, s16* dst); -------------------------------------------------------------------------------- /arm7/source/adpcm.s: -------------------------------------------------------------------------------- 1 | .text 2 | .arm 3 | 4 | indexTable: 5 | @ .byte 0, -1, 0, -1, 0, -1, 0, -1, 0, 2, 0, 4, 0, 6, 0, 8 6 | .byte 0, -3, 0, -3, 0, -2, 0, -1, 0, 2, 0, 4, 0, 6, 0, 8 7 | 8 | stepTable: 9 | .word 7, 8, 9, 10, 11, 12, 13, 14 10 | .word 16, 17, 19, 21, 23, 25, 28 11 | .word 31, 34, 37, 41, 45, 50, 55 12 | .word 60, 66, 73, 80, 88, 97, 107 13 | .word 118, 130, 143, 157, 173, 190, 209 14 | .word 230, 253, 279, 307, 337, 371, 408 15 | .word 449, 494, 544, 598, 658, 724, 796 16 | .word 876, 963, 1060, 1166, 1282, 1411, 1552 17 | .word 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026 18 | .word 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630 19 | .word 9493, 10442, 11487, 12635, 13899, 15289, 16818 20 | .word 18500, 20350, 22385, 24623, 27086, 29794, 32767 21 | 22 | //r0: src 23 | //r1: length 24 | //r2: dst 25 | .global adpcm_decompress 26 | adpcm_decompress: 27 | stmfd sp!, {r4-r11,lr} 28 | add r12, r0, r1 29 | ldr r11,= indexTable 30 | ldr r10,= stepTable 31 | mov r9, #1 32 | ldr r8,= 0x7FFF 33 | ldrsh r3, [r0], #2 //last 34 | ldrh r4, [r0], #2 //index 35 | 1: 36 | ldr r5, [r0], #4 37 | .set offs, 0 38 | .rept 8 39 | ldr r6, [r10, r4, lsl #2] 40 | movs r7, r5, lsl #(29 - offs) 41 | orr r7, r9, r7, lsr #28 42 | ldrsb lr, [r11, r7] 43 | mul r6, r7, r6 44 | addcc r3, r3, r6, lsr #3 45 | subcs r3, r3, r6, lsr #3 46 | @ mov r6, r3, asr #15 47 | @ teq r6, r3, asr #31 48 | @ eorne r3, r8, r3, asr #31 49 | cmp r3, r8 50 | movgt r3, r8 51 | cmn r3, r8 52 | rsblt r3, r8, #0 53 | strh r3, [r2], #2 54 | adds r4, r4, lr 55 | movmi r4, #0 56 | cmp r4, #88 57 | movgt r4, #88 58 | .set offs, offs + 4 59 | .endr 60 | cmp r0, r12 61 | blo 1b 62 | ldmfd sp!, {r4-r11,lr} 63 | bx lr -------------------------------------------------------------------------------- /arm7/source/dldi.s: -------------------------------------------------------------------------------- 1 | .global _dldi_start 2 | .equ _dldi_start, 0x037F8000 3 | .global _io_dldi 4 | .equ _io_dldi, (_dldi_start + 0x60) 5 | .global _DLDI_startup_ptr 6 | .equ _DLDI_startup_ptr, (_io_dldi + 0x8) 7 | .global _DLDI_isInserted_ptr 8 | .equ _DLDI_isInserted_ptr, (_io_dldi + 0xC) 9 | .global _DLDI_readSectors_ptr 10 | .equ _DLDI_readSectors_ptr, (_io_dldi + 0x10) 11 | .global _DLDI_writeSectors_ptr 12 | .equ _DLDI_writeSectors_ptr, (_io_dldi + 0x14) 13 | .global _DLDI_clearStatus_ptr 14 | .equ _DLDI_clearStatus_ptr, (_io_dldi + 0x18) 15 | .global _DLDI_shutdown_ptr 16 | .equ _DLDI_shutdown_ptr, (_io_dldi + 0x1C) -------------------------------------------------------------------------------- /arm7/source/fat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "fat/ff.h" 4 | #include "fat.h" 5 | 6 | FATFS gFatFs; 7 | 8 | void fat_init(void) 9 | { 10 | memset(&gFatFs, 0, sizeof(gFatFs)); 11 | 12 | if (isDSiMode()) 13 | f_mount(&gFatFs, "sd:", 1); //mount dsi sd card 14 | } 15 | 16 | void fat_mountDldi(void) 17 | { 18 | f_mount(&gFatFs, "fat:", 1); 19 | } 20 | -------------------------------------------------------------------------------- /arm7/source/fat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "fat/ff.h" 3 | 4 | extern FATFS gFatFs; 5 | 6 | void fat_init(void); 7 | void fat_mountDldi(void); 8 | -------------------------------------------------------------------------------- /arm7/source/fat/diskio.c: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------*/ 2 | /* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */ 3 | /*-----------------------------------------------------------------------*/ 4 | /* If a working storage control module is available, it should be */ 5 | /* attached to the FatFs via a glue function rather than modifying it. */ 6 | /* This is an example of glue functions to attach various exsisting */ 7 | /* storage control modules to the FatFs module with a defined API. */ 8 | /*-----------------------------------------------------------------------*/ 9 | 10 | #include 11 | #include 12 | #include "ff.h" /* Obtains integer types */ 13 | #include "diskio.h" /* Declarations of disk functions */ 14 | 15 | /* Definitions of physical drive number for each drive */ 16 | #define DEV_FAT 0 //dldi 17 | #define DEV_SD 1 //dsi sd 18 | 19 | 20 | /*-----------------------------------------------------------------------*/ 21 | /* Get Drive Status */ 22 | /*-----------------------------------------------------------------------*/ 23 | 24 | DSTATUS disk_status ( 25 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 26 | ) 27 | { 28 | return 0; 29 | } 30 | 31 | extern FN_MEDIUM_STARTUP _DLDI_startup_ptr; 32 | extern FN_MEDIUM_READSECTORS _DLDI_readSectors_ptr; 33 | 34 | /*-----------------------------------------------------------------------*/ 35 | /* Inidialize a Drive */ 36 | /*-----------------------------------------------------------------------*/ 37 | 38 | void sdmmc_sd_startup(); 39 | 40 | DSTATUS disk_initialize ( 41 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 42 | ) 43 | { 44 | if (pdrv == DEV_FAT) 45 | { 46 | _DLDI_startup_ptr(); 47 | return 0; 48 | } 49 | else if (isDSiMode() && pdrv == DEV_SD) 50 | { 51 | sdmmc_sd_startup(); 52 | return 0; 53 | } 54 | 55 | return STA_NOINIT; 56 | } 57 | 58 | 59 | 60 | /*-----------------------------------------------------------------------*/ 61 | /* Read Sector(s) */ 62 | /*-----------------------------------------------------------------------*/ 63 | 64 | DRESULT disk_read ( 65 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 66 | BYTE *buff, /* Data buffer to store read data */ 67 | DWORD sector, /* Start sector in LBA */ 68 | UINT count /* Number of sectors to read */ 69 | ) 70 | { 71 | if (pdrv == DEV_FAT) 72 | { 73 | _DLDI_readSectors_ptr(sector, count, buff); 74 | return RES_OK; 75 | } 76 | else if (isDSiMode() && pdrv == DEV_SD) 77 | { 78 | sdmmc_sdcard_readsectors(sector, count, buff); 79 | return RES_OK; 80 | } 81 | 82 | return RES_PARERR; 83 | } 84 | 85 | 86 | 87 | /*-----------------------------------------------------------------------*/ 88 | /* Write Sector(s) */ 89 | /*-----------------------------------------------------------------------*/ 90 | 91 | #if FF_FS_READONLY == 0 92 | 93 | DRESULT disk_write ( 94 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 95 | const BYTE *buff, /* Data to be written */ 96 | DWORD sector, /* Start sector in LBA */ 97 | UINT count /* Number of sectors to write */ 98 | ) 99 | { 100 | DRESULT res; 101 | int result; 102 | 103 | /*switch (pdrv) { 104 | case DEV_RAM : 105 | // translate the arguments here 106 | 107 | result = RAM_disk_write(buff, sector, count); 108 | 109 | // translate the reslut code here 110 | 111 | return res; 112 | 113 | case DEV_MMC : 114 | // translate the arguments here 115 | 116 | result = MMC_disk_write(buff, sector, count); 117 | 118 | // translate the reslut code here 119 | 120 | return res; 121 | 122 | case DEV_USB : 123 | // translate the arguments here 124 | 125 | result = USB_disk_write(buff, sector, count); 126 | 127 | // translate the reslut code here 128 | 129 | return res; 130 | }*/ 131 | write_sd_sectors_safe(sector, count, buff); 132 | 133 | return RES_OK; 134 | } 135 | 136 | #endif 137 | 138 | 139 | /*-----------------------------------------------------------------------*/ 140 | /* Miscellaneous Functions */ 141 | /*-----------------------------------------------------------------------*/ 142 | 143 | DRESULT disk_ioctl ( 144 | BYTE pdrv, /* Physical drive nmuber (0..) */ 145 | BYTE cmd, /* Control code */ 146 | void *buff /* Buffer to send/receive control data */ 147 | ) 148 | { 149 | return RES_OK; 150 | } 151 | 152 | -------------------------------------------------------------------------------- /arm7/source/fat/diskio.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level disk interface modlue include file (C)ChaN, 2014 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /* Status of Disk Functions */ 13 | typedef BYTE DSTATUS; 14 | 15 | /* Results of Disk Functions */ 16 | typedef enum { 17 | RES_OK = 0, /* 0: Successful */ 18 | RES_ERROR, /* 1: R/W Error */ 19 | RES_WRPRT, /* 2: Write Protected */ 20 | RES_NOTRDY, /* 3: Not Ready */ 21 | RES_PARERR /* 4: Invalid Parameter */ 22 | } DRESULT; 23 | 24 | 25 | /*---------------------------------------*/ 26 | /* Prototypes for disk control functions */ 27 | 28 | 29 | DSTATUS disk_initialize (BYTE pdrv); 30 | DSTATUS disk_status (BYTE pdrv); 31 | DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); 32 | DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); 33 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); 34 | 35 | 36 | /* Disk Status Bits (DSTATUS) */ 37 | 38 | #define STA_NOINIT 0x01 /* Drive not initialized */ 39 | #define STA_NODISK 0x02 /* No medium in the drive */ 40 | #define STA_PROTECT 0x04 /* Write protected */ 41 | 42 | 43 | /* Command code for disk_ioctrl fucntion */ 44 | 45 | /* Generic command (Used by FatFs) */ 46 | #define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */ 47 | #define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */ 48 | #define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */ 49 | #define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */ 50 | #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ 51 | 52 | /* Generic command (Not used by FatFs) */ 53 | #define CTRL_POWER 5 /* Get/Set power status */ 54 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 55 | #define CTRL_EJECT 7 /* Eject media */ 56 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 57 | 58 | /* MMC/SDC specific ioctl command */ 59 | #define MMC_GET_TYPE 10 /* Get card type */ 60 | #define MMC_GET_CSD 11 /* Get CSD */ 61 | #define MMC_GET_CID 12 /* Get CID */ 62 | #define MMC_GET_OCR 13 /* Get OCR */ 63 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 64 | #define ISDIO_READ 55 /* Read data form SD iSDIO register */ 65 | #define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ 66 | #define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ 67 | 68 | /* ATA/CF specific ioctl command */ 69 | #define ATA_GET_REV 20 /* Get F/W revision */ 70 | #define ATA_GET_MODEL 21 /* Get model name */ 71 | #define ATA_GET_SN 22 /* Get serial number */ 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /arm7/source/fat/ff.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------/ 2 | / FatFs - Generic FAT Filesystem module R0.13c / 3 | /-----------------------------------------------------------------------------/ 4 | / 5 | / Copyright (C) 2018, ChaN, all right reserved. 6 | / 7 | / FatFs module is an open source software. Redistribution and use of FatFs in 8 | / source and binary forms, with or without modification, are permitted provided 9 | / that the following condition is met: 10 | 11 | / 1. Redistributions of source code must retain the above copyright notice, 12 | / this condition and the following disclaimer. 13 | / 14 | / This software is provided by the copyright holder and contributors "AS IS" 15 | / and any warranties related to this software are DISCLAIMED. 16 | / The copyright owner or contributors be NOT LIABLE for any damages caused 17 | / by use of this software. 18 | / 19 | /----------------------------------------------------------------------------*/ 20 | 21 | 22 | #ifndef FF_DEFINED 23 | #define FF_DEFINED 86604 /* Revision ID */ 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include "ffconf.h" /* FatFs configuration options */ 30 | 31 | #if FF_DEFINED != FFCONF_DEF 32 | #error Wrong configuration file (ffconf.h). 33 | #endif 34 | 35 | 36 | /* Integer types used for FatFs API */ 37 | 38 | #if defined(_WIN32) /* Main development platform */ 39 | #define FF_INTDEF 2 40 | #include 41 | typedef unsigned __int64 QWORD; 42 | #elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */ 43 | #define FF_INTDEF 2 44 | #include 45 | typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ 46 | typedef unsigned char BYTE; /* char must be 8-bit */ 47 | typedef uint16_t WORD; /* 16-bit unsigned integer */ 48 | typedef uint16_t WCHAR; /* 16-bit unsigned integer */ 49 | typedef uint32_t DWORD; /* 32-bit unsigned integer */ 50 | typedef uint64_t QWORD; /* 64-bit unsigned integer */ 51 | #else /* Earlier than C99 */ 52 | #define FF_INTDEF 1 53 | typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ 54 | typedef unsigned char BYTE; /* char must be 8-bit */ 55 | typedef unsigned short WORD; /* 16-bit unsigned integer */ 56 | typedef unsigned short WCHAR; /* 16-bit unsigned integer */ 57 | typedef unsigned long DWORD; /* 32-bit unsigned integer */ 58 | #endif 59 | 60 | #include "math.h" 61 | 62 | /* Definitions of volume management */ 63 | 64 | #if FF_MULTI_PARTITION /* Multiple partition configuration */ 65 | typedef struct { 66 | BYTE pd; /* Physical drive number */ 67 | BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ 68 | } PARTITION; 69 | extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ 70 | #endif 71 | 72 | #if FF_STR_VOLUME_ID 73 | #ifndef FF_VOLUME_STRS 74 | extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ 75 | #endif 76 | #endif 77 | 78 | 79 | 80 | /* Type of path name strings on FatFs API */ 81 | 82 | #ifndef _INC_TCHAR 83 | #define _INC_TCHAR 84 | 85 | #if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ 86 | typedef WCHAR TCHAR; 87 | #define _T(x) L ## x 88 | #define _TEXT(x) L ## x 89 | #elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */ 90 | typedef char TCHAR; 91 | #define _T(x) u8 ## x 92 | #define _TEXT(x) u8 ## x 93 | #elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */ 94 | typedef DWORD TCHAR; 95 | #define _T(x) U ## x 96 | #define _TEXT(x) U ## x 97 | #elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) 98 | #error Wrong FF_LFN_UNICODE setting 99 | #else /* ANSI/OEM code in SBCS/DBCS */ 100 | typedef char TCHAR; 101 | #define _T(x) x 102 | #define _TEXT(x) x 103 | #endif 104 | 105 | #endif 106 | 107 | 108 | 109 | /* Type of file size variables */ 110 | 111 | #if FF_FS_EXFAT 112 | #if FF_INTDEF != 2 113 | #error exFAT feature wants C99 or later 114 | #endif 115 | typedef QWORD FSIZE_t; 116 | #else 117 | typedef DWORD FSIZE_t; 118 | #endif 119 | 120 | 121 | 122 | /* Filesystem object structure (FATFS) */ 123 | 124 | typedef struct { 125 | BYTE fs_type; /* Filesystem type (0:not mounted) */ 126 | BYTE pdrv; /* Associated physical drive */ 127 | BYTE n_fats; /* Number of FATs (1 or 2) */ 128 | BYTE wflag; /* win[] flag (b0:dirty) */ 129 | BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ 130 | WORD id; /* Volume mount ID */ 131 | WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ 132 | WORD csize; /* Cluster size [sectors] */ 133 | #if FF_MAX_SS != FF_MIN_SS 134 | WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ 135 | #endif 136 | #if FF_USE_LFN 137 | WCHAR* lfnbuf; /* LFN working buffer */ 138 | #endif 139 | #if FF_FS_EXFAT 140 | BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */ 141 | #endif 142 | #if FF_FS_REENTRANT 143 | FF_SYNC_t sobj; /* Identifier of sync object */ 144 | #endif 145 | #if !FF_FS_READONLY 146 | DWORD last_clst; /* Last allocated cluster */ 147 | DWORD free_clst; /* Number of free clusters */ 148 | #endif 149 | #if FF_FS_RPATH 150 | DWORD cdir; /* Current directory start cluster (0:root) */ 151 | #if FF_FS_EXFAT 152 | DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ 153 | DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ 154 | DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ 155 | #endif 156 | #endif 157 | DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ 158 | DWORD fsize; /* Size of an FAT [sectors] */ 159 | DWORD volbase; /* Volume base sector */ 160 | DWORD fatbase; /* FAT base sector */ 161 | DWORD dirbase; /* Root directory base sector/cluster */ 162 | DWORD database; /* Data base sector */ 163 | #if FF_FS_EXFAT 164 | DWORD bitbase; /* Allocation bitmap base sector */ 165 | #endif 166 | DWORD winsect; /* Current sector appearing in the win[] */ 167 | BYTE win[FF_MAX_SS] __attribute__((aligned(32))); /* Disk access window for Directory, FAT (and file data at tiny cfg) */ 168 | } FATFS; 169 | 170 | 171 | 172 | /* Object ID and allocation information (FFOBJID) */ 173 | 174 | typedef struct { 175 | FATFS* fs; /* Pointer to the hosting volume of this object */ 176 | WORD id; /* Hosting volume mount ID */ 177 | BYTE attr; /* Object attribute */ 178 | BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */ 179 | DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */ 180 | FSIZE_t objsize; /* Object size (valid when sclust != 0) */ 181 | #if FF_FS_EXFAT 182 | DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */ 183 | DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */ 184 | DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ 185 | DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ 186 | DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */ 187 | #endif 188 | #if FF_FS_LOCK 189 | UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ 190 | #endif 191 | } FFOBJID; 192 | 193 | 194 | 195 | /* File object structure (FIL) */ 196 | 197 | typedef struct { 198 | FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ 199 | BYTE flag; /* File status flags */ 200 | BYTE err; /* Abort flag (error code) */ 201 | FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ 202 | DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */ 203 | DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ 204 | #if !FF_FS_READONLY 205 | DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */ 206 | BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */ 207 | #endif 208 | #if FF_USE_FASTSEEK 209 | DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ 210 | #endif 211 | #if !FF_FS_TINY 212 | BYTE buf[FF_MAX_SS] __attribute__((aligned(32))); /* File private data read/write window */ 213 | #endif 214 | } FIL; 215 | 216 | 217 | 218 | /* Directory object structure (DIR) */ 219 | 220 | typedef struct { 221 | FFOBJID obj; /* Object identifier */ 222 | DWORD dptr; /* Current read/write offset */ 223 | DWORD clust; /* Current cluster */ 224 | DWORD sect; /* Current sector (0:Read operation has terminated) */ 225 | BYTE* dir; /* Pointer to the directory item in the win[] */ 226 | BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ 227 | #if FF_USE_LFN 228 | DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ 229 | #endif 230 | #if FF_USE_FIND 231 | const TCHAR* pat; /* Pointer to the name matching pattern */ 232 | #endif 233 | } DIR; 234 | 235 | 236 | 237 | /* File information structure (FILINFO) */ 238 | 239 | typedef struct { 240 | FSIZE_t fsize; /* File size */ 241 | WORD fdate; /* Modified date */ 242 | WORD ftime; /* Modified time */ 243 | BYTE fattrib; /* File attribute */ 244 | #if FF_USE_LFN 245 | TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ 246 | TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ 247 | #else 248 | TCHAR fname[12 + 1]; /* File name */ 249 | #endif 250 | } FILINFO; 251 | 252 | 253 | 254 | /* File function return code (FRESULT) */ 255 | 256 | typedef enum { 257 | FR_OK = 0, /* (0) Succeeded */ 258 | FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ 259 | FR_INT_ERR, /* (2) Assertion failed */ 260 | FR_NOT_READY, /* (3) The physical drive cannot work */ 261 | FR_NO_FILE, /* (4) Could not find the file */ 262 | FR_NO_PATH, /* (5) Could not find the path */ 263 | FR_INVALID_NAME, /* (6) The path name format is invalid */ 264 | FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ 265 | FR_EXIST, /* (8) Access denied due to prohibited access */ 266 | FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ 267 | FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ 268 | FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ 269 | FR_NOT_ENABLED, /* (12) The volume has no work area */ 270 | FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ 271 | FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ 272 | FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ 273 | FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ 274 | FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ 275 | FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */ 276 | FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ 277 | } FRESULT; 278 | 279 | 280 | 281 | /*--------------------------------------------------------------*/ 282 | /* FatFs module application interface */ 283 | 284 | DWORD f_clst2sect(FATFS* fs, DWORD clst); 285 | DWORD f_getFat(FIL* fp, DWORD clst); 286 | 287 | FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ 288 | FRESULT f_close (FIL* fp); /* Close an open file object */ 289 | FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ 290 | FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ 291 | FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ 292 | FRESULT f_truncate (FIL* fp); /* Truncate the file */ 293 | FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ 294 | FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ 295 | FRESULT f_closedir (DIR* dp); /* Close an open directory */ 296 | FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ 297 | FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ 298 | FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */ 299 | FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ 300 | FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ 301 | FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ 302 | FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ 303 | FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ 304 | FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ 305 | FRESULT f_chdir (const TCHAR* path); /* Change current directory */ 306 | FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ 307 | FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ 308 | FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ 309 | FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ 310 | FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ 311 | FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ 312 | FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ 313 | FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ 314 | FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ 315 | FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ 316 | FRESULT f_setcp (WORD cp); /* Set current code page */ 317 | int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ 318 | int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ 319 | int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ 320 | TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ 321 | 322 | #define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) 323 | #define f_error(fp) ((fp)->err) 324 | #define f_tell(fp) ((fp)->fptr) 325 | #define f_size(fp) ((fp)->obj.objsize) 326 | #define f_rewind(fp) f_lseek((fp), 0) 327 | #define f_rewinddir(dp) f_readdir((dp), 0) 328 | #define f_rmdir(path) f_unlink(path) 329 | #define f_unmount(path) f_mount(0, path, 0) 330 | 331 | #ifndef EOF 332 | #define EOF (-1) 333 | #endif 334 | 335 | 336 | 337 | 338 | /*--------------------------------------------------------------*/ 339 | /* Additional user defined functions */ 340 | 341 | /* RTC function */ 342 | #if !FF_FS_READONLY && !FF_FS_NORTC 343 | DWORD get_fattime (void); 344 | #endif 345 | 346 | /* LFN support functions */ 347 | #if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */ 348 | WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */ 349 | WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */ 350 | DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */ 351 | #endif 352 | #if FF_USE_LFN == 3 /* Dynamic memory allocation */ 353 | void* ff_memalloc (UINT msize); /* Allocate memory block */ 354 | void ff_memfree (void* mblock); /* Free memory block */ 355 | #endif 356 | 357 | /* Sync functions */ 358 | #if FF_FS_REENTRANT 359 | int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */ 360 | int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */ 361 | void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */ 362 | int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ 363 | #endif 364 | 365 | 366 | 367 | 368 | /*--------------------------------------------------------------*/ 369 | /* Flags and offset address */ 370 | 371 | 372 | /* File access mode and open method flags (3rd argument of f_open) */ 373 | #define FA_READ 0x01 374 | #define FA_WRITE 0x02 375 | #define FA_OPEN_EXISTING 0x00 376 | #define FA_CREATE_NEW 0x04 377 | #define FA_CREATE_ALWAYS 0x08 378 | #define FA_OPEN_ALWAYS 0x10 379 | #define FA_OPEN_APPEND 0x30 380 | 381 | /* Fast seek controls (2nd argument of f_lseek) */ 382 | #define CREATE_LINKMAP ((FSIZE_t)0 - 1) 383 | 384 | /* Format options (2nd argument of f_mkfs) */ 385 | #define FM_FAT 0x01 386 | #define FM_FAT32 0x02 387 | #define FM_EXFAT 0x04 388 | #define FM_ANY 0x07 389 | #define FM_SFD 0x08 390 | 391 | /* Filesystem type (FATFS.fs_type) */ 392 | #define FS_FAT12 1 393 | #define FS_FAT16 2 394 | #define FS_FAT32 3 395 | #define FS_EXFAT 4 396 | 397 | /* File attribute bits for directory entry (FILINFO.fattrib) */ 398 | #define AM_RDO 0x01 /* Read only */ 399 | #define AM_HID 0x02 /* Hidden */ 400 | #define AM_SYS 0x04 /* System */ 401 | #define AM_DIR 0x10 /* Directory */ 402 | #define AM_ARC 0x20 /* Archive */ 403 | 404 | 405 | #ifdef __cplusplus 406 | } 407 | #endif 408 | 409 | #endif /* FF_DEFINED */ 410 | -------------------------------------------------------------------------------- /arm7/source/fat/ffconf.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------/ 2 | / FatFs Functional Configurations 3 | /---------------------------------------------------------------------------*/ 4 | 5 | #define FFCONF_DEF 86604 /* Revision ID */ 6 | 7 | /*---------------------------------------------------------------------------/ 8 | / Function Configurations 9 | /---------------------------------------------------------------------------*/ 10 | 11 | #define FF_FS_READONLY 1 12 | /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) 13 | / Read-only configuration removes writing API functions, f_write(), f_sync(), 14 | / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() 15 | / and optional writing functions as well. */ 16 | 17 | 18 | #define FF_FS_MINIMIZE 0 19 | /* This option defines minimization level to remove some basic API functions. 20 | / 21 | / 0: Basic functions are fully enabled. 22 | / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() 23 | / are removed. 24 | / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. 25 | / 3: f_lseek() function is removed in addition to 2. */ 26 | 27 | 28 | #define FF_USE_STRFUNC 1 29 | /* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf(). 30 | / 31 | / 0: Disable string functions. 32 | / 1: Enable without LF-CRLF conversion. 33 | / 2: Enable with LF-CRLF conversion. */ 34 | 35 | 36 | #define FF_USE_FIND 0 37 | /* This option switches filtered directory read functions, f_findfirst() and 38 | / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ 39 | 40 | 41 | #define FF_USE_MKFS 0 42 | /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ 43 | 44 | 45 | #define FF_USE_FASTSEEK 0 46 | /* This option switches fast seek function. (0:Disable or 1:Enable) */ 47 | 48 | 49 | #define FF_USE_EXPAND 0 50 | /* This option switches f_expand function. (0:Disable or 1:Enable) */ 51 | 52 | 53 | #define FF_USE_CHMOD 0 54 | /* This option switches attribute manipulation functions, f_chmod() and f_utime(). 55 | / (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ 56 | 57 | 58 | #define FF_USE_LABEL 0 59 | /* This option switches volume label functions, f_getlabel() and f_setlabel(). 60 | / (0:Disable or 1:Enable) */ 61 | 62 | 63 | #define FF_USE_FORWARD 0 64 | /* This option switches f_forward() function. (0:Disable or 1:Enable) */ 65 | 66 | 67 | /*---------------------------------------------------------------------------/ 68 | / Locale and Namespace Configurations 69 | /---------------------------------------------------------------------------*/ 70 | 71 | #define FF_CODE_PAGE 437 //932 72 | /* This option specifies the OEM code page to be used on the target system. 73 | / Incorrect code page setting can cause a file open failure. 74 | / 75 | / 437 - U.S. 76 | / 720 - Arabic 77 | / 737 - Greek 78 | / 771 - KBL 79 | / 775 - Baltic 80 | / 850 - Latin 1 81 | / 852 - Latin 2 82 | / 855 - Cyrillic 83 | / 857 - Turkish 84 | / 860 - Portuguese 85 | / 861 - Icelandic 86 | / 862 - Hebrew 87 | / 863 - Canadian French 88 | / 864 - Arabic 89 | / 865 - Nordic 90 | / 866 - Russian 91 | / 869 - Greek 2 92 | / 932 - Japanese (DBCS) 93 | / 936 - Simplified Chinese (DBCS) 94 | / 949 - Korean (DBCS) 95 | / 950 - Traditional Chinese (DBCS) 96 | / 0 - Include all code pages above and configured by f_setcp() 97 | */ 98 | 99 | 100 | #define FF_USE_LFN 1 101 | #define FF_MAX_LFN 255 102 | /* The FF_USE_LFN switches the support for LFN (long file name). 103 | / 104 | / 0: Disable LFN. FF_MAX_LFN has no effect. 105 | / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 106 | / 2: Enable LFN with dynamic working buffer on the STACK. 107 | / 3: Enable LFN with dynamic working buffer on the HEAP. 108 | / 109 | / To enable the LFN, ffunicode.c needs to be added to the project. The LFN function 110 | / requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and 111 | / additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. 112 | / The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can 113 | / be in range of 12 to 255. It is recommended to be set 255 to fully support LFN 114 | / specification. 115 | / When use stack for the working buffer, take care on stack overflow. When use heap 116 | / memory for the working buffer, memory management functions, ff_memalloc() and 117 | / ff_memfree() in ffsystem.c, need to be added to the project. */ 118 | 119 | 120 | #define FF_LFN_UNICODE 0 121 | /* This option switches the character encoding on the API when LFN is enabled. 122 | / 123 | / 0: ANSI/OEM in current CP (TCHAR = char) 124 | / 1: Unicode in UTF-16 (TCHAR = WCHAR) 125 | / 2: Unicode in UTF-8 (TCHAR = char) 126 | / 3: Unicode in UTF-32 (TCHAR = DWORD) 127 | / 128 | / Also behavior of string I/O functions will be affected by this option. 129 | / When LFN is not enabled, this option has no effect. */ 130 | 131 | 132 | #define FF_LFN_BUF 255 133 | #define FF_SFN_BUF 12 134 | /* This set of options defines size of file name members in the FILINFO structure 135 | / which is used to read out directory items. These values should be suffcient for 136 | / the file names to read. The maximum possible length of the read file name depends 137 | / on character encoding. When LFN is not enabled, these options have no effect. */ 138 | 139 | 140 | #define FF_STRF_ENCODE 3 141 | /* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(), 142 | / f_putc(), f_puts and f_printf() convert the character encoding in it. 143 | / This option selects assumption of character encoding ON THE FILE to be 144 | / read/written via those functions. 145 | / 146 | / 0: ANSI/OEM in current CP 147 | / 1: Unicode in UTF-16LE 148 | / 2: Unicode in UTF-16BE 149 | / 3: Unicode in UTF-8 150 | */ 151 | 152 | 153 | #define FF_FS_RPATH 1 154 | /* This option configures support for relative path. 155 | / 156 | / 0: Disable relative path and remove related functions. 157 | / 1: Enable relative path. f_chdir() and f_chdrive() are available. 158 | / 2: f_getcwd() function is available in addition to 1. 159 | */ 160 | 161 | 162 | /*---------------------------------------------------------------------------/ 163 | / Drive/Volume Configurations 164 | /---------------------------------------------------------------------------*/ 165 | 166 | #define FF_VOLUMES 2 167 | /* Number of volumes (logical drives) to be used. (1-10) */ 168 | 169 | 170 | #define FF_STR_VOLUME_ID 1 171 | #define FF_VOLUME_STRS "fat","sd"//"RAM","NAND","CF","SD","SD2","USB","USB2","USB3" 172 | /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. 173 | / When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive 174 | / number in the path name. FF_VOLUME_STRS defines the volume ID strings for each 175 | / logical drives. Number of items must not be less than FF_VOLUMES. Valid 176 | / characters for the volume ID strings are A-Z, a-z and 0-9, however, they are 177 | / compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is 178 | / not defined, a user defined volume string table needs to be defined as: 179 | / 180 | / const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... 181 | */ 182 | 183 | 184 | #define FF_MULTI_PARTITION 0 185 | /* This option switches support for multiple volumes on the physical drive. 186 | / By default (0), each logical drive number is bound to the same physical drive 187 | / number and only an FAT volume found on the physical drive will be mounted. 188 | / When this function is enabled (1), each logical drive number can be bound to 189 | / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() 190 | / funciton will be available. */ 191 | 192 | 193 | #define FF_MIN_SS 512 194 | #define FF_MAX_SS 512 195 | /* This set of options configures the range of sector size to be supported. (512, 196 | / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and 197 | / harddisk. But a larger value may be required for on-board flash memory and some 198 | / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured 199 | / for variable sector size mode and disk_ioctl() function needs to implement 200 | / GET_SECTOR_SIZE command. */ 201 | 202 | 203 | #define FF_USE_TRIM 0 204 | /* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) 205 | / To enable Trim function, also CTRL_TRIM command should be implemented to the 206 | / disk_ioctl() function. */ 207 | 208 | 209 | #define FF_FS_NOFSINFO 0 210 | /* If you need to know correct free space on the FAT32 volume, set bit 0 of this 211 | / option, and f_getfree() function at first time after volume mount will force 212 | / a full FAT scan. Bit 1 controls the use of last allocated cluster number. 213 | / 214 | / bit0=0: Use free cluster count in the FSINFO if available. 215 | / bit0=1: Do not trust free cluster count in the FSINFO. 216 | / bit1=0: Use last allocated cluster number in the FSINFO if available. 217 | / bit1=1: Do not trust last allocated cluster number in the FSINFO. 218 | */ 219 | 220 | 221 | 222 | /*---------------------------------------------------------------------------/ 223 | / System Configurations 224 | /---------------------------------------------------------------------------*/ 225 | 226 | #define FF_FS_TINY 0 227 | /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) 228 | / At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes. 229 | / Instead of private sector buffer eliminated from the file object, common sector 230 | / buffer in the filesystem object (FATFS) is used for the file data transfer. */ 231 | 232 | 233 | #define FF_FS_EXFAT 0 234 | /* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) 235 | / To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) 236 | / Note that enabling exFAT discards ANSI C (C89) compatibility. */ 237 | 238 | 239 | #define FF_FS_NORTC 1 240 | #define FF_NORTC_MON 1 241 | #define FF_NORTC_MDAY 1 242 | #define FF_NORTC_YEAR 2018 243 | /* The option FF_FS_NORTC switches timestamp functiton. If the system does not have 244 | / any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable 245 | / the timestamp function. Every object modified by FatFs will have a fixed timestamp 246 | / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. 247 | / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be 248 | / added to the project to read current time form real-time clock. FF_NORTC_MON, 249 | / FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. 250 | / These options have no effect at read-only configuration (FF_FS_READONLY = 1). */ 251 | 252 | 253 | #define FF_FS_LOCK 0 254 | /* The option FF_FS_LOCK switches file lock function to control duplicated file open 255 | / and illegal operation to open objects. This option must be 0 when FF_FS_READONLY 256 | / is 1. 257 | / 258 | / 0: Disable file lock function. To avoid volume corruption, application program 259 | / should avoid illegal open, remove and rename to the open objects. 260 | / >0: Enable file lock function. The value defines how many files/sub-directories 261 | / can be opened simultaneously under file lock control. Note that the file 262 | / lock control is independent of re-entrancy. */ 263 | 264 | 265 | /* #include // O/S definitions */ 266 | #define FF_FS_REENTRANT 0 267 | #define FF_FS_TIMEOUT 1000 268 | #define FF_SYNC_t HANDLE 269 | /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs 270 | / module itself. Note that regardless of this option, file access to different 271 | / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() 272 | / and f_fdisk() function, are always not re-entrant. Only file/directory access 273 | / to the same volume is under control of this function. 274 | / 275 | / 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect. 276 | / 1: Enable re-entrancy. Also user provided synchronization handlers, 277 | / ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() 278 | / function, must be added to the project. Samples are available in 279 | / option/syscall.c. 280 | / 281 | / The FF_FS_TIMEOUT defines timeout period in unit of time tick. 282 | / The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, 283 | / SemaphoreHandle_t and etc. A header file for O/S definitions needs to be 284 | / included somewhere in the scope of ff.h. */ 285 | 286 | 287 | 288 | /*--- End of configuration options ---*/ 289 | -------------------------------------------------------------------------------- /arm7/source/fat/ffsystem.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* Sample Code of OS Dependent Functions for FatFs */ 3 | /* (C)ChaN, 2018 */ 4 | /*------------------------------------------------------------------------*/ 5 | 6 | 7 | #include "ff.h" 8 | 9 | 10 | #if FF_USE_LFN == 3 /* Dynamic memory allocation */ 11 | 12 | /*------------------------------------------------------------------------*/ 13 | /* Allocate a memory block */ 14 | /*------------------------------------------------------------------------*/ 15 | 16 | void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */ 17 | UINT msize /* Number of bytes to allocate */ 18 | ) 19 | { 20 | return malloc(msize); /* Allocate a new memory block with POSIX API */ 21 | } 22 | 23 | 24 | /*------------------------------------------------------------------------*/ 25 | /* Free a memory block */ 26 | /*------------------------------------------------------------------------*/ 27 | 28 | void ff_memfree ( 29 | void* mblock /* Pointer to the memory block to free (nothing to do if null) */ 30 | ) 31 | { 32 | free(mblock); /* Free the memory block with POSIX API */ 33 | } 34 | 35 | #endif 36 | 37 | 38 | 39 | #if FF_FS_REENTRANT /* Mutal exclusion */ 40 | 41 | /*------------------------------------------------------------------------*/ 42 | /* Create a Synchronization Object */ 43 | /*------------------------------------------------------------------------*/ 44 | /* This function is called in f_mount() function to create a new 45 | / synchronization object for the volume, such as semaphore and mutex. 46 | / When a 0 is returned, the f_mount() function fails with FR_INT_ERR. 47 | */ 48 | 49 | //const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */ 50 | 51 | 52 | int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */ 53 | BYTE vol, /* Corresponding volume (logical drive number) */ 54 | FF_SYNC_t* sobj /* Pointer to return the created sync object */ 55 | ) 56 | { 57 | /* Win32 */ 58 | *sobj = CreateMutex(NULL, FALSE, NULL); 59 | return (int)(*sobj != INVALID_HANDLE_VALUE); 60 | 61 | /* uITRON */ 62 | // T_CSEM csem = {TA_TPRI,1,1}; 63 | // *sobj = acre_sem(&csem); 64 | // return (int)(*sobj > 0); 65 | 66 | /* uC/OS-II */ 67 | // OS_ERR err; 68 | // *sobj = OSMutexCreate(0, &err); 69 | // return (int)(err == OS_NO_ERR); 70 | 71 | /* FreeRTOS */ 72 | // *sobj = xSemaphoreCreateMutex(); 73 | // return (int)(*sobj != NULL); 74 | 75 | /* CMSIS-RTOS */ 76 | // *sobj = osMutexCreate(&Mutex[vol]); 77 | // return (int)(*sobj != NULL); 78 | } 79 | 80 | 81 | /*------------------------------------------------------------------------*/ 82 | /* Delete a Synchronization Object */ 83 | /*------------------------------------------------------------------------*/ 84 | /* This function is called in f_mount() function to delete a synchronization 85 | / object that created with ff_cre_syncobj() function. When a 0 is returned, 86 | / the f_mount() function fails with FR_INT_ERR. 87 | */ 88 | 89 | int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */ 90 | FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ 91 | ) 92 | { 93 | /* Win32 */ 94 | return (int)CloseHandle(sobj); 95 | 96 | /* uITRON */ 97 | // return (int)(del_sem(sobj) == E_OK); 98 | 99 | /* uC/OS-II */ 100 | // OS_ERR err; 101 | // OSMutexDel(sobj, OS_DEL_ALWAYS, &err); 102 | // return (int)(err == OS_NO_ERR); 103 | 104 | /* FreeRTOS */ 105 | // vSemaphoreDelete(sobj); 106 | // return 1; 107 | 108 | /* CMSIS-RTOS */ 109 | // return (int)(osMutexDelete(sobj) == osOK); 110 | } 111 | 112 | 113 | /*------------------------------------------------------------------------*/ 114 | /* Request Grant to Access the Volume */ 115 | /*------------------------------------------------------------------------*/ 116 | /* This function is called on entering file functions to lock the volume. 117 | / When a 0 is returned, the file function fails with FR_TIMEOUT. 118 | */ 119 | 120 | int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ 121 | FF_SYNC_t sobj /* Sync object to wait */ 122 | ) 123 | { 124 | /* Win32 */ 125 | return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0); 126 | 127 | /* uITRON */ 128 | // return (int)(wai_sem(sobj) == E_OK); 129 | 130 | /* uC/OS-II */ 131 | // OS_ERR err; 132 | // OSMutexPend(sobj, FF_FS_TIMEOUT, &err)); 133 | // return (int)(err == OS_NO_ERR); 134 | 135 | /* FreeRTOS */ 136 | // return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE); 137 | 138 | /* CMSIS-RTOS */ 139 | // return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK); 140 | } 141 | 142 | 143 | /*------------------------------------------------------------------------*/ 144 | /* Release Grant to Access the Volume */ 145 | /*------------------------------------------------------------------------*/ 146 | /* This function is called on leaving file functions to unlock the volume. 147 | */ 148 | 149 | void ff_rel_grant ( 150 | FF_SYNC_t sobj /* Sync object to be signaled */ 151 | ) 152 | { 153 | /* Win32 */ 154 | ReleaseMutex(sobj); 155 | 156 | /* uITRON */ 157 | // sig_sem(sobj); 158 | 159 | /* uC/OS-II */ 160 | // OSMutexPost(sobj); 161 | 162 | /* FreeRTOS */ 163 | // xSemaphoreGive(sobj); 164 | 165 | /* CMSIS-RTOS */ 166 | // osMutexRelease(sobj); 167 | } 168 | 169 | #endif 170 | 171 | -------------------------------------------------------------------------------- /arm7/source/fpsAdjust.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "fpsAdjust.h" 5 | #include "isdprint.h" 6 | 7 | static fpsa_t* sActiveFpsa = NULL; 8 | 9 | static void vcountIrqLower() 10 | { 11 | while (1) 12 | { 13 | if (sActiveFpsa->initial) 14 | { 15 | sActiveFpsa->initial = FALSE; 16 | break; 17 | } 18 | 19 | if (!sActiveFpsa->backJump) 20 | sActiveFpsa->cycleDelta += sActiveFpsa->targetCycles - ((u64)FPSA_CYCLES_PER_FRAME << 24); 21 | u32 linesToAdd = 0; 22 | while (sActiveFpsa->cycleDelta >= (s64)((u64)FPSA_CYCLES_PER_LINE << 23)) 23 | { 24 | sActiveFpsa->cycleDelta -= (u64)FPSA_CYCLES_PER_LINE << 24; 25 | if (++linesToAdd == 5) 26 | break; 27 | } 28 | if (linesToAdd == 0) 29 | { 30 | sActiveFpsa->backJump = FALSE; 31 | break; 32 | } 33 | if (linesToAdd > 1) 34 | { 35 | sActiveFpsa->backJump = TRUE; 36 | } 37 | else 38 | { 39 | // don't set the backJump flag because the irq is not retriggered if the new vcount 40 | // is the same as the previous line 41 | sActiveFpsa->backJump = FALSE; 42 | } 43 | // ensure we won't accidentally run out of line time 44 | while (REG_DISPSTAT & DISP_IN_HBLANK) 45 | ; 46 | int curVCount = REG_VCOUNT; 47 | REG_VCOUNT = curVCount - (linesToAdd - 1); 48 | if (linesToAdd == 1) 49 | break; 50 | 51 | while (REG_VCOUNT >= curVCount)//FPSA_ADJUST_MAX_VCOUNT - 5) 52 | ; 53 | while (REG_VCOUNT < curVCount)//FPSA_ADJUST_MAX_VCOUNT - 5) 54 | ; 55 | } 56 | REG_IF = IRQ_VCOUNT; 57 | } 58 | 59 | static void vcountIrqHigher() 60 | { 61 | if (sActiveFpsa->initial) 62 | { 63 | sActiveFpsa->initial = FALSE; 64 | return; 65 | } 66 | sActiveFpsa->cycleDelta += ((u64)FPSA_CYCLES_PER_FRAME << 24) - sActiveFpsa->targetCycles; 67 | u32 linesToSkip = 0; 68 | while (sActiveFpsa->cycleDelta >= (s64)((u64)FPSA_CYCLES_PER_LINE << 23)) 69 | { 70 | sActiveFpsa->cycleDelta -= (u64)FPSA_CYCLES_PER_LINE << 24; 71 | if (++linesToSkip == 5) 72 | break; 73 | } 74 | if (linesToSkip == 0) 75 | return; 76 | // ensure we won't accidentally run out of line time 77 | while (REG_DISPSTAT & DISP_IN_HBLANK) 78 | ; 79 | REG_VCOUNT = REG_VCOUNT + (linesToSkip + 1); 80 | } 81 | 82 | void fpsa_init(fpsa_t* fpsa) 83 | { 84 | memset(fpsa, 0, sizeof(fpsa_t)); 85 | fpsa->isStarted = FALSE; 86 | fpsa_setTargetFrameCycles(fpsa, (u64)FPSA_CYCLES_PER_FRAME << 24); // default to no adjustment 87 | } 88 | 89 | void fpsa_start(fpsa_t* fpsa) 90 | { 91 | int irq = enterCriticalSection(); 92 | do 93 | { 94 | if (fpsa->isStarted || sActiveFpsa) 95 | break; 96 | if (fpsa->targetCycles == ((u64)FPSA_CYCLES_PER_FRAME << 24)) 97 | break; 98 | irqDisable(IRQ_VCOUNT); 99 | sActiveFpsa = fpsa; 100 | fpsa->backJump = FALSE; 101 | fpsa->cycleDelta = 0; 102 | fpsa->initial = TRUE; 103 | fpsa->isFpsLower = fpsa->targetCycles >= ((u64)FPSA_CYCLES_PER_FRAME << 24); 104 | // prevent the irq from immediately happening 105 | while (REG_VCOUNT != FPSA_ADJUST_MAX_VCOUNT + 2) 106 | ; 107 | fpsa->isStarted = TRUE; 108 | if (fpsa->isFpsLower) 109 | { 110 | SetYtrigger(FPSA_ADJUST_MAX_VCOUNT - 5); 111 | irqSet(IRQ_VCOUNT, vcountIrqLower); 112 | } 113 | else 114 | { 115 | SetYtrigger(FPSA_ADJUST_MIN_VCOUNT); 116 | irqSet(IRQ_VCOUNT, vcountIrqHigher); 117 | } 118 | irqEnable(IRQ_VCOUNT); 119 | } while (0); 120 | leaveCriticalSection(irq); 121 | } 122 | 123 | void fpsa_stop(fpsa_t* fpsa) 124 | { 125 | if (!fpsa->isStarted) 126 | return; 127 | sActiveFpsa = NULL; 128 | fpsa->isStarted = FALSE; 129 | irqDisable(IRQ_VCOUNT); 130 | } 131 | 132 | void fpsa_setTargetFrameCycles(fpsa_t* fpsa, u64 cycles) 133 | { 134 | fpsa->targetCycles = cycles; 135 | } 136 | 137 | void fpsa_setTargetFpsFraction(fpsa_t* fpsa, u32 num, u32 den) 138 | { 139 | u64 cycles = (((double)FPSA_SYS_CLOCK * den * (1 << 24)) / num) + 0.5; 140 | fpsa_setTargetFrameCycles(fpsa, cycles);//((((u64)FPSA_SYS_CLOCK * (u64)den) << 24) + ((num + 1) >> 1)) / num); 141 | } -------------------------------------------------------------------------------- /arm7/source/fpsAdjust.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define FPSA_SYS_CLOCK 33513982 4 | 5 | #define FPSA_CYCLES_PER_PIXEL 6 6 | 7 | #define FPSA_LCD_WIDTH 256 8 | #define FPSA_LCD_HBLANK 99 9 | #define FPSA_LCD_COLUMNS (FPSA_LCD_WIDTH + FPSA_LCD_HBLANK) 10 | 11 | #define FPSA_LCD_HEIGHT 192 12 | #define FPSA_LCD_VBLANK 71 13 | #define FPSA_LCD_LINES (FPSA_LCD_HEIGHT + FPSA_LCD_VBLANK) 14 | 15 | #define FPSA_CYCLES_PER_LINE (FPSA_LCD_COLUMNS * FPSA_CYCLES_PER_PIXEL) 16 | #define FPSA_CYCLES_PER_FRAME (FPSA_LCD_COLUMNS * FPSA_LCD_LINES * FPSA_CYCLES_PER_PIXEL) 17 | 18 | #define FPSA_ADJUST_MIN_VCOUNT 202 19 | #define FPSA_ADJUST_MAX_VCOUNT 212 20 | 21 | typedef struct 22 | { 23 | u32 isStarted; 24 | u32 isFpsLower; //TRUE if vblank needs to be extended, FALSE if it needs to be shortened 25 | u32 backJump; 26 | u32 initial; 27 | u64 targetCycles; //target cycles in 0.40.24 format 28 | s64 cycleDelta; 29 | } fpsa_t; 30 | 31 | void fpsa_init(fpsa_t* fpsa); 32 | void fpsa_start(fpsa_t* fpsa); 33 | void fpsa_stop(fpsa_t* fpsa); 34 | 35 | //Sets the target frame rate as clock cycles per frame in 0.40.24 format 36 | void fpsa_setTargetFrameCycles(fpsa_t* fpsa, u64 cycles); 37 | 38 | //Sets the target frame rate as a frames per second fraction 39 | void fpsa_setTargetFpsFraction(fpsa_t* fpsa, u32 num, u32 den); 40 | 41 | //Sets the target frame rate in integer frames per second 42 | static inline void fpsa_setTargetFps(fpsa_t* fpsa, u32 fps) 43 | { 44 | fpsa_setTargetFpsFraction(fpsa, fps, 1); 45 | } -------------------------------------------------------------------------------- /arm7/source/fvPlayer7.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "isdprint.h" 4 | #include "../../common/ipc.h" 5 | #include "fat.h" 6 | #include "adpcm.h" 7 | #include "irqWait.h" 8 | #include "../../common/twlwram.h" 9 | #include "fvPlayer7.h" 10 | 11 | #define FV_AUDIO_START_OFFSET 12 12 | 13 | #define FV_AUDIO_CH_LEFT 1 14 | #define FV_AUDIO_CH_RIGHT 3 15 | 16 | static fv_player7_t sPlayer; 17 | 18 | void fv_init(void) 19 | { 20 | memset(&sPlayer, 0, sizeof(sPlayer)); 21 | } 22 | 23 | static inline int getAudioTimerValue(int rate) 24 | { 25 | return (16756991 + ((rate + 1) >> 1)) / rate; 26 | } 27 | 28 | static void audioFrameIrq(void) 29 | { 30 | } 31 | 32 | static void decodeAudioFrame(void) 33 | { 34 | adpcm_decompress(sPlayer.audioQueueL[sPlayer.queueReadPtr], FV_AUDIO_FRAME_SIZE, 35 | sPlayer.audioRingL[sPlayer.ringPos]); 36 | adpcm_decompress(sPlayer.audioQueueR[sPlayer.queueReadPtr], FV_AUDIO_FRAME_SIZE, 37 | sPlayer.audioRingR[sPlayer.ringPos]); 38 | 39 | // sPlayer.ringVideoFrameIds[sPlayer.ringPos] = sPlayer.queueVideoFrameIds[sPlayer.queueReadPtr]; 40 | 41 | if (++sPlayer.ringPos == FV_AUDIO_RING_FRAMES) 42 | sPlayer.ringPos = 0; 43 | 44 | if (++sPlayer.queueReadPtr == FV_AUDIO_QUEUE_FRAMES) 45 | sPlayer.queueReadPtr = 0; 46 | 47 | sPlayer.queueFrameCount--; 48 | 49 | sPlayer.audioFramesNeeded--; 50 | sPlayer.audioFramesProvided++; 51 | } 52 | 53 | static void startAudio(void) 54 | { 55 | if (sPlayer.audioStarted) 56 | return; 57 | 58 | sPlayer.ringPos = 0; 59 | sPlayer.audioFramesNeeded = 0; 60 | sPlayer.audioFramesProvided = 0; 61 | 62 | for (int i = 0; i < FV_AUDIO_START_OFFSET; i++) 63 | { 64 | if (sPlayer.queueFrameCount == 0) 65 | break; 66 | decodeAudioFrame(); 67 | } 68 | 69 | sPlayer.audioFramesProvided -= FV_AUDIO_START_OFFSET; 70 | 71 | int tmr = getAudioTimerValue(FV_AUDIO_RATE); 72 | SCHANNEL_SOURCE(FV_AUDIO_CH_LEFT) = (u32)sPlayer.audioRingL; 73 | SCHANNEL_REPEAT_POINT(FV_AUDIO_CH_LEFT) = 0; 74 | SCHANNEL_LENGTH(FV_AUDIO_CH_LEFT) = sizeof(sPlayer.audioRingL) >> 2; 75 | SCHANNEL_TIMER(FV_AUDIO_CH_LEFT) = -tmr; 76 | 77 | SCHANNEL_SOURCE(FV_AUDIO_CH_RIGHT) = (u32)sPlayer.audioRingR; 78 | SCHANNEL_REPEAT_POINT(FV_AUDIO_CH_RIGHT) = 0; 79 | SCHANNEL_LENGTH(FV_AUDIO_CH_RIGHT) = sizeof(sPlayer.audioRingR) >> 2; 80 | SCHANNEL_TIMER(FV_AUDIO_CH_RIGHT) = -tmr; 81 | 82 | TIMER_CR(0) = 0; 83 | TIMER_CR(1) = 0; 84 | TIMER_CR(2) = 0; 85 | TIMER_CR(3) = 0; 86 | 87 | TIMER_DATA(0) = -2; // 1/2 clock divider 88 | TIMER_DATA(1) = -tmr; // sample rate 89 | TIMER_DATA(2) = -256; // length of audio frame 90 | TIMER_DATA(3) = 0; // audio block counter 91 | 92 | TIMER_CR(3) = TIMER_CASCADE | TIMER_ENABLE; 93 | TIMER_CR(2) = TIMER_CASCADE | TIMER_ENABLE | TIMER_IRQ_REQ; 94 | TIMER_CR(1) = TIMER_CASCADE | TIMER_ENABLE; 95 | 96 | irqSet(IRQ_TIMER2, audioFrameIrq); 97 | irqEnable(IRQ_TIMER2); 98 | 99 | SCHANNEL_CR(FV_AUDIO_CH_LEFT) = 100 | SCHANNEL_ENABLE | SOUND_VOL(0x7F) | SOUND_PAN(0) | SOUND_FORMAT_16BIT | SOUND_REPEAT; 101 | SCHANNEL_CR(FV_AUDIO_CH_RIGHT) = 102 | SCHANNEL_ENABLE | SOUND_VOL(0x7F) | SOUND_PAN(0x7F) | SOUND_FORMAT_16BIT | SOUND_REPEAT; 103 | TIMER_CR(0) = TIMER_ENABLE; 104 | 105 | sPlayer.audioStarted = true; 106 | } 107 | 108 | static void stopAudio(void) 109 | { 110 | if (!sPlayer.audioStarted) 111 | return; 112 | 113 | sPlayer.audioStarted = false; 114 | TIMER_CR(0) = 0; 115 | irqDisable(IRQ_TIMER2); 116 | SCHANNEL_CR(FV_AUDIO_CH_LEFT) = 0; 117 | SCHANNEL_CR(FV_AUDIO_CH_RIGHT) = 0; 118 | TIMER_CR(1) = 0; 119 | TIMER_CR(2) = 0; 120 | TIMER_CR(3) = 0; 121 | sPlayer.ringPos = 0; 122 | memset(&sPlayer.audioRingL[0][0], 0, sizeof(sPlayer.audioRingL)); 123 | memset(&sPlayer.audioRingR[0][0], 0, sizeof(sPlayer.audioRingR)); 124 | } 125 | 126 | static void updateAudio(void) 127 | { 128 | if (!sPlayer.audioStarted) 129 | return; 130 | 131 | int audioBlocks = TIMER_DATA(3); 132 | int needed = audioBlocks - (sPlayer.audioFramesProvided & 0xFFFF); 133 | if (needed < 0) 134 | needed += 65536; 135 | 136 | while (needed > 0 && sPlayer.queueFrameCount > 0) 137 | { 138 | decodeAudioFrame(); 139 | needed--; 140 | } 141 | } 142 | 143 | static void gotoKeyFrameDirect(const fv_keyframe_t* keyFrameData) 144 | { 145 | f_lseek(&sPlayer.file, keyFrameData->offset); 146 | 147 | // reset player state 148 | stopAudio(); 149 | 150 | sPlayer.ringPos = 0; 151 | sPlayer.queueReadPtr = 0; 152 | sPlayer.queueWritePtr = 0; 153 | sPlayer.queueFrameCount = 0; 154 | sPlayer.audioFramesNeeded = 0; 155 | sPlayer.audioFramesProvided = 0; 156 | 157 | memset(&sPlayer.audioQueueL[0][0], 0, sizeof(sPlayer.audioQueueL)); 158 | memset(&sPlayer.audioQueueR[0][0], 0, sizeof(sPlayer.audioQueueR)); 159 | } 160 | 161 | static u32 gotoKeyFrame(u32 keyFrame) 162 | { 163 | UINT br; 164 | fv_keyframe_t keyFrameData; 165 | 166 | f_lseek(&sPlayer.file, sizeof(fv_header_t) + sizeof(fv_keyframe_t) * keyFrame); 167 | f_read(&sPlayer.file, &keyFrameData, sizeof(fv_keyframe_t), &br); 168 | 169 | gotoKeyFrameDirect(&keyFrameData); 170 | 171 | return keyFrameData.frame; 172 | } 173 | 174 | static u32 gotoNearestKeyFrame(u32 frame, u32* resultFrame) 175 | { 176 | UINT br; 177 | fv_keyframe_t keyFrameData = { 0 }; 178 | fv_keyframe_t newKeyFrameData; 179 | u32 keyFrameId = -1; 180 | 181 | f_lseek(&sPlayer.file, sizeof(fv_header_t)); 182 | 183 | for (int i = 0; i < sPlayer.nrKeyFrames; i++) 184 | { 185 | f_read(&sPlayer.file, &newKeyFrameData, sizeof(fv_keyframe_t), &br); 186 | if (newKeyFrameData.frame <= frame) 187 | { 188 | keyFrameData = newKeyFrameData; 189 | keyFrameId = i; 190 | } 191 | 192 | if (newKeyFrameData.frame >= frame) 193 | break; 194 | } 195 | 196 | gotoKeyFrameDirect(&keyFrameData); 197 | 198 | if (resultFrame) 199 | *resultFrame = keyFrameData.frame; 200 | 201 | return keyFrameId; 202 | } 203 | 204 | static void handleFifo(u32 value) 205 | { 206 | UINT br; 207 | 208 | switch (value >> IPC_CMD_CMD_SHIFT) 209 | { 210 | case IPC_CMD_READ_FRAME: 211 | { 212 | u32 len; 213 | if (f_read(&sPlayer.file, &len, 4, &br) != FR_OK || br != 4) 214 | { 215 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_READ_FRAME, 0)); 216 | break; 217 | } 218 | f_read(&sPlayer.file, (void*)(value & IPC_CMD_ARG_MASK), len & 0x1FFFF, &br); 219 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_READ_FRAME, len & 0x1FFFF)); 220 | // read audio frames 221 | u32 audioFrames = len >> 17; 222 | for (int i = 0; i < audioFrames; i++) 223 | { 224 | f_read(&sPlayer.file, sPlayer.audioQueueL[sPlayer.queueWritePtr], FV_AUDIO_FRAME_SIZE, &br); 225 | f_read(&sPlayer.file, sPlayer.audioQueueR[sPlayer.queueWritePtr], FV_AUDIO_FRAME_SIZE, &br); 226 | // sPlayer.queueVideoFrameIds[sPlayer.queueWritePtr] = sPlayer.curVideoFrame; 227 | if (++sPlayer.queueWritePtr == FV_AUDIO_QUEUE_FRAMES) 228 | sPlayer.queueWritePtr = 0; 229 | 230 | sPlayer.queueFrameCount++; 231 | } 232 | 233 | // sPlayer.curVideoFrame++; 234 | break; 235 | } 236 | 237 | case IPC_CMD_OPEN_FILE: 238 | { 239 | FRESULT result = f_open(&sPlayer.file, (const char*)(value & IPC_CMD_ARG_MASK), FA_OPEN_EXISTING | FA_READ); 240 | if (result != FR_OK) 241 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_OPEN_FILE, 0)); 242 | else 243 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_OPEN_FILE, 1)); 244 | break; 245 | } 246 | 247 | case IPC_CMD_READ_HEADER: 248 | { 249 | fv_header_t* header = (fv_header_t*)(value & IPC_CMD_ARG_MASK); 250 | f_read(&sPlayer.file, header, sizeof(fv_header_t), &br); 251 | 252 | sPlayer.nrKeyFrames = header->nrKeyFrames; 253 | 254 | gotoKeyFrame(0); 255 | 256 | u32 num = header->fpsNum; 257 | u32 den = header->fpsDen; 258 | 259 | int vblankCount = 1; 260 | while (num * (vblankCount + 1) / den < 62) 261 | vblankCount++; 262 | 263 | // safety 264 | if (num * vblankCount / den < 62) 265 | { 266 | fpsa_init(&sPlayer.fpsa); 267 | fpsa_setTargetFpsFraction(&sPlayer.fpsa, num * vblankCount, den); 268 | sPlayer.fpsa.targetCycles = 269 | (double)sPlayer.fpsa.targetCycles * getAudioTimerValue(FV_AUDIO_RATE) * FV_AUDIO_RATE / 16756991.0; 270 | fpsa_start(&sPlayer.fpsa); 271 | } 272 | 273 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_READ_HEADER, vblankCount)); 274 | break; 275 | } 276 | 277 | case IPC_CMD_GOTO_KEYFRAME: 278 | { 279 | u32 frame = gotoKeyFrame(value & IPC_CMD_ARG_MASK); 280 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_GOTO_KEYFRAME, frame)); 281 | break; 282 | } 283 | 284 | case IPC_CMD_GOTO_NEAREST_KEYFRAME: 285 | { 286 | u32 frame = value & IPC_CMD_ARG_MASK; 287 | u32 resultFrame; 288 | u32 keyFrame = gotoNearestKeyFrame(frame, &resultFrame); 289 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_GOTO_NEAREST_KEYFRAME, keyFrame)); 290 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_GOTO_KEYFRAME, resultFrame)); 291 | break; 292 | } 293 | 294 | case IPC_CMD_CONTROL_AUDIO: 295 | { 296 | if ((value & IPC_CMD_ARG_MASK) == IPC_ARG_CONTROL_AUDIO_START) 297 | startAudio(); 298 | else if ((value & IPC_CMD_ARG_MASK) == IPC_ARG_CONTROL_AUDIO_STOP) 299 | stopAudio(); 300 | else if ((value & IPC_CMD_ARG_MASK) == IPC_ARG_CONTROL_AUDIO_STOP_CLEAR) 301 | { 302 | stopAudio(); 303 | sPlayer.ringPos = 0; 304 | sPlayer.queueReadPtr = 0; 305 | sPlayer.queueWritePtr = 0; 306 | sPlayer.queueFrameCount = 0; 307 | sPlayer.audioFramesNeeded = 0; 308 | sPlayer.audioFramesProvided = 0; 309 | 310 | memset(&sPlayer.audioQueueL[0][0], 0, sizeof(sPlayer.audioQueueL)); 311 | memset(&sPlayer.audioQueueR[0][0], 0, sizeof(sPlayer.audioQueueR)); 312 | } 313 | break; 314 | } 315 | 316 | case IPC_CMD_SETUP_DLDI: 317 | { 318 | if (!isDSiMode()) 319 | { 320 | memcpy((void*)0x037F8000, (void*)(value & IPC_CMD_ARG_MASK), 16 * 1024); 321 | fat_mountDldi(); 322 | } 323 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_SETUP_DLDI, 0)); 324 | break; 325 | } 326 | 327 | case IPC_CMD_HANDSHAKE: 328 | { 329 | bool canUseWram = false; 330 | if (isDSiMode()) 331 | canUseWram = twr_isUnlocked(); 332 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_HANDSHAKE, canUseWram ? 1 : 0)); 333 | break; 334 | } 335 | } 336 | } 337 | 338 | void fv_main(void) 339 | { 340 | if (!fifoCheckValue32(FIFO_USER_01)) 341 | irq_wait(false, IRQ_TIMER2 | IRQ_FIFO_NOT_EMPTY); 342 | 343 | updateAudio(); 344 | 345 | if (fifoCheckValue32(FIFO_USER_01)) 346 | handleFifo(fifoGetValue32(FIFO_USER_01)); 347 | } 348 | -------------------------------------------------------------------------------- /arm7/source/fvPlayer7.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../../common/fastVideo.h" 3 | #include "fat/ff.h" 4 | #include "fpsAdjust.h" 5 | 6 | #define FV_AUDIO_QUEUE_FRAMES 64 7 | 8 | #define FV_AUDIO_RING_FRAMES 24 9 | 10 | typedef struct 11 | { 12 | bool audioStarted; 13 | FIL file; 14 | fpsa_t fpsa; 15 | u32 nrKeyFrames; 16 | // u32 curVideoFrame; 17 | int queueReadPtr; 18 | int queueWritePtr; 19 | int queueFrameCount; 20 | int audioFramesNeeded; 21 | int audioFramesProvided; 22 | int ringPos; 23 | // u32 queueVideoFrameIds[FV_AUDIO_QUEUE_FRAMES]; 24 | // u32 ringVideoFrameIds[FV_AUDIO_RING_FRAMES]; 25 | u8 audioQueueL[FV_AUDIO_QUEUE_FRAMES][FV_AUDIO_FRAME_SIZE] ALIGN(4); 26 | u8 audioQueueR[FV_AUDIO_QUEUE_FRAMES][FV_AUDIO_FRAME_SIZE] ALIGN(4); 27 | s16 audioRingL[FV_AUDIO_RING_FRAMES][FV_AUDIO_FRAME_SAMPLES] ALIGN(4); 28 | s16 audioRingR[FV_AUDIO_RING_FRAMES][FV_AUDIO_FRAME_SAMPLES] ALIGN(4); 29 | } fv_player7_t; 30 | 31 | void fv_init(void); 32 | void fv_main(void); 33 | -------------------------------------------------------------------------------- /arm7/source/irqWait.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern void irq_wait(bool clearFlags, u32 mask); 4 | -------------------------------------------------------------------------------- /arm7/source/irqWait.s: -------------------------------------------------------------------------------- 1 | .arm 2 | .cpu arm7tdmi 3 | .text 4 | 5 | .global irq_wait 6 | irq_wait: 7 | push {lr} 8 | 9 | mrs r12, cpsr 10 | orr r2, r12, #0x80 //cpsr disable irq 11 | msr cpsr_c, r2 12 | 13 | and r12, r12, #0x80 14 | 15 | mov r2, #0x04000000 16 | ldr r3, [r2, #0x208] //old ime state 17 | mov lr, #1 18 | str lr, [r2, #0x208] //enable ime 19 | 20 | cmp r0, #1 21 | bne 1f 22 | 23 | ldr lr, [r2, #-8] //check flags 24 | bic lr, lr, r1 25 | str lr, [r2, #-8] 26 | 27 | 1: 28 | ldr lr, [r2, #-8] //check flags 29 | tst lr, r1 30 | bne 2f 31 | 32 | swi (6 << 16) //halt 33 | mrs lr, cpsr 34 | bic lr, lr, #0x80 //cpsr enable irq 35 | msr cpsr_c, lr 36 | //here the interrupt handler will be called 37 | mrs lr, cpsr 38 | orr lr, lr, #0x80 //cpsr disable irq 39 | msr cpsr_c, lr 40 | b 1b 41 | 42 | 2: 43 | bic lr, lr, r1 44 | str lr, [r2, #-8] 45 | 46 | str r3, [r2, #0x208] //restore ime 47 | mrs lr, cpsr 48 | bic lr, lr, #0x80 49 | orr lr, lr, r12 50 | msr cpsr_c, lr 51 | 52 | pop {r3} 53 | bx r3 -------------------------------------------------------------------------------- /arm7/source/isdprint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // #define PRINT_DEBUG 4 | 5 | #ifdef PRINT_DEBUG 6 | void isnd_printchar(char c); 7 | void isnd_initPrint(); 8 | void isnd_printf(const char *fmt, ...); 9 | #endif 10 | -------------------------------------------------------------------------------- /arm7/source/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../../common/ipc.h" 5 | #include "fat/ff.h" 6 | #include "isdprint.h" 7 | #include "fat.h" 8 | #include "fvPlayer7.h" 9 | #include "../../common/twlwram.h" 10 | 11 | volatile bool exitflag = false; 12 | 13 | static u16 sVBlankTime; 14 | static u16 sLastVBlankTime; 15 | static int sSleepCounter = 0; 16 | 17 | enum 18 | { 19 | KEYXY_TOUCH = (1 << 6), 20 | KEYXY_LID = (1 << 7) 21 | }; 22 | 23 | // fixes issue with missing touch inputs 24 | static void inputGetAndSendNew(void) 25 | { 26 | touchPosition tempPos = { 0 }; 27 | FifoMessage msg = { 0 }; 28 | 29 | u16 keys = REG_KEYXY; 30 | 31 | if (!touchPenDown()) 32 | keys |= KEYXY_TOUCH; 33 | else 34 | keys &= ~KEYXY_TOUCH; 35 | 36 | msg.SystemInput.keys = keys; 37 | 38 | if (!(keys & KEYXY_TOUCH)) 39 | { 40 | msg.SystemInput.keys |= KEYXY_TOUCH; 41 | 42 | touchReadXY(&tempPos); 43 | 44 | if (tempPos.rawx && tempPos.rawy) 45 | { 46 | msg.SystemInput.keys &= ~KEYXY_TOUCH; 47 | msg.SystemInput.touch = tempPos; 48 | } 49 | } 50 | 51 | if (keys & KEYXY_LID) 52 | sSleepCounter++; 53 | else 54 | sSleepCounter = 0; 55 | 56 | // sleep if lid has been closed for 20 frames 57 | if (sSleepCounter >= 20) 58 | { 59 | systemSleep(); 60 | sSleepCounter = 0; 61 | } 62 | 63 | msg.type = SYS_INPUT_MESSAGE; 64 | 65 | fifoSendDatamsg(FIFO_SYSTEM, sizeof(msg), (u8*)&msg); 66 | } 67 | 68 | void VblankHandler(void) 69 | { 70 | inputGetAndSendNew(); 71 | if (0 == (REG_KEYINPUT & (KEY_SELECT | KEY_START | KEY_L | KEY_R))) 72 | exitflag = true; 73 | int time = sVBlankTime - sLastVBlankTime; 74 | if (time < 0) 75 | time += 65536; 76 | // isnd_printf("%d\n", time); 77 | sLastVBlankTime = sVBlankTime; 78 | // if(sTimingStarted) 79 | // { 80 | // sVblankCount++; 81 | // u64 time = (((u64)TIMER_DATA(0) | ((u64)TIMER_DATA(1) << 16) | ((u64)TIMER_DATA(2) << 32)) + (sVblankCount + 1 82 | // >> 1)) / sVblankCount; isnd_printf("%d\n", (u32)((33513982LL * 10000) / time)); 83 | // } 84 | // else if (sStartTiming) 85 | // { 86 | // TIMER_CR(0) = 0; 87 | // TIMER_CR(1) = 0; 88 | // TIMER_CR(2) = 0; 89 | 90 | // TIMER_DATA(0) = 0; 91 | // TIMER_DATA(1) = 0; 92 | // TIMER_DATA(2) = 0; 93 | 94 | // TIMER_CR(2) = TIMER_CASCADE | TIMER_ENABLE; 95 | // TIMER_CR(1) = TIMER_CASCADE | TIMER_ENABLE; 96 | // TIMER_CR(0) = TIMER_ENABLE; 97 | 98 | // sStartTiming = FALSE; 99 | // sTimingStarted = TRUE; 100 | // } 101 | } 102 | 103 | void powerButtonCB() 104 | { 105 | exitflag = true; 106 | } 107 | 108 | extern void enableSound(void); 109 | 110 | int main(void) 111 | { 112 | // clear sound registers 113 | dmaFillWords(0, (void*)0x04000400, 0x100); 114 | 115 | if (isDSiMode()) 116 | { 117 | if (twr_isUnlockable()) 118 | twr_unlockAll(); 119 | 120 | if (twr_isUnlocked()) 121 | { 122 | twr_setBlockMapping(TWR_WRAM_BLOCK_B, 0x03100000, 0x40000, TWR_WRAM_BLOCK_IMAGE_SIZE_256K); 123 | twr_setBlockMapping(TWR_WRAM_BLOCK_C, 0x03140000, 0x40000, TWR_WRAM_BLOCK_IMAGE_SIZE_256K); 124 | } 125 | 126 | // switch to 47kHz output 127 | REG_SNDEXTCNT = 0; 128 | REG_SNDEXTCNT = SNDEXTCNT_FREQ_47KHZ | SNDEXTCNT_RATIO(8); 129 | cdcWriteReg(CDC_CONTROL, 6, 15); 130 | cdcWriteReg(CDC_CONTROL, 11, 0x85); 131 | cdcWriteReg(CDC_CONTROL, 18, 0x85); 132 | REG_SNDEXTCNT |= SNDEXTCNT_ENABLE; 133 | } 134 | 135 | enableSound(); 136 | 137 | readUserSettings(); 138 | ledBlink(0); 139 | 140 | irqInit(); 141 | initClockIRQ(); 142 | fifoInit(); 143 | touchInit(); 144 | 145 | installSystemFIFO(); 146 | 147 | irqSet(IRQ_VBLANK, VblankHandler); 148 | 149 | irqEnable(IRQ_VBLANK | IRQ_NETWORK); 150 | 151 | setPowerButtonCB(powerButtonCB); 152 | 153 | #ifdef PRINT_DEBUG 154 | isnd_initPrint(); 155 | #endif 156 | 157 | fat_init(); 158 | fv_init(); 159 | 160 | swiWaitForVBlank(); 161 | 162 | while (!exitflag) 163 | { 164 | fv_main(); 165 | } 166 | return 0; 167 | } 168 | -------------------------------------------------------------------------------- /arm9/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITARM)),) 5 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 6 | endif 7 | 8 | include $(DEVKITARM)/ds_rules 9 | 10 | #--------------------------------------------------------------------------------- 11 | # BUILD is the directory where object files & intermediate files will be placed 12 | # SOURCES is a list of directories containing source code 13 | # INCLUDES is a list of directories containing extra header files 14 | # DATA is a list of directories containing binary files 15 | # all directories are relative to this makefile 16 | #--------------------------------------------------------------------------------- 17 | BUILD := build 18 | SOURCES := source source/FastVideo ../common source/gui source/gui/core 19 | INCLUDES := include 20 | DATA := data 21 | GRAPHICS := gfx 22 | 23 | 24 | #--------------------------------------------------------------------------------- 25 | # options for code generation 26 | #--------------------------------------------------------------------------------- 27 | ARCH := -marm -mthumb-interwork 28 | 29 | CFLAGS := -g -Wall -O3\ 30 | -ffunction-sections -fdata-sections \ 31 | -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\ 32 | -ffast-math \ 33 | $(ARCH) 34 | 35 | CFLAGS += $(INCLUDE) -DARM9 36 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 37 | 38 | ASFLAGS := -g $(ARCH) -march=armv5te -mtune=arm946e-s $(INCLUDE) 39 | 40 | LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 41 | 42 | #--------------------------------------------------------------------------------- 43 | # any extra libraries we wish to link with the project 44 | #--------------------------------------------------------------------------------- 45 | LIBS := -lnds9 46 | 47 | #--------------------------------------------------------------------------------- 48 | # list of directories containing libraries, this must be the top level containing 49 | # include and lib 50 | #--------------------------------------------------------------------------------- 51 | LIBDIRS := $(LIBNDS) 52 | 53 | #--------------------------------------------------------------------------------- 54 | # no real need to edit anything past this point unless you need to add additional 55 | # rules for different file extensions 56 | #--------------------------------------------------------------------------------- 57 | ifneq ($(BUILD),$(notdir $(CURDIR))) 58 | #--------------------------------------------------------------------------------- 59 | 60 | export ARM9ELF := $(CURDIR)/$(TARGET).elf 61 | export DEPSDIR := $(CURDIR)/$(BUILD) 62 | 63 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 64 | $(foreach dir,$(DATA),$(CURDIR)/$(dir))\ 65 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) 66 | 67 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 68 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 69 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 70 | PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) 71 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 72 | 73 | #--------------------------------------------------------------------------------- 74 | # use CXX for linking C++ projects, CC for standard C 75 | #--------------------------------------------------------------------------------- 76 | ifeq ($(strip $(CPPFILES)),) 77 | #--------------------------------------------------------------------------------- 78 | export LD := $(CC) 79 | #--------------------------------------------------------------------------------- 80 | else 81 | #--------------------------------------------------------------------------------- 82 | export LD := $(CXX) 83 | #--------------------------------------------------------------------------------- 84 | endif 85 | #--------------------------------------------------------------------------------- 86 | 87 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 88 | $(PNGFILES:.png=.o)\ 89 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 90 | 91 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 92 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 93 | -I$(CURDIR)/$(BUILD) 94 | 95 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 96 | 97 | .PHONY: $(BUILD) clean 98 | 99 | #--------------------------------------------------------------------------------- 100 | $(BUILD): 101 | @[ -d $@ ] || mkdir -p $@ 102 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 103 | 104 | #--------------------------------------------------------------------------------- 105 | clean: 106 | @echo clean ... 107 | @rm -fr $(BUILD) *.elf *.nds* *.bin 108 | 109 | 110 | #--------------------------------------------------------------------------------- 111 | else 112 | 113 | #--------------------------------------------------------------------------------- 114 | # main targets 115 | #--------------------------------------------------------------------------------- 116 | $(ARM9ELF) : $(OFILES) 117 | @echo linking $(notdir $@) 118 | @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ 119 | 120 | #--------------------------------------------------------------------------------- 121 | # you need a rule like this for each extension you use as binary data 122 | #--------------------------------------------------------------------------------- 123 | %.bin.o : %.bin 124 | #--------------------------------------------------------------------------------- 125 | @echo $(notdir $<) 126 | @$(bin2o) 127 | 128 | %.ntft.o : %.ntft 129 | @echo $(notdir $<) 130 | @$(bin2o) 131 | 132 | #--------------------------------------------------------------------------------- 133 | # This rule creates assembly source files using grit 134 | # grit takes an image file and a .grit describing how the file is to be processed 135 | # add additional rules like this for each image extension 136 | # you use in the graphics folders 137 | #--------------------------------------------------------------------------------- 138 | %.s %.h: %.png %.grit 139 | #--------------------------------------------------------------------------------- 140 | grit $< -fts -o$* 141 | 142 | -include $(DEPSDIR)/*.d 143 | 144 | #--------------------------------------------------------------------------------------- 145 | endif 146 | #--------------------------------------------------------------------------------------- 147 | -------------------------------------------------------------------------------- /arm9/data/RobotoMedium13.ntft: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gericom/FastVideoDSPlayer/1250dee262ecaf0618c6217d16a9d97684411015/arm9/data/RobotoMedium13.ntft -------------------------------------------------------------------------------- /arm9/data/RobotoRegular10.ntft: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gericom/FastVideoDSPlayer/1250dee262ecaf0618c6217d16a9d97684411015/arm9/data/RobotoRegular10.ntft -------------------------------------------------------------------------------- /arm9/gfx/circle0.grit: -------------------------------------------------------------------------------- 1 | # tile format 2 | -gt 3 | 4 | # graphics bit depth is 4 (16 color) 5 | -gB4 6 | 7 | -p! -------------------------------------------------------------------------------- /arm9/gfx/circle0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gericom/FastVideoDSPlayer/1250dee262ecaf0618c6217d16a9d97684411015/arm9/gfx/circle0.png -------------------------------------------------------------------------------- /arm9/gfx/circle1.grit: -------------------------------------------------------------------------------- 1 | # tile format 2 | -gt 3 | 4 | # graphics bit depth is 4 (16 color) 5 | -gB4 6 | 7 | -p! -------------------------------------------------------------------------------- /arm9/gfx/circle1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gericom/FastVideoDSPlayer/1250dee262ecaf0618c6217d16a9d97684411015/arm9/gfx/circle1.png -------------------------------------------------------------------------------- /arm9/gfx/iconPause.grit: -------------------------------------------------------------------------------- 1 | # tile format 2 | -gt 3 | 4 | # graphics bit depth is 4 (16 color) 5 | -gB4 6 | 7 | -p! -------------------------------------------------------------------------------- /arm9/gfx/iconPause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gericom/FastVideoDSPlayer/1250dee262ecaf0618c6217d16a9d97684411015/arm9/gfx/iconPause.png -------------------------------------------------------------------------------- /arm9/gfx/iconPlay.grit: -------------------------------------------------------------------------------- 1 | # tile format 2 | -gt 3 | 4 | # graphics bit depth is 4 (16 color) 5 | -gB4 6 | 7 | -p! -------------------------------------------------------------------------------- /arm9/gfx/iconPlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gericom/FastVideoDSPlayer/1250dee262ecaf0618c6217d16a9d97684411015/arm9/gfx/iconPlay.png -------------------------------------------------------------------------------- /arm9/gfx/playBg.grit: -------------------------------------------------------------------------------- 1 | # tile format 2 | -gt 3 | 4 | # tile reduction by tiles, palette and hflip/vflip 5 | -mRtpf 6 | 7 | # graphics bit depth is 4 (16 color) 8 | -gB4 9 | 10 | -p 11 | -pn16 12 | 13 | # map layout standard bg format 14 | -mLs 15 | 16 | -gzl 17 | -mzl -------------------------------------------------------------------------------- /arm9/gfx/playBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gericom/FastVideoDSPlayer/1250dee262ecaf0618c6217d16a9d97684411015/arm9/gfx/playBg.png -------------------------------------------------------------------------------- /arm9/source/FastVideo/fvConst.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "fvConst.h" 3 | 4 | DTCM_DATA u8 fv_gClampTable[] = 5 | { 6 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 7 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 8 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 9 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 10 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 11 | 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 12 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 13 | 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 14 | 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 15 | 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 16 | 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 17 | 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F 18 | }; 19 | 20 | u32 fv_gDCTQTab2x2[] = 21 | { 22 | 0x00000000, 0x00000008, 0x00000010, 0x00000018, 23 | 0x00000020, 0x00000028, 0x00000030, 0x00000038, 24 | 0x00000040, 0x00000048, 0x00000050, 0x00000058, 25 | 0x00000060, 0x00000068, 0x00000070, 0x00000078, 26 | 0x00000002, 0x0000000A, 0x00000012, 0x0000001A, 27 | 0x00000022, 0x0000002A, 0x00000032, 0x0000003A, 28 | 0x00000042, 0x0000004A, 0x00000052, 0x0000005A, 29 | 0x00000062, 0x0000006A, 0x00000072, 0x0000007A, 30 | 0x00000004, 0x0000000C, 0x00000014, 0x0000001C, 31 | 0x00000024, 0x0000002C, 0x00000034, 0x0000003C, 32 | 0x00000044, 0x0000004C, 0x00000054, 0x0000005C, 33 | 0x00000064, 0x0000006C, 0x00000074, 0x0000007C, 34 | 0x00000006, 0x0000000E, 0x00000016, 0x0000001E, 35 | 0x00000026, 0x0000002E, 0x00000036, 0x0000003E, 36 | 0x00000046, 0x0000004E, 0x00000056, 0x0000005E, 37 | 0x00000066, 0x0000006E, 0x00000076, 0x0000007E 38 | }; -------------------------------------------------------------------------------- /arm9/source/FastVideo/fvConst.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern u8 fv_gDequantCoefs4[]; 4 | extern u8 fv_gDeZigZagTable4x4[]; 5 | extern u32 fv_gDCTQTab2x2[]; 6 | extern u8 fv_gClampTable[]; 7 | extern u8 fv_gVlcIdxTab[]; 8 | extern u8 fv_gVlcBitTab2[]; 9 | extern u32 fv_gVlcPackTab[]; -------------------------------------------------------------------------------- /arm9/source/FastVideo/fvDecoder.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "fvConst.h" 3 | #include "fvDecoder.h" 4 | 5 | void fv_initDecoderAsm(fv_decoder_asm_t* dec) 6 | { 7 | dec->q = 0xFF; 8 | for(int i = 0; i < 64; i++) 9 | dec->qTab[i] = fv_gDCTQTab2x2[i]; 10 | dec->clampTable = &fv_gClampTable[32]; 11 | dec->vlcIdxTab = fv_gVlcIdxTab; 12 | dec->vlcBitTab2 = fv_gVlcBitTab2; 13 | dec->vlcPackTab = fv_gVlcPackTab; 14 | dec->dequant0 = 32 | (23 << 16); 15 | dec->dequant1 = 23 | (64 << 16); 16 | dec->dequantP0 = 32 | (23 << 16); 17 | dec->dequantP1 = 23 | (64 << 16); 18 | // dec->dequantP0 = 32 | (36 << 16); 19 | // dec->dequantP1 = 36 | (48 << 16); 20 | // dec->dequant0 = 64 | (46 << 16); 21 | // dec->dequant1 = 46 | (128 << 16); 22 | } -------------------------------------------------------------------------------- /arm9/source/FastVideo/fvDecoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../../common/fastVideo.h" 3 | 4 | typedef struct 5 | { 6 | u16 qTab[16]; 7 | u8 q; 8 | } fv_decoder_t; 9 | 10 | typedef struct 11 | { 12 | u32 qTab[64]; 13 | const u8* clampTable; 14 | const u8* vlcIdxTab; 15 | const u8* vlcBitTab2; 16 | const u32* vlcPackTab; 17 | u32 dequant0; 18 | u32 dequant1; 19 | u32 dequantP0; 20 | u32 dequantP1; 21 | u32 q; 22 | u32 yBlocks; 23 | u32 texYOffset; 24 | const u32* dlB; 25 | u32 pFrameIsIBlock[24]; 26 | } fv_decoder_asm_t; 27 | 28 | void fv_initDecoder(fv_decoder_t* dec); 29 | void fv_initDecoderAsm(fv_decoder_asm_t* dec); 30 | void fv_decodeFrame(fv_decoder_t* dec, const u16* src, u8* dst); 31 | extern void fv_decodeFrame_asm(fv_decoder_asm_t* dec, const u16* src, u8* dst); 32 | extern const u16* fv_decodePFrameVectors(fv_decoder_asm_t* dec, const u16* src); 33 | extern void fv_decodePFrameDcts(fv_decoder_asm_t* dec, const u16* src, u8* residual); 34 | extern void fv_finishPBlock(fv_decoder_asm_t* dec, const u8* residual, const u16* prediction); 35 | 36 | static inline int fv_frameIsP(const u16* src) 37 | { 38 | return *src >> 15; 39 | } -------------------------------------------------------------------------------- /arm9/source/FastVideo/fvMcData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | extern u32 fv_gMcGxCmdBuf[]; 3 | extern u32 fv_gMcGxCmdBufA[]; -------------------------------------------------------------------------------- /arm9/source/FastVideo/fvMcData.s: -------------------------------------------------------------------------------- 1 | .section .data 2 | 3 | #define GX_CMD_PACK(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) 4 | 5 | #define GX_CMD_NOP 0x00 6 | 7 | #define GX_CMD_TRANSLATE 0x20 8 | #define GX_CMD_COLOR 0x20 9 | #define GX_CMD_NORMAL 0x21 10 | #define GX_CMD_TEXCOORD 0x22 11 | #define GX_CMD_VTX_16 0x23 12 | #define GX_CMD_VTX_10 0x24 13 | #define GX_CMD_VTX_XY 0x25 14 | #define GX_CMD_VTX_XZ 0x26 15 | #define GX_CMD_VTX_YZ 0x27 16 | #define GX_CMD_VTX_DIFF 0x28 17 | #define GX_CMD_POLY_ATTR 0x29 18 | #define GX_CMD_TEX_PARAM 0x2A 19 | 20 | #define GX_CMD_BEGIN 0x40 21 | #define GX_CMD_END 0x41 22 | 23 | #define GX_CMD_SWAPBUFFERS 0x50 24 | 25 | #define GX_BEGIN_TRIANGLE 0x00 26 | #define GX_BEGIN_QUAD 0x01 27 | #define GX_BEGIN_TRI_STRIP 0x02 28 | #define GX_BEGIN_QUAD_STRIP 0x03 29 | 30 | #define BLOCK_VTX_PACK(x,y,z) (((x) & 0x3FF) | ((((y) >> 3) & 0x3FF) << 10) | ((z) << 20)) 31 | #define VTX_PACK(a,b) (((a) & 0xFFFF) | (((b) & 0xFFFF) << 16)) 32 | 33 | #define HEIGHT 192 //144 34 | 35 | .balign 32 //cache alignment 36 | 37 | .global fv_gMcGxCmdBuf 38 | fv_gMcGxCmdBuf: 39 | .word GX_CMD_PACK(GX_CMD_POLY_ATTR, GX_CMD_BEGIN, GX_CMD_COLOR, 0x71) 40 | .word 0x1F00C0 41 | .word GX_BEGIN_TRI_STRIP 42 | .word 0 43 | .word 0 44 | .word 40 << 6 45 | .rept 11 46 | .word GX_CMD_PACK(GX_CMD_VTX_XY, GX_CMD_VTX_XY, GX_CMD_VTX_XY, GX_CMD_VTX_XY) 47 | .word VTX_PACK(0 << 6, -256 << 3) 48 | .word VTX_PACK(0 << 6, 2 << 3) 49 | .word VTX_PACK(256 << 6, 2 << 3) 50 | .word VTX_PACK(0 << 6, -256 << 3) 51 | .word GX_CMD_PACK(GX_CMD_VTX_XY, GX_CMD_VTX_XY, GX_CMD_VTX_XY, GX_CMD_VTX_XY) 52 | .word VTX_PACK(0 << 6, 2 << 3) 53 | .word VTX_PACK(256 << 6, 2 << 3) 54 | .word VTX_PACK(0 << 6, -256 << 3) 55 | .word VTX_PACK(0 << 6, 2 << 3) 56 | .word GX_CMD_PACK(GX_CMD_VTX_XY, GX_CMD_VTX_XY, GX_CMD_VTX_XY, GX_CMD_VTX_XY) 57 | .word VTX_PACK(256 << 6, 2 << 3) 58 | .word VTX_PACK(0 << 6, -256 << 3) 59 | .word VTX_PACK(0 << 6, 2 << 3) 60 | .word VTX_PACK(256 << 6, 2 << 3) 61 | .endr 62 | .word GX_CMD_END 63 | 64 | .word GX_CMD_PACK(GX_CMD_POLY_ATTR, GX_CMD_TEX_PARAM, GX_CMD_COLOR, GX_CMD_BEGIN) 65 | .word 0x1F0080 66 | .word (0xDED00000)// + ((24 * 256 * 2) >> 3)) 67 | .word 0x7FFF //color 68 | .word GX_BEGIN_TRIANGLE 69 | .global fv_gMcGxCmdBufA 70 | fv_gMcGxCmdBufA: 71 | .space 30768 72 | .balign 32 //cache alignment 73 | .end 74 | -------------------------------------------------------------------------------- /arm9/source/FastVideo/fvPlayer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../../common/ipc.h" 4 | #include "../../common/twlwram.h" 5 | #include "fvMcData.h" 6 | #include "fvPlayer.h" 7 | 8 | static ITCM_CODE void requestDataBuffer(fv_player_t* player) 9 | { 10 | int irq = enterCriticalSection(); 11 | do 12 | { 13 | if (player->isRequesting) 14 | break; 15 | player->isRequesting = TRUE; 16 | 17 | if (isDSiMode()) 18 | { 19 | // map wram block to arm7 20 | int slot = (player->dataBufferWriteIdx * 2) & 7; 21 | if (player->dataBufferWriteIdx < 4) 22 | { 23 | twr_mapWramBSlot(slot, TWR_WRAM_B_SLOT_MASTER_ARM7, slot, true); 24 | twr_mapWramBSlot(slot + 1, TWR_WRAM_B_SLOT_MASTER_ARM7, slot + 1, true); 25 | } 26 | else 27 | { 28 | twr_mapWramCSlot(slot, TWR_WRAM_C_SLOT_MASTER_ARM7, slot, true); 29 | twr_mapWramCSlot(slot + 1, TWR_WRAM_C_SLOT_MASTER_ARM7, slot + 1, true); 30 | } 31 | } 32 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_READ_FRAME, 33 | (u32)fv_getPlayerDataBuffer(player, player->dataBufferWriteIdx))); 34 | } while (0); 35 | leaveCriticalSection(irq); 36 | } 37 | 38 | static ITCM_CODE void fifoHandler(u32 value, void* arg) 39 | { 40 | fv_player_t* player = (fv_player_t*)arg; 41 | u32 cmd = value >> IPC_CMD_CMD_SHIFT; 42 | if (cmd == IPC_CMD_READ_FRAME) 43 | { 44 | if (isDSiMode()) 45 | { 46 | // map wram block to arm9 47 | int slot = (player->dataBufferWriteIdx * 2) & 7; 48 | if (player->dataBufferWriteIdx < 4) 49 | { 50 | twr_mapWramBSlot(slot, TWR_WRAM_B_SLOT_MASTER_ARM9, slot, true); 51 | twr_mapWramBSlot(slot + 1, TWR_WRAM_B_SLOT_MASTER_ARM9, slot + 1, true); 52 | } 53 | else 54 | { 55 | twr_mapWramCSlot(slot, TWR_WRAM_C_SLOT_MASTER_ARM9, slot, true); 56 | twr_mapWramCSlot(slot + 1, TWR_WRAM_C_SLOT_MASTER_ARM9, slot + 1, true); 57 | } 58 | } 59 | player->isRequesting = FALSE; 60 | u32 length = value & IPC_CMD_ARG_MASK; 61 | if (length == 0) 62 | { 63 | // todo: this means the video has ended 64 | return; 65 | } 66 | player->frameDataSizes[player->dataBufferWriteIdx] = length; 67 | player->frameHasAudio[player->dataBufferWriteIdx] = true; 68 | if (++player->dataBufferWriteIdx == FV_PLAYER_DATA_BUFFER_COUNT) 69 | player->dataBufferWriteIdx = 0; 70 | player->validDataBufferCount++; 71 | if (--player->freeDataBufferCount != 0 && player->isPlaying) 72 | requestDataBuffer(player); 73 | } 74 | else if (cmd == IPC_CMD_GOTO_KEYFRAME) 75 | { 76 | player->curFrame = value & IPC_CMD_ARG_MASK; 77 | player->seekComplete = true; 78 | } 79 | else if (cmd == IPC_CMD_GOTO_NEAREST_KEYFRAME) 80 | { 81 | player->lastKeyFrame = value & IPC_CMD_ARG_MASK; 82 | } 83 | // else if ((value >> IPC_CMD_CMD_SHIFT) == 0xE) 84 | // { 85 | // // iprintf("%d\n", value & 0xFFFF); 86 | // } 87 | } 88 | 89 | static volatile DTCM_BSS int sVBlankCount; 90 | 91 | static ITCM_CODE void vblankHandler() 92 | { 93 | sVBlankCount++; 94 | } 95 | 96 | static void setupProjectionMtx() 97 | { 98 | glMatrixMode(GL_PROJECTION); 99 | MATRIX_LOAD4x3 = divf32(inttof32(2), (256 << 6) - 0); 100 | MATRIX_LOAD4x3 = 0; 101 | MATRIX_LOAD4x3 = 0; 102 | 103 | MATRIX_LOAD4x3 = 0; 104 | MATRIX_LOAD4x3 = -21845; 105 | MATRIX_LOAD4x3 = 0; 106 | 107 | MATRIX_LOAD4x3 = 0; 108 | MATRIX_LOAD4x3 = 0; 109 | MATRIX_LOAD4x3 = 4096 >> 5; 110 | 111 | MATRIX_LOAD4x3 = -divf32((256 << 6), (256 << 6)); 112 | MATRIX_LOAD4x3 = -divf32((192 << 6), -(192 << 6)); 113 | MATRIX_LOAD4x3 = 0; 114 | } 115 | 116 | static void setupTextureMtx() 117 | { 118 | glMatrixMode(GL_TEXTURE); 119 | MATRIX_LOAD4x3 = 4096 * 64 * 16; 120 | MATRIX_LOAD4x3 = 0; 121 | MATRIX_LOAD4x3 = 0; 122 | 123 | MATRIX_LOAD4x3 = 0; 124 | MATRIX_LOAD4x3 = 4096 * 64 * 8 * 16; 125 | MATRIX_LOAD4x3 = 0; 126 | 127 | MATRIX_LOAD4x3 = 0; 128 | MATRIX_LOAD4x3 = 0; 129 | MATRIX_LOAD4x3 = 0; 130 | 131 | MATRIX_LOAD4x3 = 0; 132 | MATRIX_LOAD4x3 = 0; 133 | MATRIX_LOAD4x3 = 0; 134 | } 135 | 136 | static void setup3DEngine() 137 | { 138 | powerOn(POWER_3D_CORE | POWER_MATRIX); 139 | while (GFX_BUSY) 140 | ; 141 | GFX_STATUS |= 1 << 29; // clear fifo 142 | glResetMatrixStack(); 143 | glFlush(1); 144 | GFX_CONTROL = 0x9; 145 | glClearColor(0, 0, 0, 31); 146 | glClearPolyID(63); 147 | glClearDepth(0x7FFF); 148 | glFlush(0); 149 | setupProjectionMtx(); 150 | setupTextureMtx(); 151 | glMatrixMode(GL_MODELVIEW); 152 | glLoadIdentity(); 153 | glViewport(0, 0, 255, 191); 154 | } 155 | 156 | static ITCM_CODE void executeDisplayList(const fv_player_t* player) 157 | { 158 | DMA3_SRC = (u32)fv_gMcGxCmdBuf; 159 | DMA3_DEST = (u32)&GFX_FIFO; 160 | DMA3_CR = DMA_FIFO | (player->dlLength >> 2); 161 | } 162 | 163 | static ITCM_CODE void startCapture(int dst) 164 | { 165 | REG_DISPCAPCNT = DCAP_ENABLE | DCAP_MODE(DCAP_MODE_A) | DCAP_BANK(dst) | DCAP_SIZE(DCAP_SIZE_256x192) | 166 | DCAP_SRC_A(DCAP_SRC_A_3DONLY); 167 | } 168 | 169 | #define BLOCK_VTX_PACK(x, y, z) (((x)&0x3FF) | ((((y) >> 3) & 0x3FF) << 10) | ((z) << 20)) 170 | 171 | static u32* makeMbRenderDl(u32* displayList, int height, u32 texParam) 172 | { 173 | int y = 0; 174 | for (; y < height / 8 / 2; y++) 175 | { 176 | for (int x = 0; x < 256 / 8; x++) 177 | { 178 | *displayList++ = FIFO_COMMAND_PACK(FIFO_TEX_COORD, FIFO_VERTEX10, FIFO_VERTEX10, FIFO_VERTEX10); 179 | *displayList++ = 0; // texcoord placeholder 180 | *displayList++ = BLOCK_VTX_PACK(x * 8, y * 8 - 8, 33 - x); 181 | *displayList++ = BLOCK_VTX_PACK(x * 8, y * 8 + 8, 32 - x); 182 | *displayList++ = BLOCK_VTX_PACK(x * 8 + 16, y * 8 + 8, 32 - x); 183 | } 184 | } 185 | *displayList++ = FIFO_COMMAND_PACK(FIFO_END, FIFO_TEX_FORMAT, FIFO_BEGIN, FIFO_NOP); 186 | *displayList++ = texParam; 187 | *displayList++ = GL_TRIANGLES; 188 | for (; y < height / 8; y++) 189 | { 190 | for (int x = 0; x < 256 / 8; x++) 191 | { 192 | *displayList++ = FIFO_COMMAND_PACK(FIFO_TEX_COORD, FIFO_VERTEX10, FIFO_VERTEX10, FIFO_VERTEX10); 193 | *displayList++ = 0; // texcoord placeholder 194 | *displayList++ = BLOCK_VTX_PACK(x * 8, y * 8 - 8, 33 - x); 195 | *displayList++ = BLOCK_VTX_PACK(x * 8, y * 8 + 8, 32 - x); 196 | *displayList++ = BLOCK_VTX_PACK(x * 8 + 16, y * 8 + 8, 32 - x); 197 | } 198 | } 199 | return displayList; 200 | } 201 | 202 | bool fv_initPlayer(fv_player_t* player, const char* filePath, bool useWram) 203 | { 204 | memset(player, 0, sizeof(fv_player_t)); 205 | if (isDSiMode() && twr_isUnlocked() && useWram) 206 | player->dataBuffer = (u8*)twr_getBlockAddress(TWR_WRAM_BLOCK_B); 207 | else 208 | player->dataBuffer = memalign(32, FV_PLAYER_DATA_BUFFER_SIZE * FV_PLAYER_DATA_BUFFER_COUNT); 209 | player->fvHeader = memalign(32, sizeof(fv_header_t)); 210 | player->dataBufferReadIdx = 0; 211 | player->dataBufferWriteIdx = 0; 212 | player->validDataBufferCount = 0; 213 | player->stage1Buffer = -1; 214 | player->stage2Buffer = -1; 215 | fv_initDecoderAsm(&player->decoder); 216 | vramSetBankA(VRAM_A_LCD); 217 | vramSetBankB(VRAM_B_LCD); 218 | vramSetBankC(VRAM_C_LCD); 219 | 220 | // open file 221 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_OPEN_FILE, filePath)); 222 | fifoWaitValue32(FIFO_USER_01); 223 | if ((fifoGetValue32(FIFO_USER_01) & IPC_CMD_ARG_MASK) == 0) 224 | return false; 225 | 226 | // open file and read header 227 | DC_InvalidateRange(player->fvHeader, sizeof(fv_header_t)); 228 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_READ_HEADER, (u32)player->fvHeader)); 229 | fifoWaitValue32(FIFO_USER_01); 230 | player->vblankPerFrame = fifoGetValue32(FIFO_USER_01) & IPC_CMD_ARG_MASK; 231 | if (player->fvHeader->signature != FV_SIGNATURE) 232 | return false; 233 | if (player->vblankPerFrame == 0) 234 | return false; 235 | 236 | int height = player->fvHeader->height; 237 | 238 | player->decoder.yBlocks = height / 8; 239 | player->decoder.texYOffset = (256 - height) << 1; 240 | u32 texParam = 0xDED00000 + (((-(256 - height) * 256 * 2) >> 3) & 0xFFFF); 241 | 242 | // make display list 243 | u32* displayList = (u32*)fv_gMcGxCmdBufA; 244 | displayList = makeMbRenderDl(displayList, height, texParam); 245 | *displayList++ = FIFO_COMMAND_PACK(FIFO_END, FIFO_POLY_FORMAT, FIFO_TEX_FORMAT, FIFO_BEGIN); 246 | *displayList++ = 0x0F4080; 247 | *displayList++ = 0xDED00000; 248 | *displayList++ = GL_TRIANGLES; 249 | player->decoder.dlB = displayList + 1; 250 | displayList = makeMbRenderDl(displayList, height, texParam); 251 | *displayList++ = FIFO_COMMAND_PACK(FIFO_END, FIFO_FLUSH, FIFO_NOP, FIFO_NOP); 252 | *displayList++ = 0; 253 | 254 | player->dlLength = (u32)displayList - (u32)fv_gMcGxCmdBuf; 255 | 256 | DC_FlushRange(fv_gMcGxCmdBuf, player->dlLength); 257 | 258 | // int irq = enterCriticalSection(); 259 | // iprintf("fps %d/%d\n", player->fvHeader->fpsNum, player->fvHeader->fpsDen); 260 | // iprintf("lcd %d/%d\n", player->fvHeader->fpsNum * player->vblankPerFrame, player->fvHeader->fpsDen); 261 | // leaveCriticalSection(irq); 262 | 263 | fifoSetValue32Handler(FIFO_USER_01, fifoHandler, player); 264 | setup3DEngine(); 265 | 266 | player->lastKeyFrame = 0; 267 | player->curFrame = 0; 268 | 269 | return true; 270 | } 271 | 272 | void fv_destroyPlayer(fv_player_t* player) 273 | { 274 | fifoSetValue32Handler(FIFO_USER_01, NULL, NULL); 275 | if (!isDSiMode()) 276 | free(player->dataBuffer); 277 | } 278 | 279 | ITCM_CODE void fv_startPlayer(fv_player_t* player) 280 | { 281 | if (VRAM_A_CR == 0x81) 282 | { 283 | player->nextVramBlock = 1; 284 | VRAM_B_CR = 0x80; 285 | VRAM_C_CR = 0x80; 286 | } 287 | else if (VRAM_B_CR == 0x81) 288 | { 289 | player->nextVramBlock = 2; 290 | VRAM_A_CR = 0x80; 291 | VRAM_C_CR = 0x80; 292 | } 293 | else 294 | { 295 | player->nextVramBlock = 0; 296 | VRAM_A_CR = 0x80; 297 | VRAM_B_CR = 0x80; 298 | } 299 | 300 | player->stage3VramBlock = -1; 301 | player->stage2Buffer = -1; 302 | player->stage1Buffer = -1; 303 | player->freeDataBufferCount = FV_PLAYER_DATA_BUFFER_COUNT; 304 | player->validDataBufferCount = 0; 305 | player->dataBufferReadIdx = 0; 306 | player->dataBufferWriteIdx = 0; 307 | player->isPlaying = true; 308 | // start by loading the first blocks of data 309 | requestDataBuffer(player); 310 | while (player->validDataBufferCount < 3) 311 | ; 312 | swiWaitForVBlank(); 313 | sVBlankCount = 0; 314 | irqSet(IRQ_VBLANK, vblankHandler); 315 | player->lateCount = 0; 316 | player->audioStarted = false; 317 | player->firstKeyFrame = true; 318 | 319 | REG_DISPCNT = MODE_5_2D | DISPLAY_BG2_ACTIVE; 320 | REG_BG2CNT = (u16)BgSize_B16_256x256; 321 | REG_BG2PA = 256; 322 | REG_BG2PB = 0; 323 | REG_BG2PC = 0; 324 | REG_BG2PD = 256; 325 | REG_BG2X = 0; 326 | REG_BG2Y = -((192 - player->fvHeader->height) >> 1) << 8; 327 | } 328 | 329 | static void startAudio(void) 330 | { 331 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_CONTROL_AUDIO, IPC_ARG_CONTROL_AUDIO_START)); 332 | } 333 | 334 | static void stopAudio(void) 335 | { 336 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_CONTROL_AUDIO, IPC_ARG_CONTROL_AUDIO_STOP)); 337 | } 338 | 339 | static void stopAudioClearQueue(void) 340 | { 341 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_CONTROL_AUDIO, IPC_ARG_CONTROL_AUDIO_STOP_CLEAR)); 342 | } 343 | 344 | ITCM_CODE void fv_updatePlayer(fv_player_t* player) 345 | { 346 | if (player->validDataBufferCount > 0) 347 | { 348 | player->stage1Buffer = player->dataBufferReadIdx; 349 | if (++player->dataBufferReadIdx == FV_PLAYER_DATA_BUFFER_COUNT) 350 | player->dataBufferReadIdx = 0; 351 | int irq = enterCriticalSection(); 352 | { 353 | player->validDataBufferCount--; 354 | } 355 | leaveCriticalSection(irq); 356 | } 357 | const u16* nextStage2DataPtr = NULL; 358 | int nextStage2IsPFrame = FALSE; 359 | int doStartCapture = FALSE; 360 | int stage1VramBlock = -1; 361 | if (player->stage1Buffer != -1) 362 | { 363 | stage1VramBlock = player->nextVramBlock; 364 | if (++player->nextVramBlock == 3) 365 | player->nextVramBlock = 0; 366 | const u16* dataBuf = fv_getPlayerDataBuffer(player, player->stage1Buffer); 367 | DC_FlushAll(); 368 | // DC_InvalidateRange(dataBuf, player->frameDataSizes[player->stage1Buffer]); 369 | int isPFrame = fv_frameIsP(dataBuf); 370 | if (isPFrame) 371 | { 372 | dataBuf = fv_decodePFrameVectors(&player->decoder, dataBuf); 373 | executeDisplayList(player); 374 | doStartCapture = TRUE; 375 | } 376 | nextStage2DataPtr = dataBuf; 377 | nextStage2IsPFrame = isPFrame; 378 | } 379 | bool nextStage3HasAudio = false; 380 | if (player->stage2Buffer != -1) 381 | { 382 | if (player->stage2IsPFrame) 383 | { 384 | fv_decodePFrameDcts(&player->decoder, player->stage2DataPtr, (u8*)VRAM_D); 385 | // DC_InvalidateRange((u16*)((u32)VRAM_A + 0x20000 * player->stage2VramBlock), 256 * 386 | // player->fvHeader->height * 2); 387 | DC_FlushAll(); 388 | while (sVBlankCount == 0 && REG_VCOUNT >= 192) 389 | ; 390 | fv_finishPBlock(&player->decoder, (u8*)VRAM_D, (u16*)((u32)VRAM_A + 0x20000 * player->stage2VramBlock)); 391 | } 392 | else 393 | { 394 | fv_decodeFrame_asm(&player->decoder, player->stage2DataPtr, 395 | (u8*)((u32)VRAM_A + 0x20000 * player->stage2VramBlock)); 396 | } 397 | nextStage3HasAudio = player->frameHasAudio[player->stage2Buffer]; 398 | int irq = enterCriticalSection(); 399 | { 400 | player->freeDataBufferCount++; 401 | } 402 | leaveCriticalSection(irq); 403 | if (player->isPlaying) 404 | requestDataBuffer(player); 405 | DC_FlushAll(); 406 | // DC_FlushRange((u16*)((u32)VRAM_A + 0x20000 * player->stage2VramBlock), 256 * player->fvHeader->height * 2); 407 | } 408 | if ((sVBlankCount == player->vblankPerFrame && (REG_VCOUNT >= 245 || REG_VCOUNT < 192)) || 409 | sVBlankCount > player->vblankPerFrame) 410 | { 411 | player->lateCount++; 412 | int irq = enterCriticalSection(); 413 | iprintf("late %d, %d, %d\n", player->lateCount, player->stage2IsPFrame, REG_VCOUNT); 414 | leaveCriticalSection(irq); 415 | swiWaitForVBlank(); 416 | } 417 | else 418 | { 419 | while (sVBlankCount < player->vblankPerFrame) 420 | { 421 | // halt to save energy, if a vblank irq already happened this will return immediately 422 | swiIntrWait(0, IRQ_VBLANK); 423 | } 424 | } 425 | sVBlankCount = 0; 426 | 427 | // iprintf("\n%d", GFX_RDLINES_COUNT); 428 | 429 | if (player->stage1Buffer != -1) 430 | { 431 | *(vu8*)(0x04000240 + stage1VramBlock) = 0x80; // map to lcdc 432 | } 433 | if (player->stage3VramBlock != -1) 434 | { 435 | *(vu8*)(0x04000240 + player->stage3VramBlock) = 0x81; // map to bg 436 | player->stage3VramBlock = -1; 437 | if (!player->audioStarted && player->stage3HasAudio) 438 | { 439 | player->audioStarted = true; 440 | startAudio(); 441 | } 442 | 443 | if (player->firstKeyFrame) 444 | player->firstKeyFrame = false; 445 | else 446 | { 447 | player->curFrame++; 448 | if (!player->stage3IsPFrame) 449 | player->lastKeyFrame++; 450 | } 451 | } 452 | if (player->stage2Buffer != -1) 453 | { 454 | *(vu8*)(0x04000240 + player->stage2VramBlock) = 0x83; // map to tex 455 | player->stage3VramBlock = player->stage2VramBlock; 456 | player->stage2VramBlock = -1; 457 | player->stage3IsPFrame = player->stage2IsPFrame; 458 | player->stage3HasAudio = nextStage3HasAudio; 459 | } 460 | player->stage2Buffer = player->stage1Buffer; 461 | player->stage1Buffer = -1; 462 | player->stage2DataPtr = nextStage2DataPtr; 463 | player->stage2IsPFrame = nextStage2IsPFrame; 464 | player->stage2VramBlock = stage1VramBlock; 465 | if (doStartCapture) 466 | startCapture(stage1VramBlock); 467 | } 468 | 469 | void fv_pausePlayer(fv_player_t* player) 470 | { 471 | player->isPlaying = false; 472 | while (player->isRequesting) 473 | ; 474 | stopAudioClearQueue(); 475 | player->audioStarted = false; 476 | for (int i = 0; i < FV_PLAYER_DATA_BUFFER_COUNT; i++) 477 | player->frameHasAudio[i] = false; 478 | player->stage3HasAudio = false; 479 | } 480 | 481 | void fv_resumePlayer(fv_player_t* player) 482 | { 483 | sVBlankCount = 0; 484 | player->isPlaying = true; 485 | } 486 | 487 | void fv_gotoKeyFrame(fv_player_t* player, u32 keyFrame) 488 | { 489 | if (keyFrame >= player->fvHeader->nrKeyFrames) 490 | keyFrame = player->fvHeader->nrKeyFrames - 1; 491 | 492 | player->isPlaying = false; 493 | player->seekComplete = false; 494 | while (player->isRequesting) 495 | ; 496 | stopAudio(); 497 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_GOTO_KEYFRAME, keyFrame)); 498 | while (!player->seekComplete) 499 | ; 500 | while (REG_DISPCAPCNT & DCAP_ENABLE) 501 | ; 502 | player->lastKeyFrame = keyFrame; 503 | } 504 | 505 | void fv_gotoNearestKeyFrame(fv_player_t* player, u32 frame) 506 | { 507 | if (frame >= player->fvHeader->nrFrames) 508 | frame = player->fvHeader->nrFrames - 1; 509 | 510 | player->isPlaying = false; 511 | player->seekComplete = false; 512 | while (player->isRequesting) 513 | ; 514 | stopAudio(); 515 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_GOTO_NEAREST_KEYFRAME, frame)); 516 | while (!player->seekComplete) 517 | ; 518 | while (REG_DISPCAPCNT & DCAP_ENABLE) 519 | ; 520 | } 521 | -------------------------------------------------------------------------------- /arm9/source/FastVideo/fvPlayer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "fvDecoder.h" 3 | 4 | //the player has 4 stages 5 | //0 - fetching video data -> done by arm7 6 | //1 - decoding vectors of P frames and sending DL to GE 7 | //2 - decoding full I frames or dcts of P frames 8 | //3 - finishing P frames 9 | 10 | #define FV_PLAYER_DATA_BUFFER_SIZE (64 * 1024) 11 | #define FV_PLAYER_DATA_BUFFER_COUNT 6 12 | 13 | typedef struct 14 | { 15 | u8* dataBuffer; //cache-aligned buffer containing encoded frame data 16 | int frameDataSizes[FV_PLAYER_DATA_BUFFER_COUNT]; //size of the encoded data in the buffers 17 | bool frameHasAudio[FV_PLAYER_DATA_BUFFER_COUNT]; 18 | int dataBufferReadIdx; 19 | int dataBufferWriteIdx; 20 | volatile int validDataBufferCount; 21 | volatile int freeDataBufferCount; 22 | fv_decoder_asm_t decoder; 23 | int stage1Buffer; 24 | int stage2Buffer; 25 | const u16* stage2DataPtr; 26 | int stage2IsPFrame; 27 | int stage3IsPFrame; 28 | int stage2VramBlock; 29 | int stage3VramBlock; 30 | bool stage3HasAudio; 31 | int nextVramBlock; 32 | volatile int isRequesting; 33 | fv_header_t* fvHeader; 34 | int vblankPerFrame; 35 | u32 dlLength; 36 | int lateCount; 37 | bool audioStarted; 38 | bool firstKeyFrame; 39 | int curFrame; 40 | int lastKeyFrame; 41 | volatile bool seekComplete; 42 | volatile bool isPlaying; 43 | } fv_player_t; 44 | 45 | #ifdef __cplusplus 46 | extern "C" { 47 | #endif 48 | 49 | bool fv_initPlayer(fv_player_t* player, const char* filePath, bool useWram); 50 | void fv_destroyPlayer(fv_player_t* player); 51 | void fv_startPlayer(fv_player_t* player); 52 | void fv_updatePlayer(fv_player_t* player); 53 | 54 | void fv_pausePlayer(fv_player_t* player); 55 | void fv_resumePlayer(fv_player_t* player); 56 | void fv_gotoKeyFrame(fv_player_t* player, u32 keyFrame); 57 | void fv_gotoNearestKeyFrame(fv_player_t* player, u32 frame); 58 | 59 | static inline u32 fv_gotoNextKeyFrame(fv_player_t* player) 60 | { 61 | int frame = player->lastKeyFrame + 1; 62 | if ((u32)frame >= player->fvHeader->nrKeyFrames) 63 | frame = player->fvHeader->nrKeyFrames - 1; 64 | fv_gotoKeyFrame(player, frame); 65 | return frame; 66 | } 67 | 68 | static inline u32 fv_gotoPreviousKeyFrame(fv_player_t* player) 69 | { 70 | int frame = player->lastKeyFrame - 1; 71 | if (frame < 0) 72 | frame = 0; 73 | fv_gotoKeyFrame(player, frame); 74 | return frame; 75 | } 76 | 77 | static inline u16* fv_getPlayerDataBuffer(const fv_player_t* player, int index) 78 | { 79 | return (u16*)&player->dataBuffer[FV_PLAYER_DATA_BUFFER_SIZE * index]; 80 | } 81 | 82 | #ifdef __cplusplus 83 | } 84 | #endif -------------------------------------------------------------------------------- /arm9/source/dldi_stub.s: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------- 2 | 3 | Copyright (C) 2006 - 2016 4 | Michael Chisholm (Chishm) 5 | Dave Murphy (WinterMute) 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any 9 | damages arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any 12 | purpose, including commercial applications, and to alter it and 13 | redistribute it freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you 16 | must not claim that you wrote the original software. If you use 17 | this software in a product, an acknowledgment in the product 18 | documentation would be appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and 20 | must not be misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source 22 | distribution. 23 | 24 | ---------------------------------------------------------------------------------*/ 25 | 26 | #include 27 | 28 | .align 4 29 | .arm 30 | .global gDldiStub 31 | @--------------------------------------------------------------------------------- 32 | 33 | .equ DLDI_ALLOCATED_SPACE, 16384 34 | 35 | gDldiStub: 36 | 37 | dldi_start: 38 | 39 | @--------------------------------------------------------------------------------- 40 | @ Driver patch file standard header -- 16 bytes 41 | .word 0xBF8DA5ED @ Magic number to identify this region 42 | .asciz " Chishm" @ Identifying Magic string (8 bytes with null terminator) 43 | .byte 0x01 @ Version number 44 | .byte DLDI_SIZE_16KB @16KiB @ Log [base-2] of the size of this driver in bytes. 45 | .byte 0x00 @ Sections to fix 46 | .byte DLDI_SIZE_16KB @16KiB @ Log [base-2] of the allocated space in bytes. 47 | 48 | @--------------------------------------------------------------------------------- 49 | @ Text identifier - can be anything up to 47 chars + terminating null -- 16 bytes 50 | .align 4 51 | .asciz "Default (No interface)" 52 | 53 | @--------------------------------------------------------------------------------- 54 | @ Offsets to important sections within the data -- 32 bytes 55 | .align 6 56 | .word 0x037F8000 //dldi_start @ data start 57 | .word 0x037FC000 //dldi_end @ data end 58 | .word 0x00000000 @ Interworking glue start -- Needs address fixing 59 | .word 0x00000000 @ Interworking glue end 60 | .word 0x00000000 @ GOT start -- Needs address fixing 61 | .word 0x00000000 @ GOT end 62 | .word 0x00000000 @ bss start -- Needs setting to zero 63 | .word 0x00000000 @ bss end 64 | 65 | @--------------------------------------------------------------------------------- 66 | @ DISC_INTERFACE data -- 32 bytes 67 | .ascii "DLDI" @ ioType 68 | .word 0x00000000 @ Features 69 | .word _DLDI_startup @ 70 | .word _DLDI_isInserted @ 71 | .word _DLDI_readSectors @ Function pointers to standard device driver functions 72 | .word _DLDI_writeSectors @ 73 | .word _DLDI_clearStatus @ 74 | .word _DLDI_shutdown @ 75 | 76 | @--------------------------------------------------------------------------------- 77 | 78 | _DLDI_startup: 79 | _DLDI_isInserted: 80 | _DLDI_readSectors: 81 | _DLDI_writeSectors: 82 | _DLDI_clearStatus: 83 | _DLDI_shutdown: 84 | mov r0, #0x00 @ Return false for every function 85 | bx lr 86 | 87 | 88 | 89 | @--------------------------------------------------------------------------------- 90 | .align 91 | .pool 92 | 93 | dldi_data_end: 94 | 95 | @ Pad to end of allocated space 96 | .space DLDI_ALLOCATED_SPACE - (dldi_data_end - dldi_start) 97 | 98 | dldi_end: 99 | .end 100 | @--------------------------------------------------------------------------------- 101 | -------------------------------------------------------------------------------- /arm9/source/gui/PlayerController.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "PlayerController.h" 3 | 4 | #define DIM_WAIT_SEC 5 5 | #define DIM_FADE_SEC 3 6 | 7 | PlayerController::PlayerController(fv_player_t* player) 8 | : _subScreenState(SUB_SCREEN_STATE_ACTIVE), _subScreenStateCounter(0), _subBacklightOff(false), _player(player), 9 | _playing(true), _lastTime(-1), _seekPenDown(false), _playPausePenDown(false), _seekLastFrame(-1), 10 | _inputRepeater(KEY_LEFT | KEY_RIGHT, 12, 3) 11 | { 12 | } 13 | 14 | void PlayerController::Initialize() 15 | { 16 | _view.Initialize(); 17 | 18 | _dimWaitFrames = DIM_WAIT_SEC * _player->fvHeader->fpsNum / _player->fvHeader->fpsDen; 19 | _dimFadeFrames = DIM_FADE_SEC * _player->fvHeader->fpsNum / _player->fvHeader->fpsDen; 20 | _invDimFadeFrames = 0x800000 / _dimFadeFrames; 21 | 22 | u32 totalSeconds = ((u64)_player->fvHeader->nrFrames * _player->fvHeader->fpsDen) / _player->fvHeader->fpsNum; 23 | _view.SetTotalTime(totalSeconds); 24 | _view.SetCurrentTime(0); 25 | } 26 | 27 | void PlayerController::TogglePlayPause() 28 | { 29 | if (_playing) 30 | { 31 | fv_pausePlayer(_player); 32 | _playing = false; 33 | } 34 | else 35 | { 36 | fv_resumePlayer(_player); 37 | _playing = true; 38 | } 39 | } 40 | 41 | void PlayerController::UpdateTouch() 42 | { 43 | if (_subScreenState != SUB_SCREEN_STATE_ACTIVE) 44 | return; 45 | 46 | touchPosition touch; 47 | touchRead(&touch); 48 | 49 | if (_inputProvider.Triggered(KEY_TOUCH)) 50 | { 51 | if (touch.px >= 16 && touch.px < 240 && touch.py >= /*117*/ 113 && touch.py < /*121*/ 125) 52 | { 53 | _seekPenDown = true; 54 | _seekLastFrame = -1; 55 | } 56 | else if (touch.px >= 110 && touch.px < 110 + 36 && touch.py >= 140 && touch.py < 140 + 36) 57 | { 58 | _playPausePenDown = true; 59 | } 60 | } 61 | 62 | if (_inputProvider.Released(KEY_TOUCH)) 63 | { 64 | _seekPenDown = false; 65 | 66 | if (_playPausePenDown) 67 | { 68 | TogglePlayPause(); 69 | _playPausePenDown = false; 70 | } 71 | } 72 | 73 | if (_inputProvider.Current(KEY_TOUCH) && _playPausePenDown) 74 | { 75 | if (!(touch.px >= 110 && touch.px < 110 + 36 && touch.py >= 140 && touch.py < 140 + 36)) 76 | { 77 | _playPausePenDown = false; 78 | } 79 | } 80 | 81 | if (_inputProvider.Current(KEY_TOUCH) && _seekPenDown) 82 | { 83 | int frame = ((int)touch.px - 16) * (int)_player->fvHeader->nrFrames / 224; 84 | if (frame < 0) 85 | frame = 0; 86 | else if ((u32)frame >= _player->fvHeader->nrFrames) 87 | frame = _player->fvHeader->nrFrames - 1; 88 | if (frame != _seekLastFrame) 89 | { 90 | _seekLastFrame = frame; 91 | fv_gotoNearestKeyFrame(_player, frame); 92 | fv_startPlayer(_player); 93 | _playing = true; 94 | } 95 | } 96 | } 97 | 98 | void PlayerController::UpdateKeys() 99 | { 100 | if (_inputProvider.Current(KEY_LID)) 101 | { 102 | // pause when lid is closed 103 | if (_playing) 104 | { 105 | fv_pausePlayer(_player); 106 | _playing = false; 107 | } 108 | } 109 | else if (_inputProvider.Triggered(KEY_A)) 110 | { 111 | TogglePlayPause(); 112 | } 113 | else if (_inputProvider.Triggered(KEY_LEFT)) 114 | { 115 | _seekKeyFrame = fv_gotoPreviousKeyFrame(_player); 116 | fv_startPlayer(_player); 117 | _playing = true; 118 | } 119 | else if (_inputRepeater.Triggered(KEY_LEFT)) 120 | { 121 | if (_seekKeyFrame > 0) 122 | _seekKeyFrame--; 123 | fv_gotoKeyFrame(_player, _seekKeyFrame); 124 | fv_startPlayer(_player); 125 | _playing = true; 126 | } 127 | else if (_inputProvider.Triggered(KEY_RIGHT)) 128 | { 129 | _seekKeyFrame = fv_gotoNextKeyFrame(_player); 130 | fv_startPlayer(_player); 131 | _playing = true; 132 | } 133 | else if (_inputRepeater.Triggered(KEY_RIGHT)) 134 | { 135 | fv_gotoKeyFrame(_player, ++_seekKeyFrame); 136 | fv_startPlayer(_player); 137 | _playing = true; 138 | } 139 | } 140 | 141 | void PlayerController::UpdateDim() 142 | { 143 | if (!_playing || (_inputProvider.GetCurrentKeys() & ~KEY_LID) || (_inputProvider.GetReleasedKeys() & KEY_LID)) 144 | { 145 | _subScreenState = SUB_SCREEN_STATE_ACTIVE; 146 | _subScreenStateCounter = 0; 147 | } 148 | 149 | if (_subBacklightOff && _subScreenState != SUB_SCREEN_STATE_OFF) 150 | { 151 | powerOn(PM_BACKLIGHT_BOTTOM); 152 | _subBacklightOff = false; 153 | } 154 | else if (!_subBacklightOff && _subScreenState == SUB_SCREEN_STATE_OFF) 155 | { 156 | powerOff(PM_BACKLIGHT_BOTTOM); 157 | _subBacklightOff = true; 158 | } 159 | 160 | switch (_subScreenState) 161 | { 162 | case SUB_SCREEN_STATE_ACTIVE: 163 | REG_MASTER_BRIGHT_SUB = 0; 164 | if (++_subScreenStateCounter >= _dimWaitFrames) 165 | { 166 | _subScreenStateCounter = 0; 167 | _subScreenState = SUB_SCREEN_STATE_DIMMING; 168 | } 169 | break; 170 | 171 | case SUB_SCREEN_STATE_DIMMING: 172 | { 173 | int dimFrame = (_subScreenStateCounter * 16 * _invDimFadeFrames + 0x400000) >> 23; 174 | if (dimFrame < 0) 175 | dimFrame = 0; 176 | else if (dimFrame > 16) 177 | dimFrame = 16; 178 | REG_MASTER_BRIGHT_SUB = dimFrame | (2 << 14); 179 | if (dimFrame < 16) 180 | _subScreenStateCounter++; 181 | else 182 | _subScreenState = SUB_SCREEN_STATE_OFF; 183 | break; 184 | } 185 | 186 | case SUB_SCREEN_STATE_OFF: 187 | break; 188 | } 189 | } 190 | 191 | void PlayerController::Update() 192 | { 193 | _view.SetPlaying(_playing); 194 | if (_playing) 195 | { 196 | REG_DIVCNT = DIV_64_32; 197 | REG_DIV_NUMER = (u64)_player->curFrame * (u64)_player->fvHeader->fpsDen; 198 | REG_DIV_DENOM_L = _player->fvHeader->fpsNum; 199 | 200 | fv_updatePlayer(_player); 201 | 202 | while (REG_DIVCNT & DIV_BUSY) 203 | ; 204 | u32 time = REG_DIV_RESULT_L; 205 | if (time != _lastTime) 206 | { 207 | _lastTime = time; 208 | _view.SetCurrentTime(time); 209 | _view.Update(); 210 | _view.VBlank(); 211 | } 212 | } 213 | else 214 | { 215 | _view.Update(); 216 | swiWaitForVBlank(); 217 | _view.VBlank(); 218 | _lastTime = -1; 219 | } 220 | 221 | _inputProvider.Sample(); // todo: sample more frequently 222 | _inputProvider.Update(); 223 | _inputRepeater.Update(&_inputProvider); 224 | UpdateTouch(); 225 | UpdateKeys(); 226 | UpdateDim(); 227 | } -------------------------------------------------------------------------------- /arm9/source/gui/PlayerController.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "core/PadInputProvider.h" 4 | #include "core/InputRepeater.h" 5 | #include "../FastVideo/fvPlayer.h" 6 | #include "PlayerView.h" 7 | 8 | class PlayerController 9 | { 10 | enum SubScreenState 11 | { 12 | SUB_SCREEN_STATE_ACTIVE, 13 | SUB_SCREEN_STATE_DIMMING, 14 | SUB_SCREEN_STATE_OFF 15 | }; 16 | 17 | SubScreenState _subScreenState; 18 | int _subScreenStateCounter; 19 | bool _subBacklightOff; 20 | 21 | int _dimWaitFrames; 22 | int _dimFadeFrames; 23 | u32 _invDimFadeFrames; 24 | 25 | fv_player_t* _player; 26 | bool _playing; 27 | u32 _seekKeyFrame; 28 | u32 _lastTime; 29 | 30 | bool _seekPenDown; 31 | bool _playPausePenDown; 32 | int _seekLastFrame; 33 | 34 | PadInputProvider _inputProvider; 35 | InputRepeater _inputRepeater; 36 | 37 | PlayerView _view; 38 | 39 | void TogglePlayPause(); 40 | 41 | void UpdateTouch(); 42 | void UpdateKeys(); 43 | void UpdateDim(); 44 | 45 | public: 46 | PlayerController(fv_player_t* player); 47 | 48 | void Initialize(); 49 | void Update(); 50 | }; 51 | -------------------------------------------------------------------------------- /arm9/source/gui/PlayerView.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "RobotoRegular10_ntft.h" 4 | #include "playBg.h" 5 | #include "iconPlay.h" 6 | #include "iconPause.h" 7 | #include "circle0.h" 8 | #include "circle1.h" 9 | #include "core/uiUtil.h" 10 | #include "PlayerView.h" 11 | 12 | PlayerView::PlayerView() : _robotoRegular10(RobotoRegular10_ntft) 13 | { 14 | } 15 | 16 | void PlayerView::Initialize() 17 | { 18 | decompress(playBgTiles, BG_GFX_SUB, LZ77Vram); 19 | decompress(playBgMap, (u8*)BG_GFX_SUB + 0x800, LZ77Vram); 20 | dmaCopyWords(3, playBgPal, BG_PALETTE_SUB, playBgPalLen); 21 | 22 | _subOam.Apply(OAM_SUB); 23 | 24 | REG_DISPCNT_SUB = DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE | DISPLAY_BG2_ACTIVE | DISPLAY_SPR_ACTIVE | 25 | DISPLAY_SPR_1D | DISPLAY_SPR_1D_SIZE_32 | MODE_0_2D | DISPLAY_WIN0_ON; 26 | 27 | REG_BG2CNT_SUB &= ~BG_PRIORITY_3; 28 | 29 | REG_BG0CNT_SUB = BG_32x32 | BG_PRIORITY_2 | BG_COLOR_16 | BG_MAP_BASE(1) | BG_TILE_BASE(0); 30 | REG_BG0HOFS_SUB = 0; 31 | REG_BG0VOFS_SUB = 0; 32 | 33 | REG_BG1CNT_SUB = BG_32x32 | BG_PRIORITY_1 | BG_COLOR_16 | BG_MAP_BASE(1) | BG_TILE_BASE(0); 34 | REG_BG1HOFS_SUB = 0; 35 | REG_BG1VOFS_SUB = -176; 36 | 37 | SUB_WIN0_X0 = 16; 38 | SUB_WIN0_X1 = 128; 39 | SUB_WIN0_Y0 = 117; 40 | SUB_WIN0_Y1 = 117 + 4; 41 | 42 | SUB_WIN_IN = 0x16; 43 | SUB_WIN_OUT = 0x15; 44 | 45 | for (int i = 0; i < 16; i++) 46 | { 47 | int rnew = 4 + ((31 - 4) * i) / 15; 48 | int gnew = 6 + ((31 - 6) * i) / 15; 49 | int bnew = 8 + ((31 - 8) * i) / 15; 50 | SPRITE_PALETTE_SUB[16 + i] = RGB5(rnew, gnew, bnew); 51 | } 52 | 53 | for (int i = 0; i < 16; i++) 54 | { 55 | int rnew = 6 + ((31 - 6) * i) / 15; 56 | int gnew = 8 + ((31 - 8) * i) / 15; 57 | int bnew = 11 + ((31 - 11) * i) / 15; 58 | SPRITE_PALETTE_SUB[32 + i] = RGB5(rnew, gnew, bnew); 59 | } 60 | 61 | for (int i = 0; i < 16; i++) 62 | { 63 | int rnew = 4 + ((6 - 4) * i) / 15; 64 | int gnew = 6 + ((8 - 6) * i) / 15; 65 | int bnew = 8 + ((11 - 8) * i) / 15; 66 | SPRITE_PALETTE_SUB[48 + i] = RGB5(rnew, gnew, bnew); 67 | } 68 | 69 | _twoDigitObjAddr = _subObj.Alloc(/*100*/ 60 * 2 * 32) >> 5; 70 | char twoDigitStr[3]; 71 | for (int i = 0; i < /*100*/ 60; i++) 72 | { 73 | memset(_textTmpBuf, 0, sizeof(_textTmpBuf)); 74 | twoDigitStr[0] = '0' + ((u32)i / 10); 75 | twoDigitStr[1] = '0' + ((u32)i % 10); 76 | twoDigitStr[2] = 0; 77 | int w, h; 78 | _robotoRegular10.MeasureString(twoDigitStr, w, h); 79 | const ntft_cinfo_char_t* charInfo = _robotoRegular10.GetCharInfo(twoDigitStr[0]); 80 | _twoDigitOffset[i] = charInfo->characterBeginOffset < 0 ? charInfo->characterBeginOffset : 0; 81 | charInfo = _robotoRegular10.GetCharInfo(twoDigitStr[1]); 82 | _twoDigitWidth[i] = w; 83 | _twoDigitEndOffset[i] = charInfo->characterEndOffset; 84 | _robotoRegular10.CreateStringData(twoDigitStr, _textTmpBuf, 16); 85 | uiutil_convertToObj(_textTmpBuf + 2 * 16, 16, 8, 16, &SPRITE_GFX_SUB[(_twoDigitObjAddr + 2 * i) << 4]); 86 | } 87 | 88 | _oneDigitObjAddr = _subObj.Alloc(10 * 32) >> 5; 89 | for (int i = 0; i < 10; i++) 90 | { 91 | memset(_textTmpBuf, 0, sizeof(_textTmpBuf)); 92 | twoDigitStr[0] = '0' + i; 93 | twoDigitStr[1] = 0; 94 | int w, h; 95 | _robotoRegular10.MeasureString(twoDigitStr, w, h); 96 | const ntft_cinfo_char_t* charInfo = _robotoRegular10.GetCharInfo(twoDigitStr[0]); 97 | _oneDigitOffset[i] = charInfo->characterBeginOffset < 0 ? charInfo->characterBeginOffset : 0; 98 | _oneDigitWidth[i] = w; 99 | _oneDigitEndOffset[i] = charInfo->characterEndOffset; 100 | _robotoRegular10.CreateStringData(twoDigitStr, _textTmpBuf, 8); 101 | uiutil_convertToObj(_textTmpBuf + 2 * 8, 8, 8, 8, &SPRITE_GFX_SUB[(_oneDigitObjAddr + i) << 4]); 102 | } 103 | 104 | _colonObjAddr = _subObj.Alloc(32) >> 5; 105 | memset(_textTmpBuf, 0, sizeof(_textTmpBuf)); 106 | twoDigitStr[0] = ':'; 107 | twoDigitStr[1] = 0; 108 | int w, h; 109 | _robotoRegular10.MeasureString(twoDigitStr, w, h); 110 | const ntft_cinfo_char_t* charInfo = _robotoRegular10.GetCharInfo(twoDigitStr[0]); 111 | _colonOffset = charInfo->characterBeginOffset < 0 ? charInfo->characterBeginOffset : 0; 112 | _colonWidth = w + charInfo->characterEndOffset; 113 | _robotoRegular10.CreateStringData(twoDigitStr, _textTmpBuf, 8); 114 | uiutil_convertToObj(_textTmpBuf + 2 * 8, 8, 8, 8, &SPRITE_GFX_SUB[_colonObjAddr << 4]); 115 | 116 | SetCurrentTime(0 * 60 * 60 + 16 * 60 + 27); 117 | SetTotalTime(3 * 60 * 60 + 48 * 60 + 59); 118 | 119 | _circleObjAddr = _subObj.Alloc(32 * 20) >> 5; 120 | dmaCopyWords(3, circle0Tiles, &SPRITE_GFX_SUB[_circleObjAddr << 4], circle0TilesLen); 121 | dmaCopyWords(3, circle1Tiles, &SPRITE_GFX_SUB[(_circleObjAddr + 16) << 4], circle1TilesLen); 122 | 123 | _playIconObjAddr = _subObj.Alloc(32 * 4) >> 5; 124 | dmaCopyWords(3, iconPlayTiles, &SPRITE_GFX_SUB[_playIconObjAddr << 4], iconPlayTilesLen); 125 | _pauseIconObjAddr = _subObj.Alloc(32 * 4) >> 5; 126 | dmaCopyWords(3, iconPauseTiles, &SPRITE_GFX_SUB[_pauseIconObjAddr << 4], iconPauseTilesLen); 127 | 128 | _playing = false; 129 | } 130 | 131 | int PlayerView::RenderColon(SpriteEntry* oam, int x, int y) 132 | { 133 | x += _colonOffset; 134 | 135 | oam[0].attribute[0] = ATTR0_NORMAL | ATTR0_TYPE_NORMAL | ATTR0_COLOR_16 | ATTR0_SQUARE | y; // OBJ_Y(y); 136 | oam[0].attribute[1] = ATTR1_SIZE_8 | x; // OBJ_X(x); 137 | oam[0].attribute[2] = ATTR2_PRIORITY(3) | ATTR2_PALETTE(1) | _colonObjAddr; 138 | 139 | return x + _colonWidth; 140 | } 141 | 142 | int PlayerView::RenderSingleDigit(SpriteEntry* oam, int digit, int x, int y) 143 | { 144 | x += _oneDigitOffset[digit]; 145 | 146 | oam[0].attribute[0] = ATTR0_NORMAL | ATTR0_TYPE_NORMAL | ATTR0_COLOR_16 | ATTR0_SQUARE | y; // OBJ_Y(y); 147 | oam[0].attribute[1] = ATTR1_SIZE_8 | x; // OBJ_X(x); 148 | oam[0].attribute[2] = ATTR2_PRIORITY(3) | ATTR2_PALETTE(1) | (_oneDigitObjAddr + digit); 149 | 150 | return x + _oneDigitWidth[digit] + _oneDigitEndOffset[digit]; 151 | } 152 | 153 | int PlayerView::RenderDoubleDigit(SpriteEntry* oam, int digits, int x, int y) 154 | { 155 | x += _twoDigitOffset[digits]; 156 | 157 | oam[0].attribute[0] = ATTR0_NORMAL | ATTR0_TYPE_NORMAL | ATTR0_COLOR_16 | ATTR0_WIDE | y; // OBJ_Y(y); 158 | oam[0].attribute[1] = ATTR1_SIZE_8 | x; // OBJ_X(x); 159 | oam[0].attribute[2] = ATTR2_PRIORITY(3) | ATTR2_PALETTE(1) | (_twoDigitObjAddr + digits * 2); 160 | 161 | return x + _twoDigitWidth[digits] + _twoDigitEndOffset[digits]; 162 | } 163 | 164 | void PlayerView::Update() 165 | { 166 | _subOam.Clear(); 167 | 168 | SpriteEntry* oams = _subOam.AllocOams(14); 169 | memcpy(&oams[0], _curTimeOams, sizeof(_curTimeOams)); 170 | memcpy(&oams[5], _totalTimeOams, sizeof(_totalTimeOams)); 171 | 172 | // circle 173 | oams[11].attribute[0] = ATTR0_NORMAL | ATTR0_TYPE_NORMAL | ATTR0_COLOR_16 | ATTR0_SQUARE | 140; 174 | oams[11].attribute[1] = ATTR1_SIZE_32 | 110; 175 | oams[11].attribute[2] = ATTR2_PRIORITY(3) | ATTR2_PALETTE(3) | _circleObjAddr; 176 | oams[12].attribute[0] = ATTR0_NORMAL | ATTR0_TYPE_NORMAL | ATTR0_COLOR_16 | ATTR0_TALL | 140; 177 | oams[12].attribute[1] = ATTR1_SIZE_16 | (110 + 32); 178 | oams[12].attribute[2] = ATTR2_PRIORITY(3) | ATTR2_PALETTE(3) | (_circleObjAddr + 16); 179 | oams[13].attribute[0] = ATTR0_NORMAL | ATTR0_TYPE_NORMAL | ATTR0_COLOR_16 | ATTR0_WIDE | (140 + 32 - 4); 180 | oams[13].attribute[1] = ATTR1_SIZE_16 | ATTR1_FLIP_Y | 110; 181 | oams[13].attribute[2] = ATTR2_PRIORITY(3) | ATTR2_PALETTE(3) | _circleObjAddr; 182 | 183 | // play/pause 184 | oams[10].attribute[0] = ATTR0_NORMAL | ATTR0_TYPE_NORMAL | ATTR0_COLOR_16 | ATTR0_SQUARE | (146 + 4); 185 | oams[10].attribute[1] = ATTR1_SIZE_16 | (116 + 4); 186 | oams[10].attribute[2] = ATTR2_PRIORITY(3) | ATTR2_PALETTE(2) | (_playing ? _pauseIconObjAddr : _playIconObjAddr); 187 | } 188 | 189 | void PlayerView::VBlank() 190 | { 191 | _subOam.Apply(OAM_SUB); 192 | 193 | SUB_WIN0_X0 = 16; 194 | u32 width; 195 | if (_curTime >= _totalTime) 196 | width = 224; 197 | else 198 | width = (_curTime * 224 * _invTotalTime + 0x400000) >> 23; 199 | SUB_WIN0_X1 = 16 + width; 200 | SUB_WIN0_Y0 = 117; 201 | SUB_WIN0_Y1 = 117 + 4; 202 | } 203 | 204 | void PlayerView::SetTotalTime(u32 totalTime) 205 | { 206 | _totalTime = totalTime; 207 | 208 | _invTotalTime = 0x800000 / _totalTime; 209 | 210 | u32 totalH = _totalTime / 3600; 211 | u32 totalM = _totalTime / 60 % 60; 212 | u32 totalS = _totalTime % 60; 213 | 214 | int x = 240; 215 | x -= _twoDigitWidth[totalS]; 216 | x -= _twoDigitOffset[totalS]; 217 | x -= _colonWidth; 218 | x -= _colonOffset; 219 | x -= _twoDigitEndOffset[totalM]; 220 | x -= _twoDigitWidth[totalM]; 221 | x -= _twoDigitOffset[totalM]; 222 | if (totalH != 0) 223 | { 224 | x -= _colonWidth; 225 | x -= _colonOffset; 226 | x -= _oneDigitEndOffset[totalH]; 227 | x -= _oneDigitWidth[totalH]; 228 | x -= _oneDigitOffset[totalH]; 229 | x = RenderSingleDigit(&_totalTimeOams[0], totalH, x, 101); 230 | x = RenderColon(&_totalTimeOams[1], x, 101); 231 | } 232 | else 233 | { 234 | _totalTimeOams[0].attribute[0] = 0x200; 235 | _totalTimeOams[1].attribute[0] = 0x200; 236 | } 237 | x = RenderDoubleDigit(&_totalTimeOams[2], totalM, x, 101); 238 | x = RenderColon(&_totalTimeOams[3], x, 101); 239 | x = RenderDoubleDigit(&_totalTimeOams[4], totalS, x, 101); 240 | } 241 | 242 | void PlayerView::SetCurrentTime(u32 currentTime) 243 | { 244 | _curTime = currentTime; 245 | 246 | int x = 16; 247 | if (_totalTime >= 3600) 248 | { 249 | x = RenderSingleDigit(&_curTimeOams[0], _curTime / 3600, x, 101); 250 | x = RenderColon(&_curTimeOams[1], x, 101); 251 | } 252 | else 253 | { 254 | _curTimeOams[0].attribute[0] = 0x200; 255 | _curTimeOams[1].attribute[0] = 0x200; 256 | } 257 | x = RenderDoubleDigit(&_curTimeOams[2], _curTime / 60 % 60, x, 101); 258 | x = RenderColon(&_curTimeOams[3], x, 101); 259 | x = RenderDoubleDigit(&_curTimeOams[4], _curTime % 60, x, 101); 260 | } -------------------------------------------------------------------------------- /arm9/source/gui/PlayerView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "core/VramManager.h" 4 | #include "core/OamManager.h" 5 | #include "core/NtftFont.h" 6 | 7 | class PlayerView 8 | { 9 | VramManager _subObj; 10 | OamManager _subOam; 11 | NtftFont _robotoRegular10; 12 | 13 | u8 _textTmpBuf[16 * 10]; 14 | 15 | u16 _oneDigitObjAddr; 16 | s8 _oneDigitOffset[10]; 17 | u8 _oneDigitWidth[10]; 18 | s8 _oneDigitEndOffset[10]; 19 | 20 | u16 _twoDigitObjAddr; 21 | s8 _twoDigitOffset[/*100*/60]; 22 | u8 _twoDigitWidth[/*100*/60]; 23 | s8 _twoDigitEndOffset[/*100*/60]; 24 | 25 | u16 _colonObjAddr; 26 | s8 _colonOffset; 27 | u8 _colonWidth; 28 | 29 | u32 _curTime; 30 | 31 | SpriteEntry _curTimeOams[5]; 32 | 33 | u32 _totalTime; 34 | u32 _invTotalTime; 35 | 36 | SpriteEntry _totalTimeOams[5]; 37 | 38 | u16 _circleObjAddr; 39 | u16 _playIconObjAddr; 40 | u16 _pauseIconObjAddr; 41 | 42 | bool _playing; 43 | 44 | int RenderColon(SpriteEntry* oam, int x, int y); 45 | int RenderSingleDigit(SpriteEntry* oam, int digit, int x, int y); 46 | int RenderDoubleDigit(SpriteEntry* oam, int digits, int x, int y); 47 | 48 | public: 49 | 50 | PlayerView(); 51 | 52 | void Initialize(); 53 | void Update(); 54 | void VBlank(); 55 | 56 | void SetTotalTime(u32 totalTime); 57 | void SetCurrentTime(u32 currentTime); 58 | 59 | void SetPlaying(bool playing) 60 | { 61 | _playing = playing; 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /arm9/source/gui/core/InputProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class InputProvider 4 | { 5 | u16 _currentKeys; 6 | u16 _triggeredKeys; 7 | u16 _releasedKeys; 8 | 9 | u16 _inputBuffer[4]; 10 | u8 _inputBufferRPtr; 11 | u8 _inputBufferWPtr; 12 | 13 | protected: 14 | InputProvider() : _currentKeys(0), _triggeredKeys(0), _releasedKeys(0), _inputBufferRPtr(0), _inputBufferWPtr(0) 15 | { 16 | } 17 | 18 | virtual u16 SampleIntern() = 0; 19 | 20 | public: 21 | virtual ~InputProvider() 22 | { 23 | } 24 | 25 | void Update() 26 | { 27 | u16 cur = _currentKeys; 28 | u16 trig = 0; 29 | u16 rel = 0; 30 | 31 | while (_inputBufferRPtr != _inputBufferWPtr) 32 | { 33 | u16 keyMask = _inputBuffer[_inputBufferRPtr]; 34 | trig |= (keyMask ^ cur) & keyMask; 35 | rel |= (keyMask ^ cur) & cur; 36 | cur = keyMask; 37 | _inputBufferRPtr = (_inputBufferRPtr + 1) & 3; 38 | } 39 | 40 | _triggeredKeys = trig; 41 | _releasedKeys = rel; 42 | _currentKeys = cur; 43 | } 44 | 45 | /** 46 | * \brief Returns a bitmask of the keys currently being held 47 | */ 48 | u16 GetCurrentKeys() const 49 | { 50 | return _currentKeys; 51 | } 52 | 53 | bool Current(u16 mask) const 54 | { 55 | return _currentKeys & mask; 56 | } 57 | 58 | /** 59 | * \brief Returns a bitmask of the keys that went from unpressed to pressed in the latest update 60 | */ 61 | u16 GetTriggeredKeys() const 62 | { 63 | return _triggeredKeys; 64 | } 65 | 66 | bool Triggered(u16 mask) const 67 | { 68 | return _triggeredKeys & mask; 69 | } 70 | 71 | /** 72 | * \brief Returns a bitmask of the keys that went from pressed to unpressed in the latest update 73 | */ 74 | u16 GetReleasedKeys() const 75 | { 76 | return _releasedKeys; 77 | } 78 | 79 | bool Released(u16 mask) const 80 | { 81 | return _releasedKeys & mask; 82 | } 83 | 84 | void Sample() 85 | { 86 | _inputBuffer[_inputBufferWPtr] = SampleIntern(); 87 | _inputBufferWPtr = (_inputBufferWPtr + 1) & 3; 88 | } 89 | 90 | void Reset() 91 | { 92 | _currentKeys = 0; 93 | _inputBufferRPtr = 0; 94 | _inputBufferWPtr = 0; 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /arm9/source/gui/core/InputRepeater.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "InputRepeater.h" 3 | 4 | void InputRepeater::Update(const InputProvider* inputProvider) 5 | { 6 | _trigKeys = inputProvider->GetTriggeredKeys(); 7 | u16 curKeys = inputProvider->GetCurrentKeys(); 8 | _repKeys = 0; 9 | if (_state != STATE_IDLE) 10 | { 11 | if (_state == STATE_FIRST) 12 | { 13 | if (curKeys & _mask) 14 | { 15 | _frameCounter++; 16 | if (_frameCounter >= _firstFrame) 17 | { 18 | _state = STATE_NEXT; 19 | _frameCounter = 0; 20 | _repKeys = curKeys & _mask; 21 | } 22 | } 23 | else 24 | _state = STATE_IDLE; 25 | } 26 | else if (_state == STATE_NEXT) 27 | { 28 | if (curKeys & _mask) 29 | { 30 | _frameCounter++; 31 | if (_frameCounter >= _nextFrame) 32 | { 33 | _frameCounter = 0; 34 | _repKeys = curKeys & _mask; 35 | } 36 | } 37 | else 38 | _state = STATE_IDLE; 39 | } 40 | } 41 | else if (curKeys & _mask) 42 | { 43 | _state = STATE_FIRST; 44 | _frameCounter = 0; 45 | _repKeys = curKeys & _mask; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /arm9/source/gui/core/InputRepeater.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "InputProvider.h" 4 | 5 | class InputRepeater 6 | { 7 | enum State 8 | { 9 | STATE_IDLE, 10 | STATE_FIRST, 11 | STATE_NEXT 12 | }; 13 | 14 | u16 _trigKeys; 15 | u16 _repKeys; 16 | State _state; 17 | u16 _frameCounter; 18 | u16 _mask; 19 | u16 _firstFrame; 20 | u16 _nextFrame; 21 | public: 22 | InputRepeater(u16 mask, u16 firstFrame, u16 nextFrame) 23 | : _trigKeys(0), _repKeys(0), _state(STATE_IDLE), _frameCounter(0), _mask(mask), _firstFrame(firstFrame), _nextFrame(nextFrame) 24 | { } 25 | 26 | void Update(const InputProvider* inputProvider); 27 | 28 | u16 GetTriggeredKeys() const { return _trigKeys | _repKeys; } 29 | 30 | bool Triggered(u16 mask) const 31 | { 32 | return (_trigKeys | _repKeys) & mask; 33 | } 34 | }; -------------------------------------------------------------------------------- /arm9/source/gui/core/NtftFont.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "NtftFont.h" 3 | 4 | NtftFont::NtftFont(const void* data) 5 | { 6 | const ntft_header_t* font = (const ntft_header_t*)data; 7 | _font.header = font; 8 | _font.characterInfo = (const ntft_cinfo_t*)((u32)font + font->charInfoOffset); 9 | _font.glyphData = (const ntft_gdata_t*)((u32)font + font->glyphDataOffset); 10 | } 11 | 12 | void NtftFont::MeasureString(const char* text, int& width, int& height) const 13 | { 14 | width = 0; 15 | height = _font.characterInfo->characterHeight; 16 | int tmpwidth = 0; 17 | unsigned char c = *text++; 18 | while (c != 0) 19 | { 20 | int end = 0; 21 | if (c == '\n') 22 | { 23 | if (width < tmpwidth) 24 | width = tmpwidth; 25 | tmpwidth = 0; 26 | height += _font.characterInfo->characterHeight + 1; 27 | } 28 | else 29 | { 30 | if ((tmpwidth + _font.characterInfo->characters[c].characterBeginOffset) >= 0) 31 | tmpwidth += _font.characterInfo->characters[c].characterBeginOffset; 32 | tmpwidth += _font.characterInfo->characters[c].characterWidth; 33 | end = _font.characterInfo->characters[c].characterEndOffset; 34 | } 35 | c = *text++; 36 | if (c != 0) 37 | tmpwidth += end; 38 | } 39 | if (width < tmpwidth) 40 | width = tmpwidth; 41 | } 42 | 43 | void NtftFont::CreateStringData(const char* text, u8* dst, int stride) const 44 | { 45 | // todo: maybe support alignment 46 | // int width; 47 | // int height; 48 | // GetStringSize(text, width, height); 49 | int xpos = 0; 50 | int ypos = 0; 51 | bool nodraw = false; 52 | unsigned char c = *text++; 53 | while (c != 0) 54 | { 55 | if (c == '\n') 56 | { 57 | xpos = 0; 58 | ypos += _font.characterInfo->characterHeight + 1; 59 | nodraw = false; 60 | } 61 | else 62 | { 63 | if ((xpos + _font.characterInfo->characters[c].characterBeginOffset) >= 0) 64 | xpos += _font.characterInfo->characters[c].characterBeginOffset; 65 | if (xpos + (int)_font.characterInfo->characters[c].characterWidth > stride) 66 | nodraw = true; 67 | if (!nodraw) 68 | { 69 | u8* glyph = (uint8_t*)&_font.glyphData->glyphData[_font.characterInfo->characters[c].glyphDataOffset]; 70 | u8* dst_ptr = dst + ypos * stride + xpos; 71 | int w = _font.characterInfo->characters[c].characterWidth; 72 | int h = _font.characterInfo->characterHeight; 73 | for (int y = 0; y < h; y++) 74 | { 75 | for (int x = 0; x < w; x++) 76 | { 77 | u8 data = *glyph++; 78 | int oldval = *dst_ptr; 79 | int newval = oldval + data; 80 | if (newval > 255) 81 | newval = 255; 82 | *dst_ptr++ = newval; 83 | } 84 | dst_ptr -= _font.characterInfo->characters[c].characterWidth; 85 | dst_ptr += stride; 86 | } 87 | xpos += _font.characterInfo->characters[c].characterWidth; 88 | xpos += _font.characterInfo->characters[c].characterEndOffset; 89 | } 90 | } 91 | c = *text++; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /arm9/source/gui/core/NtftFont.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NTFT_SIGNATURE_HEADER MKTAG('N', 'T', 'F', 'T') 4 | #define NTFT_SIGNATURE_CHARACTER_INFO MKTAG('C', 'I', 'N', 'F') 5 | #define NTFT_SIGNATURE_GLYPH_DATA MKTAG('G', 'L', 'P', 'D') 6 | 7 | struct ntft_header_t 8 | { 9 | u32 signature; 10 | u32 fileSize; 11 | u32 charInfoOffset; 12 | u32 glyphDataOffset; 13 | }; 14 | 15 | struct ntft_cinfo_char_t 16 | { 17 | u32 glyphDataOffset; 18 | s32 characterBeginOffset; 19 | u32 characterWidth; 20 | s32 characterEndOffset; 21 | }; 22 | 23 | struct ntft_cinfo_t 24 | { 25 | u32 signature; 26 | u32 blockSize; 27 | u32 characterHeight; 28 | ntft_cinfo_char_t characters[256]; 29 | }; 30 | 31 | struct ntft_gdata_t 32 | { 33 | uint32_t signature; 34 | uint32_t blockSize; 35 | uint32_t glyphDataSize; 36 | uint8_t glyphData[0]; 37 | }; 38 | 39 | struct ntft_t 40 | { 41 | const ntft_header_t* header; 42 | const ntft_cinfo_t* characterInfo; 43 | const ntft_gdata_t* glyphData; 44 | }; 45 | 46 | class NtftFont 47 | { 48 | ntft_t _font; 49 | 50 | public: 51 | explicit NtftFont(const void* data); 52 | 53 | void MeasureString(const char* text, int& width, int& height) const; 54 | void CreateStringData(const char* text, u8* dst, int stride) const; 55 | int GetFontHeight() const 56 | { 57 | return _font.characterInfo->characterHeight; 58 | } 59 | 60 | const ntft_cinfo_char_t* GetCharInfo(unsigned char c) const 61 | { 62 | return &_font.characterInfo->characters[c]; 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /arm9/source/gui/core/OamManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class OamManager 5 | { 6 | SpriteEntry* _oamPtr; 7 | int _mtxIdx; 8 | OAMTable _oamTable ALIGN(4); 9 | 10 | public: 11 | OamManager() 12 | { 13 | Clear(); 14 | } 15 | 16 | SpriteEntry* AllocOams(int count) 17 | { 18 | _oamPtr -= count; 19 | return _oamPtr; 20 | } 21 | 22 | SpriteRotation* AllocMatrices(int count, int& mtxId) 23 | { 24 | mtxId = _mtxIdx; 25 | SpriteRotation* result = &_oamTable.matrixBuffer[_mtxIdx]; 26 | _mtxIdx += count; 27 | return result; 28 | } 29 | 30 | void Apply(u16* dst) 31 | { 32 | DC_FlushRange(&_oamTable, sizeof(OAMTable)); 33 | dmaCopyWords(3, &_oamTable, dst, sizeof(OAMTable)); 34 | } 35 | 36 | void Clear() 37 | { 38 | memset(&_oamTable, 0x2, sizeof(OAMTable)); 39 | _oamPtr = &_oamTable.oamBuffer[128]; 40 | _mtxIdx = 0; 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /arm9/source/gui/core/PadInputProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "InputProvider.h" 3 | 4 | class PadInputProvider : public InputProvider 5 | { 6 | public: 7 | u16 SampleIntern() 8 | { 9 | return keysCurrent(); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /arm9/source/gui/core/VramManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class VramManager 4 | { 5 | u16 _offset; 6 | 7 | public: 8 | VramManager() 9 | : _offset(0) 10 | { 11 | 12 | } 13 | 14 | u16 Alloc(u16 length) 15 | { 16 | u16 result = _offset; 17 | _offset += length; 18 | return result; 19 | } 20 | 21 | u16 GetState() const { return _offset; } 22 | void SetState(u16 state) { _offset = state; } 23 | }; -------------------------------------------------------------------------------- /arm9/source/gui/core/uiUtil.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "uiUtil.h" 3 | 4 | void uiutil_convertToObj(u8* src, int width, int height, int stride, u16* dst) 5 | { 6 | for (int y = 0; y < height / 8; y++) 7 | { 8 | for (int x = 0; x < width / 8; x++) 9 | { 10 | for (int y2 = 0; y2 < 8; y2++) 11 | { 12 | //write in 32 bit units for vram compatibility 13 | *((uint32_t*)dst) = 14 | ((src[0] * 15 + 128) >> 8) | 15 | (((src[1] * 15 + 128) >> 8) << 4) | 16 | (((src[2] * 15 + 128) >> 8) << 8) | 17 | (((src[3] * 15 + 128) >> 8) << 12) | 18 | (((src[4] * 15 + 128) >> 8) << 16) | 19 | (((src[5] * 15 + 128) >> 8) << 20) | 20 | (((src[6] * 15 + 128) >> 8) << 24) | 21 | (((src[7] * 15 + 128) >> 8) << 28); 22 | dst += 2; 23 | src += stride; 24 | } 25 | src -= 8 * stride; 26 | src += 8; 27 | } 28 | src -= width; 29 | src += 8 * stride; 30 | } 31 | } -------------------------------------------------------------------------------- /arm9/source/gui/core/uiUtil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | union XBGR1555 4 | { 5 | uint16_t color; 6 | 7 | struct 8 | { 9 | uint16_t r : 5; 10 | uint16_t g : 5; 11 | uint16_t b : 5; 12 | uint16_t x : 1; 13 | }; 14 | }; 15 | 16 | void uiutil_convertToObj(u8* src, int width, int height, int stride, u16* dst); -------------------------------------------------------------------------------- /arm9/source/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../../common/ipc.h" 4 | #include "FastVideo/fvDecoder.h" 5 | #include "FastVideo/fvMcData.h" 6 | #include "FastVideo/fvPlayer.h" 7 | #include "mpu.h" 8 | #include "gui/PlayerController.h" 9 | #include "../../common/twlwram.h" 10 | 11 | static DTCM_BSS fv_player_t sPlayer; 12 | 13 | static PlayerController* sPlayerController; 14 | 15 | extern u8 gDldiStub[]; 16 | 17 | int main(int argc, char** argv) 18 | { 19 | DC_FlushAll(); 20 | 21 | mpu_enableVramCache(); 22 | 23 | bool canUseWram = false; 24 | if (isDSiMode() && twr_isUnlocked()) 25 | { 26 | twr_setBlockMapping(TWR_WRAM_BLOCK_A, 0x03000000, 0x40000, TWR_WRAM_BLOCK_IMAGE_SIZE_256K); 27 | twr_setBlockMapping(TWR_WRAM_BLOCK_B, 0x03100000, 0x40000, TWR_WRAM_BLOCK_IMAGE_SIZE_256K); 28 | twr_setBlockMapping(TWR_WRAM_BLOCK_C, 0x03140000, 0x40000, TWR_WRAM_BLOCK_IMAGE_SIZE_256K); 29 | mpu_enableTwlWramCache(); 30 | canUseWram = true; 31 | } 32 | 33 | fifoSetValue32Handler(FIFO_USER_01, NULL, NULL); 34 | 35 | // handshake 36 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_HANDSHAKE, 0)); 37 | fifoWaitValue32(FIFO_USER_01); 38 | u32 handShake = fifoGetValue32(FIFO_USER_01); 39 | 40 | if (canUseWram && (handShake & IPC_CMD_ARG_MASK) == 0) 41 | canUseWram = false; 42 | 43 | if (!isDSiMode()) 44 | { 45 | // setup dldi on arm7 if not on dsi 46 | DC_FlushRange(gDldiStub, 16 * 1024); 47 | fifoSendValue32(FIFO_USER_01, IPC_CMD_PACK(IPC_CMD_SETUP_DLDI, (u32)gDldiStub)); 48 | fifoWaitValue32(FIFO_USER_01); 49 | fifoGetValue32(FIFO_USER_01); 50 | } 51 | 52 | videoSetModeSub(MODE_0_2D); 53 | vramSetBankH(VRAM_H_SUB_BG); 54 | vramSetBankI(VRAM_I_SUB_SPRITE); 55 | 56 | consoleInit(NULL, 2, BgType_Text4bpp, BgSize_T_256x256, /*0, 1*/ 2, 1, false, true); 57 | 58 | // iprintf("FastVideoDS Player by Gericom\n\n"); 59 | 60 | vramSetBankA(VRAM_A_LCD); 61 | vramSetBankB(VRAM_B_LCD); 62 | vramSetBankC(VRAM_C_LCD); 63 | vramSetBankD(VRAM_D_LCD); 64 | vramSetBankE(VRAM_E_LCD); 65 | 66 | for (int i = 0; i < 3 * 128 * 1024; i += 4) 67 | *(vu32*)((u32)VRAM_A + i) = 0x80008000; 68 | 69 | const char* filePath; 70 | 71 | if (isDSiMode()) 72 | filePath = "sd:/testVideo.fv"; 73 | else 74 | filePath = "fat:/testVideo.fv"; 75 | 76 | if (argc >= 2) 77 | filePath = argv[1]; 78 | 79 | // iprintf("Playing %s\n", filePath); 80 | if (fv_initPlayer(&sPlayer, filePath, canUseWram)) 81 | { 82 | sPlayerController = new PlayerController(&sPlayer); 83 | sPlayerController->Initialize(); 84 | fv_startPlayer(&sPlayer); 85 | while (1) 86 | sPlayerController->Update(); 87 | } 88 | fv_destroyPlayer(&sPlayer); 89 | while (1) 90 | swiWaitForVBlank(); 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /arm9/source/mpu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | extern void mpu_enableVramCache(void); 8 | extern void mpu_enableTwlWramCache(void); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif -------------------------------------------------------------------------------- /arm9/source/mpu.s: -------------------------------------------------------------------------------- 1 | .section .itcm 2 | .arm 3 | 4 | #define PAGE_1M (0b10011 << 1) 5 | 6 | .global mpu_enableVramCache 7 | mpu_enableVramCache: 8 | //setup region 2 for vram 9 | ldr r0,= (PAGE_1M | 0x06800000 | 1) 10 | mcr p15, 0, r0, c6, c2, 0 11 | //write buffer 12 | mrc p15, 0, r0, c3, c0, 0 13 | orr r0, #(1 << 2) 14 | mcr p15, 0, r0, c3, c0, 0 15 | //dcache 16 | mrc p15, 0, r0, c2, c0, 0 17 | orr r0, #(1 << 2) 18 | mcr p15, 0, r0, c2, c0, 0 19 | 20 | bx lr 21 | 22 | .global mpu_enableTwlWramCache 23 | mpu_enableTwlWramCache: 24 | //write buffer 25 | mrc p15, 0, r0, c3, c0, 0 26 | orr r0, #(1 << 3) 27 | mcr p15, 0, r0, c3, c0, 0 28 | //dcache 29 | mrc p15, 0, r0, c2, c0, 0 30 | orr r0, #(1 << 3) 31 | mcr p15, 0, r0, c2, c0, 0 32 | 33 | bx lr -------------------------------------------------------------------------------- /common/fastVideo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define FV_AUDIO_RATE 47605 4 | #define FV_AUDIO_FRAME_SAMPLES 256 5 | #define FV_AUDIO_FRAME_SIZE (4 + FV_AUDIO_FRAME_SAMPLES / 2) 6 | 7 | #define FV_SIGNATURE 0x53445646 // FVDS 8 | 9 | typedef struct 10 | { 11 | u32 frame; 12 | u32 offset; 13 | } fv_keyframe_t; 14 | 15 | typedef struct 16 | { 17 | u32 signature; 18 | u16 width; 19 | u16 height; 20 | u32 fpsNum; 21 | u32 fpsDen; 22 | u16 audioRate; 23 | u16 audioChannels; 24 | u32 nrFrames; 25 | u32 nrKeyFrames; 26 | } fv_header_t; 27 | -------------------------------------------------------------------------------- /common/ipc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define IPC_CMD_READ_FRAME 1 4 | #define IPC_CMD_OPEN_FILE 2 5 | #define IPC_CMD_CONTROL_AUDIO 3 6 | #define IPC_CMD_READ_HEADER 4 7 | #define IPC_CMD_GOTO_KEYFRAME 5 8 | #define IPC_CMD_GOTO_NEAREST_KEYFRAME 6 9 | #define IPC_CMD_SETUP_DLDI 13 10 | #define IPC_CMD_HANDSHAKE 15 11 | 12 | #define IPC_CMD_ARG_MASK 0x0FFFFFFF 13 | #define IPC_CMD_CMD_SHIFT 28 14 | #define IPC_CMD_CMD_MASK 0xF0000000 15 | #define IPC_CMD_PACK(cmd, arg) ((((u32)(cmd) << IPC_CMD_CMD_SHIFT) & IPC_CMD_CMD_MASK) | ((u32)(arg)&IPC_CMD_ARG_MASK)) 16 | 17 | #define IPC_ARG_CONTROL_AUDIO_STOP 0 18 | #define IPC_ARG_CONTROL_AUDIO_START 1 19 | #define IPC_ARG_CONTROL_AUDIO_STOP_CLEAR 2 20 | -------------------------------------------------------------------------------- /common/twlwram.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef enum 4 | { 5 | TWR_WRAM_BLOCK_A = 0, 6 | TWR_WRAM_BLOCK_B = 1, 7 | TWR_WRAM_BLOCK_C = 2 8 | } TWRWramBlock; 9 | 10 | typedef enum 11 | { 12 | TWR_WRAM_BLOCK_IMAGE_SIZE_32K = 0, 13 | TWR_WRAM_BLOCK_IMAGE_SIZE_64K, 14 | TWR_WRAM_BLOCK_IMAGE_SIZE_128K, 15 | TWR_WRAM_BLOCK_IMAGE_SIZE_256K, 16 | } TWRWramBlockImageSize; 17 | 18 | #define TWR_WRAM_BASE 0x03000000 19 | 20 | //WRAM A 21 | #define TWR_WRAM_A_SLOT_SIZE 0x10000 22 | #define TWR_WRAM_A_SLOT_SHIFT 16 23 | #define TWR_WRAM_A_SLOT_COUNT 4 24 | 25 | #define TWR_WRAM_A_ADDRESS_MAX 0x03FF0000 26 | 27 | #define TWR_WRAM_A_SLOT_OFFSET(i) ((i) << 2) 28 | #define TWR_WRAM_A_SLOT_ENABLE 0x80 29 | 30 | typedef enum 31 | { 32 | TWR_WRAM_A_SLOT_MASTER_ARM9 = 0, 33 | TWR_WRAM_A_SLOT_MASTER_ARM7 = 1 34 | } TWRWramASlotMaster; 35 | 36 | #define TWR_MBK6_START_ADDR_MASK 0x00000FF0 37 | #define TWR_MBK6_START_ADDR_SHIFT 4 38 | 39 | #define TWR_MBK6_IMAGE_SIZE_SHIFT 12 40 | 41 | #define TWR_MBK6_END_ADDR_SHIFT 20 42 | 43 | //WRAM B 44 | #define TWR_WRAM_BC_SLOT_SIZE 0x8000 45 | #define TWR_WRAM_BC_SLOT_SHIFT 15 46 | #define TWR_WRAM_BC_SLOT_COUNT 8 47 | 48 | #define TWR_WRAM_BC_ADDRESS_MAX 0x03FF8000 49 | 50 | #define TWR_WRAM_BC_SLOT_OFFSET(i) ((i) << 2) 51 | #define TWR_WRAM_BC_SLOT_ENABLE 0x80 52 | 53 | typedef enum 54 | { 55 | TWR_WRAM_B_SLOT_MASTER_ARM9 = 0, 56 | TWR_WRAM_B_SLOT_MASTER_ARM7 = 1, 57 | TWR_WRAM_B_SLOT_MASTER_DSP_CODE = 2 58 | } TWRWramBSlotMaster; 59 | 60 | typedef enum 61 | { 62 | TWR_WRAM_C_SLOT_MASTER_ARM9 = 0, 63 | TWR_WRAM_C_SLOT_MASTER_ARM7 = 1, 64 | TWR_WRAM_C_SLOT_MASTER_DSP_DATA = 2 65 | } TWRWramCSlotMaster; 66 | 67 | #define TWR_MBK7_START_ADDR_MASK 0x00000FF8 68 | #define TWR_MBK7_START_ADDR_SHIFT 3 69 | 70 | #define TWR_MBK7_IMAGE_SIZE_SHIFT 12 71 | 72 | #define TWR_MBK7_END_ADDR_SHIFT 19 73 | 74 | #define TWR_MBK8_START_ADDR_MASK 0x00000FF8 75 | #define TWR_MBK8_START_ADDR_SHIFT 3 76 | 77 | #define TWR_MBK8_IMAGE_SIZE_SHIFT 12 78 | 79 | #define TWR_MBK8_END_ADDR_SHIFT 19 80 | 81 | #ifdef __cplusplus 82 | extern "C" { 83 | #endif 84 | 85 | u32 twr_getBlockAddress(TWRWramBlock block); 86 | void twr_setBlockMapping(TWRWramBlock block, u32 start, u32 length, TWRWramBlockImageSize imageSize); 87 | 88 | #ifdef ARM9 89 | 90 | void twr_mapWramASlot(int slot, TWRWramASlotMaster master, int offset, bool enable); 91 | void twr_mapWramBSlot(int slot, TWRWramBSlotMaster master, int offset, bool enable); 92 | void twr_mapWramCSlot(int slot, TWRWramCSlotMaster master, int offset, bool enable); 93 | 94 | #endif 95 | 96 | bool twr_isUnlocked(void); 97 | 98 | #ifdef ARM7 99 | 100 | static inline bool twr_isUnlockable(void) 101 | { 102 | return (REG_SCFG_EXT & 0x80000000) != 0; 103 | } 104 | 105 | static inline void twr_unlockAll(void) 106 | { 107 | REG_MBK9 = 0; 108 | } 109 | 110 | #endif 111 | 112 | #ifdef __cplusplus 113 | } 114 | #endif -------------------------------------------------------------------------------- /common/twlwram.twl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "twlwram.h" 3 | 4 | u32 twr_getBlockAddress(TWRWramBlock block) 5 | { 6 | switch(block) 7 | { 8 | case TWR_WRAM_BLOCK_A: 9 | return TWR_WRAM_BASE + (((REG_MBK6 & TWR_MBK6_START_ADDR_MASK) >> TWR_MBK6_START_ADDR_SHIFT) << TWR_WRAM_A_SLOT_SHIFT); 10 | case TWR_WRAM_BLOCK_B: 11 | return TWR_WRAM_BASE + (((REG_MBK7 & TWR_MBK7_START_ADDR_MASK) >> TWR_MBK7_START_ADDR_SHIFT) << TWR_WRAM_BC_SLOT_SHIFT); 12 | case TWR_WRAM_BLOCK_C: 13 | return TWR_WRAM_BASE + (((REG_MBK8 & TWR_MBK8_START_ADDR_MASK) >> TWR_MBK8_START_ADDR_SHIFT) << TWR_WRAM_BC_SLOT_SHIFT); 14 | } 15 | return 0; 16 | } 17 | 18 | void twr_setBlockMapping(TWRWramBlock block, u32 start, u32 length, TWRWramBlockImageSize imageSize) 19 | { 20 | start -= TWR_WRAM_BASE; 21 | u32 end; 22 | switch(block) 23 | { 24 | case TWR_WRAM_BLOCK_A: 25 | start >>= TWR_WRAM_A_SLOT_SHIFT; 26 | length >>= TWR_WRAM_A_SLOT_SHIFT; 27 | end = start + length; 28 | REG_MBK6 = (start << TWR_MBK6_START_ADDR_SHIFT) | (imageSize << TWR_MBK6_IMAGE_SIZE_SHIFT) | (end << TWR_MBK6_END_ADDR_SHIFT); 29 | break; 30 | case TWR_WRAM_BLOCK_B: 31 | start >>= TWR_WRAM_BC_SLOT_SHIFT; 32 | length >>= TWR_WRAM_BC_SLOT_SHIFT; 33 | end = start + length; 34 | REG_MBK7 = (start << TWR_MBK7_START_ADDR_SHIFT) | (imageSize << TWR_MBK7_IMAGE_SIZE_SHIFT) | (end << TWR_MBK7_END_ADDR_SHIFT); 35 | break; 36 | case TWR_WRAM_BLOCK_C: 37 | start >>= TWR_WRAM_BC_SLOT_SHIFT; 38 | length >>= TWR_WRAM_BC_SLOT_SHIFT; 39 | end = start + length; 40 | REG_MBK8 = (start << TWR_MBK8_START_ADDR_SHIFT) | (imageSize << TWR_MBK8_IMAGE_SIZE_SHIFT) | (end << TWR_MBK8_END_ADDR_SHIFT); 41 | break; 42 | } 43 | } 44 | 45 | #ifdef ARM9 46 | 47 | void twr_mapWramASlot(int slot, TWRWramASlotMaster master, int offset, bool enable) 48 | { 49 | if(slot < 0 || slot > 3 || offset < 0 || offset > 3) 50 | return; 51 | REG_MBK1[slot] = enable ? (TWR_WRAM_A_SLOT_ENABLE | master | TWR_WRAM_A_SLOT_OFFSET(offset)) : 0; 52 | } 53 | 54 | void twr_mapWramBSlot(int slot, TWRWramBSlotMaster master, int offset, bool enable) 55 | { 56 | if(slot < 0 || slot > 7 || offset < 0 || offset > 7) 57 | return; 58 | REG_MBK2[slot] = enable ? (TWR_WRAM_BC_SLOT_ENABLE | master | TWR_WRAM_BC_SLOT_OFFSET(offset)) : 0; 59 | } 60 | 61 | void twr_mapWramCSlot(int slot, TWRWramCSlotMaster master, int offset, bool enable) 62 | { 63 | if(slot < 0 || slot > 7 || offset < 0 || offset > 7) 64 | return; 65 | REG_MBK4[slot] = enable ? (TWR_WRAM_BC_SLOT_ENABLE | master | TWR_WRAM_BC_SLOT_OFFSET(offset)) : 0; 66 | } 67 | 68 | #endif 69 | 70 | bool twr_isUnlocked(void) 71 | { 72 | if ((REG_SCFG_EXT & 0x80000000) == 0) 73 | return false; // SCFG and MBK registers are permanently locked 74 | 75 | if ((REG_MBK9 & 0xFFFF0F) != 0) 76 | return false; // One or more MBK registers are locked 77 | 78 | return true; 79 | } 80 | -------------------------------------------------------------------------------- /icon.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gericom/FastVideoDSPlayer/1250dee262ecaf0618c6217d16a9d97684411015/icon.bmp -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | FastVideoDS Player 2 | =================== 3 | Player for the FastVideoDS format. Opens the video file supplied by argv, or otherwise `testVideo.fv` on the root of your sd card. Use [FastVideoDS Encoder](https://github.com/Gericom/FastVideoDSEncoder) to encode your videos. 4 | 5 | ## Features 6 | - Supports long videos 7 | - Smooth playback by adjusting the lcd refresh rate to an integer multiple of the frame rate 8 | - Supports up to 60 fps on dsi (~30 fps on ds) 9 | - Uses the 3d engine for motion compensation 10 | - Loads data from the sd card and decodes audio on the arm7 while the arm9 is fully available for decoding video 11 | - Argv support (for use with TWiLight Menu++ for example) 12 | - Video controls: play/pause and keyframe seeking 13 | - Disables the backlight of the bottom screen while playing to save energy 14 | 15 | ## Controls 16 | ### Buttons 17 | - A - Play/pause 18 | - Dpad left - Jump to previous keyframe (hold to keep going) 19 | - Dpad right - Jump to next keyframe (hold to keep going) 20 | 21 | ### Touch 22 | The touch screen can be used to play/pause the video and to seek by tapping/dragging the seek bar. 23 | 24 | ## Libraries Used 25 | - [FatFS](http://elm-chan.org/fsw/ff/00index_e.html) --------------------------------------------------------------------------------