From bea72e9514a5b3ced091d952762a5fa633e27740 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 9 Jul 2006 11:47:17 +0000 Subject: Patch #1519399: DS Backend svn-id: r23452 --- backends/platform/ds/arm9/buildkeyboard.bat | 8 + backends/platform/ds/arm9/lib/readme.txt | 2 + backends/platform/ds/arm9/makefile | 522 ++ backends/platform/ds/arm9/source/blitters.cpp | 272 + backends/platform/ds/arm9/source/blitters.h | 62 + backends/platform/ds/arm9/source/cdaudio.cpp | 968 +++ backends/platform/ds/arm9/source/cdaudio.h | 74 + backends/platform/ds/arm9/source/compressor/lz.cpp | 1078 ++++ backends/platform/ds/arm9/source/compressor/lz.h | 100 + backends/platform/ds/arm9/source/console2.h | 124 + backends/platform/ds/arm9/source/dsmain.cpp | 3888 ++++++++++++ backends/platform/ds/arm9/source/dsmain.h | 236 + backends/platform/ds/arm9/source/dsoptions.cpp | 412 ++ backends/platform/ds/arm9/source/dsoptions.h | 122 + backends/platform/ds/arm9/source/fat/disc_io.c | 844 +++ backends/platform/ds/arm9/source/fat/disc_io.h | 364 ++ backends/platform/ds/arm9/source/fat/gba_nds_fat.c | 6660 ++++++++++++++++++++ backends/platform/ds/arm9/source/fat/gba_nds_fat.h | 888 +++ backends/platform/ds/arm9/source/fat/io_efa2.c | 642 ++ backends/platform/ds/arm9/source/fat/io_efa2.h | 56 + backends/platform/ds/arm9/source/fat/io_fcsr.c | 658 ++ backends/platform/ds/arm9/source/fat/io_fcsr.h | 48 + backends/platform/ds/arm9/source/fat/io_m3cf.c | 734 +++ backends/platform/ds/arm9/source/fat/io_m3cf.h | 50 + backends/platform/ds/arm9/source/fat/io_m3sd.c | 750 +++ backends/platform/ds/arm9/source/fat/io_m3sd.h | 50 + backends/platform/ds/arm9/source/fat/io_m3sd_asm.s | 392 ++ backends/platform/ds/arm9/source/fat/io_mpcf.c | 714 +++ backends/platform/ds/arm9/source/fat/io_mpcf.h | 50 + backends/platform/ds/arm9/source/fat/io_nmmc.c | 716 +++ backends/platform/ds/arm9/source/fat/io_nmmc.h | 54 + backends/platform/ds/arm9/source/fat/io_sccf.c | 174 + backends/platform/ds/arm9/source/fat/io_sccf.h | 50 + backends/platform/ds/arm9/source/fat/io_scsd.c | 186 + backends/platform/ds/arm9/source/fat/io_scsd.h | 58 + backends/platform/ds/arm9/source/fat/io_scsd_asm.s | 1042 +++ backends/platform/ds/arm9/source/gbampsave.cpp | 372 ++ backends/platform/ds/arm9/source/gbampsave.h | 164 + backends/platform/ds/arm9/source/mad/readme.txt | 2 + backends/platform/ds/arm9/source/osystem_ds.cpp | 968 +++ backends/platform/ds/arm9/source/osystem_ds.h | 284 + backends/platform/ds/arm9/source/portdefs.cpp | 84 + backends/platform/ds/arm9/source/portdefs.h | 100 + backends/platform/ds/arm9/source/ramsave.cpp | 918 +++ backends/platform/ds/arm9/source/ramsave.h | 280 + backends/platform/ds/arm9/source/scummconsole.c | 1122 ++++ backends/platform/ds/arm9/source/scummconsole.h | 124 + backends/platform/ds/arm9/source/scummhelp.cpp | 172 + backends/platform/ds/arm9/source/scummhelp.h | 64 + backends/platform/ds/arm9/source/touchkeyboard.cpp | 626 ++ backends/platform/ds/arm9/source/touchkeyboard.h | 202 + backends/platform/ds/arm9/source/zipreader.cpp | 442 ++ backends/platform/ds/arm9/source/zipreader.h | 162 + 53 files changed, 29134 insertions(+) create mode 100644 backends/platform/ds/arm9/buildkeyboard.bat create mode 100644 backends/platform/ds/arm9/lib/readme.txt create mode 100644 backends/platform/ds/arm9/makefile create mode 100644 backends/platform/ds/arm9/source/blitters.cpp create mode 100644 backends/platform/ds/arm9/source/blitters.h create mode 100644 backends/platform/ds/arm9/source/cdaudio.cpp create mode 100644 backends/platform/ds/arm9/source/cdaudio.h create mode 100644 backends/platform/ds/arm9/source/compressor/lz.cpp create mode 100644 backends/platform/ds/arm9/source/compressor/lz.h create mode 100644 backends/platform/ds/arm9/source/console2.h create mode 100644 backends/platform/ds/arm9/source/dsmain.cpp create mode 100644 backends/platform/ds/arm9/source/dsmain.h create mode 100644 backends/platform/ds/arm9/source/dsoptions.cpp create mode 100644 backends/platform/ds/arm9/source/dsoptions.h create mode 100644 backends/platform/ds/arm9/source/fat/disc_io.c create mode 100644 backends/platform/ds/arm9/source/fat/disc_io.h create mode 100644 backends/platform/ds/arm9/source/fat/gba_nds_fat.c create mode 100644 backends/platform/ds/arm9/source/fat/gba_nds_fat.h create mode 100644 backends/platform/ds/arm9/source/fat/io_efa2.c create mode 100644 backends/platform/ds/arm9/source/fat/io_efa2.h create mode 100644 backends/platform/ds/arm9/source/fat/io_fcsr.c create mode 100644 backends/platform/ds/arm9/source/fat/io_fcsr.h create mode 100644 backends/platform/ds/arm9/source/fat/io_m3cf.c create mode 100644 backends/platform/ds/arm9/source/fat/io_m3cf.h create mode 100644 backends/platform/ds/arm9/source/fat/io_m3sd.c create mode 100644 backends/platform/ds/arm9/source/fat/io_m3sd.h create mode 100644 backends/platform/ds/arm9/source/fat/io_m3sd_asm.s create mode 100644 backends/platform/ds/arm9/source/fat/io_mpcf.c create mode 100644 backends/platform/ds/arm9/source/fat/io_mpcf.h create mode 100644 backends/platform/ds/arm9/source/fat/io_nmmc.c create mode 100644 backends/platform/ds/arm9/source/fat/io_nmmc.h create mode 100644 backends/platform/ds/arm9/source/fat/io_sccf.c create mode 100644 backends/platform/ds/arm9/source/fat/io_sccf.h create mode 100644 backends/platform/ds/arm9/source/fat/io_scsd.c create mode 100644 backends/platform/ds/arm9/source/fat/io_scsd.h create mode 100644 backends/platform/ds/arm9/source/fat/io_scsd_asm.s create mode 100644 backends/platform/ds/arm9/source/gbampsave.cpp create mode 100644 backends/platform/ds/arm9/source/gbampsave.h create mode 100644 backends/platform/ds/arm9/source/mad/readme.txt create mode 100644 backends/platform/ds/arm9/source/osystem_ds.cpp create mode 100644 backends/platform/ds/arm9/source/osystem_ds.h create mode 100644 backends/platform/ds/arm9/source/portdefs.cpp create mode 100644 backends/platform/ds/arm9/source/portdefs.h create mode 100644 backends/platform/ds/arm9/source/ramsave.cpp create mode 100644 backends/platform/ds/arm9/source/ramsave.h create mode 100644 backends/platform/ds/arm9/source/scummconsole.c create mode 100644 backends/platform/ds/arm9/source/scummconsole.h create mode 100644 backends/platform/ds/arm9/source/scummhelp.cpp create mode 100644 backends/platform/ds/arm9/source/scummhelp.h create mode 100644 backends/platform/ds/arm9/source/touchkeyboard.cpp create mode 100644 backends/platform/ds/arm9/source/touchkeyboard.h create mode 100644 backends/platform/ds/arm9/source/zipreader.cpp create mode 100644 backends/platform/ds/arm9/source/zipreader.h (limited to 'backends/platform/ds/arm9') diff --git a/backends/platform/ds/arm9/buildkeyboard.bat b/backends/platform/ds/arm9/buildkeyboard.bat new file mode 100644 index 0000000000..2078381ccb --- /dev/null +++ b/backends/platform/ds/arm9/buildkeyboard.bat @@ -0,0 +1,8 @@ +cd data +..\tools\gfx2gba -c16 -t8 -M -pkeyboard_pal.raw ..\keyboard.bmp +del keyboard.map +pause +cd data +..\tools\gfx2gba -c16 -t8 -M -pkeyboard_pal.raw ..\keyboard.bmp +del keyboard.map +pause \ No newline at end of file diff --git a/backends/platform/ds/arm9/lib/readme.txt b/backends/platform/ds/arm9/lib/readme.txt new file mode 100644 index 0000000000..f6e7f4c511 --- /dev/null +++ b/backends/platform/ds/arm9/lib/readme.txt @@ -0,0 +1,2 @@ +If building with MAD enabled, put libmad.a in here. +If building with MAD enabled, put libmad.a in here. \ No newline at end of file diff --git a/backends/platform/ds/arm9/makefile b/backends/platform/ds/arm9/makefile new file mode 100644 index 0000000000..c648d6d4dd --- /dev/null +++ b/backends/platform/ds/arm9/makefile @@ -0,0 +1,522 @@ +#BUILD_PLUGINS = 1 +libndsdir = $(DEVKITPRO)/libnds + +DS_SCUMM_BUILD = 1 + +#DS_NON_SCUMM_BUILD = 1 + +USE_MAD = 1 + +VPATH = $(srcdir) + +# Command to build libmad is: +# ./configure --host=arm-elf --enable-speed --enable-sso -enable-fpm=arm CFLAGS='-specs=ds_arm9.specs -mthumb-interwork' + +ifdef DS_NON_SCUMM_BUILD + DEFINES = -DDS_NON_SCUMM_BUILD + DISABLE_HE = 1 + DISABLE_SCUMM = 1 + DISABLE_SCUMM_7_8 = 1 + #DISABLE_SIMON = 1 + #DISABLE_SKY = 1 + DISABLE_SWORD1 = 1 + DISABLE_SWORD2 = 1 + #DISABLE_QUEEN = 1 + DISABLE_SAGA = 1 + DISABLE_KYRA = 1 + #DISABLE_GOB = 1 + DISABLE_LURE = 1 + DISABLE_CINE = 1 + DISABLE_AGI = 1 + BUILD=scummvm-B +endif + +ifdef DS_SCUMM_BUILD + DEFINES = -DDS_SCUMM_BUILD + DISABLE_HE = 1 + #DISABLE_SCUMM = 1 + DISABLE_SCUMM_7_8 = 1 + DISABLE_SIMON = 1 + DISABLE_SKY = 1 + DISABLE_SWORD1 = 1 + DISABLE_SWORD2 = 1 + DISABLE_QUEEN = 1 + DISABLE_SAGA = 1 + DISABLE_KYRA = 1 + DISABLE_GOB = 1 + DISABLE_LURE = 1 + DISABLE_CINE = 1 + DISABLE_AGI = 1 + BUILD=scummvm-A +endif + +ARM7BIN := -7 $(CURDIR)/../../arm7/arm7.bin +ICON := -b ../../../logo.bmp "ScummVM;By Neil Millstone;" + +CC = arm-eabi-gcc +CXX = arm-eabi-g++ + +CFLAGS = -Wno-multichar -Wall -Os\ + -Wno-multichar -mcpu=arm9tdmi -mtune=arm9tdmi \ + -mcpu=arm9tdmi -mtune=arm9tdmi -fomit-frame-pointer\ + -ffast-math -mthumb-interwork + +CXXFLAGS= $(CFLAGS) -Wno-non-virtual-dtor -Wno-non-virtual-dtor \ + -fno-exceptions -fno-rtti + +ASFLAGS = -mcpu=arm9tdmi -mthumb-interwork +DEFINES += -D__DS__ -DNDS -DARM9 -DNONSTANDARD_PORT -DDISABLE_FANCY_THEMES +ifdef USE_MAD + DEFINES += -DUSE_MAD +endif + +LDFLAGS = -specs=ds_arm9.specs -mthumb-interwork -mno-fpu -Wl,-Map,map.txt + +INCLUDES= -I./ -I$(portdir)/$(BUILD) -I$(srcdir) -I$(srcdir)/common -I$(portdir)/source -I$(portdir)/source/compressor -I$(portdir)/source/fat \ + -I$(srcdir)/backends/fs -I$(srcdir)/backends/fs/ds -I$(portdir)/data -I$(libndsdir)/include -I$(portdir)/../commoninclude\ + -I$(srcdir)/scumm -I$(libndsdir)/include -I$(libndsdir)/include/nds -I$(srcdir)/engines -I$(portdir)/source/mad + + +LIBS = -lm -L$(libndsdir)/lib -L$(portdir)/lib -lnds9 +ifdef USE_MAD + LIBS += -lmad +endif + +#-Lscumm -lscumm -Lbase -lbase -Lcommon -lcommon -Lgraphics -lgraphics -Lgui -lgui -Lsound -lsound +EXECUTABLE = scummvm.elf +PLUGIN_PREFIX = +PLUGIN_SUFFIX = .plg +PLUGIN_EXTRA_DEPS = plugin.x plugin.syms scummvm.elf +PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,-Tplugin.x,--just-symbols,scummvm.elf,--retain-symbols-file,plugin.syms -L$(ronindir)/lib +MKDIR = mkdir -p +RM = rm -f +RM_REC = rm -rf +AR = arm-eabi-ar cru +RANLIB = arm-eabi-ranlib +OBJCOPY = arm-eabi-objcopy +AS = arm-eabi-as +HAVE_GCC3 = true +DISABLE_SCALERS = true + +ifdef BUILD_PLUGINS +DEFINES += -DDYNAMIC_MODULES +PRE_OBJS_FLAGS = -Wl,--whole-archive +POST_OBJS_FLAGS = -Wl,--no-whole-archive +endif + +PORT_OBJS := $(portdir)/source/blitters.o $(portdir)/source/cdaudio.o $(portdir)/source/dsmain.o \ + $(portdir)/../../../fs/ds/ds-fs.o $(portdir)/source/gbampsave.o $(portdir)/source/scummhelp.o\ + $(portdir)/source/osystem_ds.o $(portdir)/source/portdefs.o $(portdir)/source/ramsave.o\ + $(portdir)/source/scummconsole.o $(portdir)/source/touchkeyboard.o $(portdir)/source/zipreader.o\ + $(portdir)/source/dsoptions.o + +DATA_OBJS := $(portdir)/data/icons.o $(portdir)/data/keyboard.o $(portdir)/data/keyboard_pal.o $(portdir)/data/default_font.o + + +COMPRESSOR_OBJS := $(portdir)/source/compressor/lz.o + +FAT_OBJS := $(portdir)/source/fat/disc_io.o $(portdir)/source/fat/gba_nds_fat.o\ + $(portdir)/source/fat/io_fcsr.o $(portdir)/source/fat/io_m3cf.o\ + $(portdir)/source/fat/io_mpcf.o $(portdir)/source/fat/io_sccf.o\ + $(portdir)/source/fat/io_m3sd.o\ + $(portdir)/source/fat/io_nmmc.o $(portdir)/source/fat/io_scsd.o \ + $(portdir)/source/fat/io_m3sd_asm.o $(portdir)/source/fat/io_scsd_asm.o + + + +OBJS := $(DATA_OBJS) $(PORT_OBJS) $(COMPRESSOR_OBJS) $(FAT_OBJS) + + + +MODULE_DIRS += . + +ndsall: + @[ -d $(BUILD) ] || mkdir -p $(BUILD) + make -C ./$(BUILD) -f ../makefile scummvm.nds + +include $(srcdir)/Makefile.common + +clean: + $(RM) $(OBJS) $(EXECUTABLE) + rm -fr $(BUILD) + +plugin_dist : + find . -name '*.plg' | while read p; do \ + sh-elf-strip -g -o "`basename \"$$p\" | tr '[:lower:]' '[:upper:]'`" "$$p"; \ + done + +dist : SCUMMVM.BIN plugins plugin_dist + + +#--------------------------------------------------------------------------------- +# canned command sequence for binary data +#--------------------------------------------------------------------------------- +#define bin2o +# bin2s $< | $(AS) -mthumb -mthumb-interwork -o $(@) +# echo "extern const u8" `(echo $( `(echo $(> `(echo $(> `(echo $( `(echo $(> `(echo $(> `(echo $( $(*).h +# echo "extern const u32" $(notdir $(*))_size[]";" >> $(*).h +# +# echo $(*).h +# rm $(*).tmp +#endef + +############## +# Replacement rule for the one in makefile.common +############## +ifndef HAVE_GCC3 +# If you use GCC, disable the above and enable this for intelligent +# dependency tracking. +.cpp.o: + $(MKDIR) $(*D)/$(DEPDIR) + $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d2" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o + $(ECHO) "$(*D)/" > $(*D)/$(DEPDIR)/$(*F).d + $(CAT) "$(*D)/$(DEPDIR)/$(*F).d2" >> "$(*D)/$(DEPDIR)/$(*F).d" + $(RM) "$(*D)/$(DEPDIR)/$(*F).d2" +else +# If you even have GCC 3.x, you can use this build rule, which is safer; the above +# rule can get you into a bad state if you Ctrl-C at the wrong moment. +# Also, with this GCC inserts additional dummy rules for the involved headers, +# which ensures a smooth compilation even if said headers become obsolete. +.cpp.o: + $(MKDIR) $(*D)/$(DEPDIR) +# $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o + $(CXX) -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o +endif + + + +#--------------------------------------------------------------------------------- +%.o : %.pcx +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o : %.raw +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o : %.pal +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o : %.map +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o : %.mdl +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.nds: %.bin + @echo ndstool -c $@ -9 scummvm.bin $(ARM7BIN) -b ../../logo.bmp "$(shell basename $@);ScummVM 0.9.0;Port v0.6" + ndstool -c $@ -9 scummvm.bin $(ARM7BIN) -b ../../logo.bmp "$(shell basename $@);ScummVM 0.9.0;Port v0.6" + dsbuild $@ + padbin 16 $(basename $@).ds.gba + +#--------------------------------------------------------------------------------- +%.bin: %.elf + $(OBJCOPY) -S scummvm.elf scummvm-stripped.elf + $(OBJCOPY) -O binary scummvm-stripped.elf scummvm.bin + +#%.o: %.s +# $(MKDIR) $(*D)/$(DEPDIR) +# $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o + +#BUILD_PLUGINS = 1 +libndsdir = $(DEVKITPRO)/libnds + +DS_SCUMM_BUILD = 1 + +#DS_NON_SCUMM_BUILD = 1 + +USE_MAD = 1 + +VPATH = $(srcdir) + +# Command to build libmad is: +# ./configure --host=arm-elf --enable-speed --enable-sso -enable-fpm=arm CFLAGS='-specs=ds_arm9.specs -mthumb-interwork' + +ifdef DS_NON_SCUMM_BUILD + DEFINES = -DDS_NON_SCUMM_BUILD + DISABLE_HE = 1 + DISABLE_SCUMM = 1 + DISABLE_SCUMM_7_8 = 1 + #DISABLE_SIMON = 1 + #DISABLE_SKY = 1 + DISABLE_SWORD1 = 1 + DISABLE_SWORD2 = 1 + #DISABLE_QUEEN = 1 + DISABLE_SAGA = 1 + DISABLE_KYRA = 1 + #DISABLE_GOB = 1 + DISABLE_LURE = 1 + DISABLE_CINE = 1 + DISABLE_AGI = 1 + BUILD=scummvm-B +endif + +ifdef DS_SCUMM_BUILD + DEFINES = -DDS_SCUMM_BUILD + DISABLE_HE = 1 + #DISABLE_SCUMM = 1 + DISABLE_SCUMM_7_8 = 1 + DISABLE_SIMON = 1 + DISABLE_SKY = 1 + DISABLE_SWORD1 = 1 + DISABLE_SWORD2 = 1 + DISABLE_QUEEN = 1 + DISABLE_SAGA = 1 + DISABLE_KYRA = 1 + DISABLE_GOB = 1 + DISABLE_LURE = 1 + DISABLE_CINE = 1 + DISABLE_AGI = 1 + BUILD=scummvm-A +endif + +ARM7BIN := -7 $(CURDIR)/../../arm7/arm7.bin +ICON := -b ../../../logo.bmp "ScummVM;By Neil Millstone;" + +CC = arm-eabi-gcc +CXX = arm-eabi-g++ + +CFLAGS = -Wno-multichar -Wall -Os\ + -Wno-multichar -mcpu=arm9tdmi -mtune=arm9tdmi \ + -mcpu=arm9tdmi -mtune=arm9tdmi -fomit-frame-pointer\ + -ffast-math -mthumb-interwork + +CXXFLAGS= $(CFLAGS) -Wno-non-virtual-dtor -Wno-non-virtual-dtor \ + -fno-exceptions -fno-rtti + +ASFLAGS = -mcpu=arm9tdmi -mthumb-interwork +DEFINES += -D__DS__ -DNDS -DARM9 -DNONSTANDARD_PORT -DDISABLE_FANCY_THEMES +ifdef USE_MAD + DEFINES += -DUSE_MAD +endif + +LDFLAGS = -specs=ds_arm9.specs -mthumb-interwork -mno-fpu -Wl,-Map,map.txt + +INCLUDES= -I./ -I$(portdir)/$(BUILD) -I$(srcdir) -I$(srcdir)/common -I$(portdir)/source -I$(portdir)/source/compressor -I$(portdir)/source/fat \ + -I$(srcdir)/backends/fs -I$(srcdir)/backends/fs/ds -I$(portdir)/data -I$(libndsdir)/include -I$(portdir)/../commoninclude\ + -I$(srcdir)/scumm -I$(libndsdir)/include -I$(libndsdir)/include/nds -I$(srcdir)/engines -I$(portdir)/source/mad + + +LIBS = -lm -L$(libndsdir)/lib -L$(portdir)/lib -lnds9 +ifdef USE_MAD + LIBS += -lmad +endif + +#-Lscumm -lscumm -Lbase -lbase -Lcommon -lcommon -Lgraphics -lgraphics -Lgui -lgui -Lsound -lsound +EXECUTABLE = scummvm.elf +PLUGIN_PREFIX = +PLUGIN_SUFFIX = .plg +PLUGIN_EXTRA_DEPS = plugin.x plugin.syms scummvm.elf +PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,-Tplugin.x,--just-symbols,scummvm.elf,--retain-symbols-file,plugin.syms -L$(ronindir)/lib +MKDIR = mkdir -p +RM = rm -f +RM_REC = rm -rf +AR = arm-eabi-ar cru +RANLIB = arm-eabi-ranlib +OBJCOPY = arm-eabi-objcopy +AS = arm-eabi-as +HAVE_GCC3 = true +DISABLE_SCALERS = true + +ifdef BUILD_PLUGINS +DEFINES += -DDYNAMIC_MODULES +PRE_OBJS_FLAGS = -Wl,--whole-archive +POST_OBJS_FLAGS = -Wl,--no-whole-archive +endif + +PORT_OBJS := $(portdir)/source/blitters.o $(portdir)/source/cdaudio.o $(portdir)/source/dsmain.o \ + $(portdir)/../../../fs/ds/ds-fs.o $(portdir)/source/gbampsave.o $(portdir)/source/scummhelp.o\ + $(portdir)/source/osystem_ds.o $(portdir)/source/portdefs.o $(portdir)/source/ramsave.o\ + $(portdir)/source/scummconsole.o $(portdir)/source/touchkeyboard.o $(portdir)/source/zipreader.o\ + $(portdir)/source/dsoptions.o + +DATA_OBJS := $(portdir)/data/icons.o $(portdir)/data/keyboard.o $(portdir)/data/keyboard_pal.o $(portdir)/data/default_font.o + + +COMPRESSOR_OBJS := $(portdir)/source/compressor/lz.o + +FAT_OBJS := $(portdir)/source/fat/disc_io.o $(portdir)/source/fat/gba_nds_fat.o\ + $(portdir)/source/fat/io_fcsr.o $(portdir)/source/fat/io_m3cf.o\ + $(portdir)/source/fat/io_mpcf.o $(portdir)/source/fat/io_sccf.o\ + $(portdir)/source/fat/io_m3sd.o\ + $(portdir)/source/fat/io_nmmc.o $(portdir)/source/fat/io_scsd.o \ + $(portdir)/source/fat/io_m3sd_asm.o $(portdir)/source/fat/io_scsd_asm.o + + + +OBJS := $(DATA_OBJS) $(PORT_OBJS) $(COMPRESSOR_OBJS) $(FAT_OBJS) + + + +MODULE_DIRS += . + +ndsall: + @[ -d $(BUILD) ] || mkdir -p $(BUILD) + make -C ./$(BUILD) -f ../makefile scummvm.nds + +include $(srcdir)/Makefile.common + +clean: + $(RM) $(OBJS) $(EXECUTABLE) + rm -fr $(BUILD) + +plugin_dist : + find . -name '*.plg' | while read p; do \ + sh-elf-strip -g -o "`basename \"$$p\" | tr '[:lower:]' '[:upper:]'`" "$$p"; \ + done + +dist : SCUMMVM.BIN plugins plugin_dist + + +#--------------------------------------------------------------------------------- +# canned command sequence for binary data +#--------------------------------------------------------------------------------- +#define bin2o +# bin2s $< | $(AS) -mthumb -mthumb-interwork -o $(@) +# echo "extern const u8" `(echo $( `(echo $(> `(echo $(> `(echo $( `(echo $(> `(echo $(> `(echo $( $(*).h +# echo "extern const u32" $(notdir $(*))_size[]";" >> $(*).h +# +# echo $(*).h +# rm $(*).tmp +#endef + +############## +# Replacement rule for the one in makefile.common +############## +ifndef HAVE_GCC3 +# If you use GCC, disable the above and enable this for intelligent +# dependency tracking. +.cpp.o: + $(MKDIR) $(*D)/$(DEPDIR) + $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d2" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o + $(ECHO) "$(*D)/" > $(*D)/$(DEPDIR)/$(*F).d + $(CAT) "$(*D)/$(DEPDIR)/$(*F).d2" >> "$(*D)/$(DEPDIR)/$(*F).d" + $(RM) "$(*D)/$(DEPDIR)/$(*F).d2" +else +# If you even have GCC 3.x, you can use this build rule, which is safer; the above +# rule can get you into a bad state if you Ctrl-C at the wrong moment. +# Also, with this GCC inserts additional dummy rules for the involved headers, +# which ensures a smooth compilation even if said headers become obsolete. +.cpp.o: + $(MKDIR) $(*D)/$(DEPDIR) +# $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o + $(CXX) -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o +endif + + + +#--------------------------------------------------------------------------------- +%.o : %.pcx +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o : %.raw +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o : %.pal +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o : %.map +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o : %.mdl +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.nds: %.bin + @echo ndstool -c $@ -9 scummvm.bin $(ARM7BIN) -b ../../logo.bmp "$(shell basename $@);ScummVM 0.9.0;Port v0.6" + ndstool -c $@ -9 scummvm.bin $(ARM7BIN) -b ../../logo.bmp "$(shell basename $@);ScummVM 0.9.0;Port v0.6" + dsbuild $@ + padbin 16 $(basename $@).ds.gba + +#--------------------------------------------------------------------------------- +%.bin: %.elf + $(OBJCOPY) -S scummvm.elf scummvm-stripped.elf + $(OBJCOPY) -O binary scummvm-stripped.elf scummvm.bin + +#%.o: %.s +# $(MKDIR) $(*D)/$(DEPDIR) +# $(CXX) -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o + \ No newline at end of file diff --git a/backends/platform/ds/arm9/source/blitters.cpp b/backends/platform/ds/arm9/source/blitters.cpp new file mode 100644 index 0000000000..5c9e07fd47 --- /dev/null +++ b/backends/platform/ds/arm9/source/blitters.cpp @@ -0,0 +1,272 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "stdafx.h" +#define CHARSET_MASK_TRANSPARENCY 253 + +namespace DS { + +void asmDrawStripToScreen(int height, int width, byte const* text, byte const* src, byte* dst, + int vsPitch, int vmScreenWidth, int textSurfacePitch) { + + + if (height <= 0) height = 1; + if (width < 4) return; + + + width &= ~4; +// src = (const byte *) (((int) (src)) & (~4)); +// dst = (byte *) (((int) (dst)) & (~4)); +// text = (const byte *) (((int) (text)) & (~4)); + + asm ( "mov r5, %0\n" // Height + "yLoop:\n" + "mov r3, #0\n" // X pos + + "xLoop:\n" + + "ldr r4, [%2, r3]\n" // Load text layer word + "cmp r4, %5\n" + "bne singleByteCompare\n" + "ldr r4, [%3, r3]\n" + "str r4, [%4, r3]\n" + "add r3, r3, #4\n" + "cmp r3, %1\n" // x == width? + "blt xLoop\n" + + "add %2, %2, %8\n" // src += vs->pitch + "add %3, %3, %6\n" // dst += _vm->_screenWidth + "add %4, %4, %7\n" // text += _textSurface.pitch + "sub r5, r5, #1\n" // y -= 1 + "cmp r5, #0\n" // y == 0? + "bne yLoop\n" + "b end\n" + + + "singleByteCompare:\n" + "ldrb r4, [%2, r3]\n" // Load text byte + "cmps r4, %5, lsr #24\n" // Compare with mask + "strneb r4, [%4, r3]\n" // Store if not equal + "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte + "streqb r4, [%4, r3]\n" // Store it + "add r3, r3, #1\n" + + "ldrb r4, [%2, r3]\n" // Load text byte + "cmps r4, %5, lsr #24\n" // Compare with mask + "strneb r4, [%4, r3]\n" // Store if not equal + "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte + "streqb r4, [%4, r3]\n" // Store it + "add r3, r3, #1\n" + + "ldrb r4, [%2, r3]\n" // Load text byte + "cmps r4, %5, lsr #24\n" // Compare with mask + "strneb r4, [%4, r3]\n" // Store if not equal + "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte + "streqb r4, [%4, r3]\n" // Store it + "add r3, r3, #1\n" + + "ldrb r4, [%2, r3]\n" // Load text byte + "cmps r4, %5, lsr #24\n" // Compare with mask + "strneb r4, [%4, r3]\n" // Store if not equal + "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte + "streqb r4, [%4, r3]\n" // Store it + "add r3, r3, #1\n" + + "cmps r3, %1\n" // x == width? + "blt xLoop\n" // Repeat + "add %2, %2, %8\n" // src += vs->pitch + "add %3, %3, %6\n" // dst += _vm->_screenWidth + "add %4, %4, %7\n" // text += _textSurface.pitch + "sub r5, r5, #1\n" // y -= 1 + "cmp r5, #0\n" // y == 0? + "bne yLoop\n" + + "end:\n" + : /* no output registers */ + : "r" (height), "r" (width), "r" (text), "r" (src), "r" (dst), "r" (CHARSET_MASK_TRANSPARENCY | (CHARSET_MASK_TRANSPARENCY << 8) | (CHARSET_MASK_TRANSPARENCY << 16) | (CHARSET_MASK_TRANSPARENCY << 24)), + "r" (vsPitch), "r" (vmScreenWidth), "r" (textSurfacePitch) + : "r5", "r3", "r4", "%2", "%3", "%4", "memory"); +} + + + +void asmCopy8Col(byte* dst, int dstPitch, const byte* src, int height) { + asm("ands r0, %3, #1\n" + "addne %3, %3, #1\n" + "bne roll2\n" + + "yLoop2:\n" + "ldr r0, [%2, #0]\n" + "str r0, [%0, #0]\n" + "ldr r0, [%2, #4]\n" + "str r0, [%0, #4]\n" + "add %0, %0, %1\n" + "add %2, %2, %1\n" + "roll2:\n" + "ldr r0, [%2, #0]\n" + "str r0, [%0, #0]\n" + "ldr r0, [%2, #4]\n" + "str r0, [%0, #4]\n" + "add %0, %0, %1\n" + "add %2, %2, %1\n" + "subs %3, %3, #2\n" + "bne yLoop2\n" + + : /* no output registers */ + : "r" (dst), "r" (dstPitch), "r" (src), "r" (height) + : "r0", "%0", "%2", "%3"); +} + +} +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "stdafx.h" +#define CHARSET_MASK_TRANSPARENCY 253 + +namespace DS { + +void asmDrawStripToScreen(int height, int width, byte const* text, byte const* src, byte* dst, + int vsPitch, int vmScreenWidth, int textSurfacePitch) { + + + if (height <= 0) height = 1; + if (width < 4) return; + + + width &= ~4; +// src = (const byte *) (((int) (src)) & (~4)); +// dst = (byte *) (((int) (dst)) & (~4)); +// text = (const byte *) (((int) (text)) & (~4)); + + asm ( "mov r5, %0\n" // Height + "yLoop:\n" + "mov r3, #0\n" // X pos + + "xLoop:\n" + + "ldr r4, [%2, r3]\n" // Load text layer word + "cmp r4, %5\n" + "bne singleByteCompare\n" + "ldr r4, [%3, r3]\n" + "str r4, [%4, r3]\n" + "add r3, r3, #4\n" + "cmp r3, %1\n" // x == width? + "blt xLoop\n" + + "add %2, %2, %8\n" // src += vs->pitch + "add %3, %3, %6\n" // dst += _vm->_screenWidth + "add %4, %4, %7\n" // text += _textSurface.pitch + "sub r5, r5, #1\n" // y -= 1 + "cmp r5, #0\n" // y == 0? + "bne yLoop\n" + "b end\n" + + + "singleByteCompare:\n" + "ldrb r4, [%2, r3]\n" // Load text byte + "cmps r4, %5, lsr #24\n" // Compare with mask + "strneb r4, [%4, r3]\n" // Store if not equal + "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte + "streqb r4, [%4, r3]\n" // Store it + "add r3, r3, #1\n" + + "ldrb r4, [%2, r3]\n" // Load text byte + "cmps r4, %5, lsr #24\n" // Compare with mask + "strneb r4, [%4, r3]\n" // Store if not equal + "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte + "streqb r4, [%4, r3]\n" // Store it + "add r3, r3, #1\n" + + "ldrb r4, [%2, r3]\n" // Load text byte + "cmps r4, %5, lsr #24\n" // Compare with mask + "strneb r4, [%4, r3]\n" // Store if not equal + "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte + "streqb r4, [%4, r3]\n" // Store it + "add r3, r3, #1\n" + + "ldrb r4, [%2, r3]\n" // Load text byte + "cmps r4, %5, lsr #24\n" // Compare with mask + "strneb r4, [%4, r3]\n" // Store if not equal + "ldreqb r4, [%3, r3]\n" // Otherwise Load src byte + "streqb r4, [%4, r3]\n" // Store it + "add r3, r3, #1\n" + + "cmps r3, %1\n" // x == width? + "blt xLoop\n" // Repeat + "add %2, %2, %8\n" // src += vs->pitch + "add %3, %3, %6\n" // dst += _vm->_screenWidth + "add %4, %4, %7\n" // text += _textSurface.pitch + "sub r5, r5, #1\n" // y -= 1 + "cmp r5, #0\n" // y == 0? + "bne yLoop\n" + + "end:\n" + : /* no output registers */ + : "r" (height), "r" (width), "r" (text), "r" (src), "r" (dst), "r" (CHARSET_MASK_TRANSPARENCY | (CHARSET_MASK_TRANSPARENCY << 8) | (CHARSET_MASK_TRANSPARENCY << 16) | (CHARSET_MASK_TRANSPARENCY << 24)), + "r" (vsPitch), "r" (vmScreenWidth), "r" (textSurfacePitch) + : "r5", "r3", "r4", "%2", "%3", "%4", "memory"); +} + + + +void asmCopy8Col(byte* dst, int dstPitch, const byte* src, int height) { + asm("ands r0, %3, #1\n" + "addne %3, %3, #1\n" + "bne roll2\n" + + "yLoop2:\n" + "ldr r0, [%2, #0]\n" + "str r0, [%0, #0]\n" + "ldr r0, [%2, #4]\n" + "str r0, [%0, #4]\n" + "add %0, %0, %1\n" + "add %2, %2, %1\n" + "roll2:\n" + "ldr r0, [%2, #0]\n" + "str r0, [%0, #0]\n" + "ldr r0, [%2, #4]\n" + "str r0, [%0, #4]\n" + "add %0, %0, %1\n" + "add %2, %2, %1\n" + "subs %3, %3, #2\n" + "bne yLoop2\n" + + : /* no output registers */ + : "r" (dst), "r" (dstPitch), "r" (src), "r" (height) + : "r0", "%0", "%2", "%3"); +} + +} diff --git a/backends/platform/ds/arm9/source/blitters.h b/backends/platform/ds/arm9/source/blitters.h new file mode 100644 index 0000000000..3b0ee5e4c5 --- /dev/null +++ b/backends/platform/ds/arm9/source/blitters.h @@ -0,0 +1,62 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + #ifndef _BLITTERS_H_ + #define _BLITTERS_H_ + +namespace DS { + +void asmDrawStripToScreen(int height, int width, byte const* text, byte const* src, byte* dst, + int vsPitch, int vmScreenWidth, int textSurfacePitch); +void asmCopy8Col(byte* dst, int dstPitch, const byte* src, int height); + +} + +#endif +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + #ifndef _BLITTERS_H_ + #define _BLITTERS_H_ + +namespace DS { + +void asmDrawStripToScreen(int height, int width, byte const* text, byte const* src, byte* dst, + int vsPitch, int vmScreenWidth, int textSurfacePitch); +void asmCopy8Col(byte* dst, int dstPitch, const byte* src, int height); + +} + +#endif diff --git a/backends/platform/ds/arm9/source/cdaudio.cpp b/backends/platform/ds/arm9/source/cdaudio.cpp new file mode 100644 index 0000000000..a6e4455b22 --- /dev/null +++ b/backends/platform/ds/arm9/source/cdaudio.cpp @@ -0,0 +1,968 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "stdafx.h" +#include "cdaudio.h" +#include "ds-fs.h" +#include "config-manager.h" +#include "dsmain.h" +#include "nds/scummvm_ipc.h" +#include "console2.h" + +#define WAV_FORMAT_IMA_ADPCM 0x14 +#define BUFFER_SIZE 8192 +#define BUFFER_CHUNK_SIZE (BUFFER_SIZE >> 2) + +namespace DS { +namespace CD { + +struct WaveHeader { + + char riff[4]; // 'RIFF' + u32 size; // Size of the file + char wave[4]; // 'WAVE' + + // fmt chunk + char fmt[4]; // 'fmt ' + u32 fmtSize; // Chunk size + u16 fmtFormatTag; // Format of this file + u16 fmtChannels; // Num channels + u32 fmtSamPerSec; // Samples per second + u32 fmtBytesPerSec; // Bytes per second + u16 fmtBlockAlign; // Block alignment + u16 fmtBitsPerSam; // Bits per sample + + u16 fmtExtraData; // Number of extra fmt bytes + u16 fmtExtra; // Samples per block (only for IMA-ADPCM files) +} __attribute__ ((packed)); + +struct chunkHeader { + char name[4]; + u32 size; +} __attribute__ ((packed)); + +struct Header { + s16 firstSample; + char stepTableIndex; + char reserved; +} __attribute__ ((packed)); + +struct decoderFormat { + s16 initial; + unsigned char tableIndex; + unsigned char test; + unsigned char sample[1024]; +} __attribute__ ((packed)); + +bool active = false; +WaveHeader waveHeader; +Header blockHeader; +FILE* file; +int fillPos; +bool isPlayingFlag = false; + +s16* audioBuffer; +u32 sampleNum; +s16* decompressionBuffer; +int numLoops; +int blockCount; +int dataChunkStart; +int blocksLeft; + + +// These are from Microsoft's document on DVI ADPCM +const int stepTab[ 89 ] = { +7, 8, 9, 10, 11, 12, 13, 14, +16, 17, 19, 21, 23, 25, 28, 31, +34, 37, 41, 45, 50, 55, 60, 66, +73, 80, 88, 97, 107, 118, 130, 143, +157, 173, 190, 209, 230, 253, 279, 307, +337, 371, 408, 449, 494, 544, 598, 658, +724, 796, 876, 963, 1060, 1166, 1282, 1411, +1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, +3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, +7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, +15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, +32767 }; + +const int indexTab[ 16 ] = { -1, -1, -1, -1, 2, 4, 6, 8, +-1, -1, -1, -1, 2, 4, 6, 8 }; + +void playNextBlock(); +void decompressBlock(); + + +void allocBuffers() { + +} + +void setActive(bool active) { + active = active; +} + +bool getActive() { + return active; +} + +void playTrack(int track, int numLoops, int startFrame, int duration) { + Common::String path = ConfMan.get("path"); + + if (isPlayingFlag) { + stopTrack(); + } + + + + + + char str[100]; + sprintf(str, "/track%d.wav", track); + path = path + str; + + + //1820160 + + file = DS::std_fopen(path.c_str(), "rb"); + + if (!file) { + consolePrintf("Failed to open %s!\n", path.c_str()); + return; + } + + + DS::std_fread((const void *) &waveHeader, sizeof(waveHeader), 1, file); + + consolePrintf("Playing track %d\n", track); + consolePrintf("Format: %d\n", waveHeader.fmtFormatTag); + consolePrintf("Rate : %d\n", waveHeader.fmtSamPerSec); + consolePrintf("Bits : %d\n", waveHeader.fmtBitsPerSam); + consolePrintf("BlkSz : %d\n", waveHeader.fmtExtra); + + if ((waveHeader.fmtFormatTag != 17) && (waveHeader.fmtFormatTag != 20)) { + consolePrintf("Wave file is in the wrong format! You must use IMA-ADPCM 4-bit mono.\n"); + return; + } + + for (int r = 0; r < 8; r++) { + IPC->adpcm.buffer[r] = (u8 * volatile) (decoderFormat *) malloc(waveHeader.fmtBlockAlign); + IPC->adpcm.filled[r] = false; + IPC->adpcm.arm7Dirty[r] = false; + } + + // Skip chunks until we reach the data chunk + chunkHeader chunk; + DS::std_fread((const void *) &chunk, sizeof(chunkHeader), 1, file); + + while (!((chunk.name[0] == 'd') && (chunk.name[1] == 'a') && (chunk.name[2] == 't') && (chunk.name[3] == 'a'))) { + DS::std_fseek(file, chunk.size, SEEK_CUR); + DS::std_fread((const void *) &chunk, sizeof(chunkHeader), 1, file); + } + + dataChunkStart = DS::std_ftell(file); + + + static bool started = false; + sampleNum = 0; + blockCount = 0; + + IPC->streamFillNeeded[0] = true; + IPC->streamFillNeeded[1] = true; + IPC->streamFillNeeded[2] = true; + IPC->streamFillNeeded[3] = true; + if (!started) { + fillPos = 0; + audioBuffer = (s16 *) malloc(BUFFER_SIZE * 2); + decompressionBuffer = (s16 *) malloc(waveHeader.fmtExtra * 2); + started = true; +// consolePrintf("****Starting buffer*****\n"); + memset(audioBuffer, 0, BUFFER_SIZE * 2); + memset(decompressionBuffer, 0, waveHeader.fmtExtra * 2); + DS::playSound(audioBuffer, BUFFER_SIZE * 2, false, false, waveHeader.fmtSamPerSec); + + } + fillPos = (IPC->streamPlayingSection + 1) & 3; + isPlayingFlag = true; + + + // Startframe is a 75Hz timer. Dunno why, since nothing else + // seems to run at that rate. + int tenths = (startFrame * 10) / 75; + + // Seek to the nearest block start to the start time + int samples = (tenths * waveHeader.fmtSamPerSec) / 10; + int block = samples / waveHeader.fmtExtra; + + + if (duration == 0) { + blocksLeft = 0; + } else { + blocksLeft = ((((duration * 100) / 75) * (waveHeader.fmtSamPerSec)) / (waveHeader.fmtExtra) / 100) + 10; + } +// consolePrintf("Playing %d blocks (%d)\n\n", blocksLeft, duration); + + // No need to seek if we're starting from the beginning + if (block != 0) { + DS::std_fseek(file, dataChunkStart + block * waveHeader.fmtBlockAlign, SEEK_SET); +// consolePrintf("Startframe: %d msec: %d (%d,%d)\n", startFrame, tenthssec, samples, block); + } + + + //decompressBlock(); + playNextBlock(); + numLoops = numLoops; +} + +void update() { + playNextBlock(); +} + +void decompressBlock() { + int block[2048]; + bool loop = false; + + blockCount++; + + if (blockCount < 10) return; + + + do { + DS::std_fread((const void *) &blockHeader, sizeof(blockHeader), 1, file); + + DS::std_fread(&block[0], waveHeader.fmtBlockAlign - sizeof(blockHeader), 1, file); + + if (DS::std_feof(file) ) { + // Reached end of file, so loop + + + if ((numLoops == -1) || (numLoops > 1)) { + // Seek file to first packet + if (numLoops != -1) { + numLoops--; + } + DS::std_fseek(file, dataChunkStart, SEEK_SET); + loop = true; + } else { + // Fill decompression buffer with zeros to prevent glitching + for (int r = 0; r < waveHeader.fmtExtra; r++) { + decompressionBuffer[r] = 0; + } +// consolePrintf("Stopping music\n"); + stopTrack(); + return; + } + + } else { + loop = false; + } + + } while (loop); + + + if (blocksLeft > 0) { + blocksLeft--; + // consolePrintf("%d ", blocksLeft); + if (blocksLeft == 0) { + stopTrack(); + return; + } + } + + // First sample is in header + decompressionBuffer[0] = blockHeader.firstSample; + + // Set up initial table indeces + int stepTableIndex = blockHeader.stepTableIndex; + int prevSample = blockHeader.firstSample; + +// consolePrintf("Decompressing block step=%d fs=%d\n", stepTableIndex, prevSample); + + for (int r = 0; r < waveHeader.fmtExtra - 1; r++) { + + int word = block[r >> 3]; + int offset = 0; + + switch (7 - (r & 0x0007)) { + case 0: { + offset = (word & 0xF0000000) >> 28; + break; + } + + case 1: { + offset = (word & 0x0F000000) >> 24; + break; + } + + case 2: { + offset = (word & 0x00F00000) >> 20; + break; + } + + case 3: { + offset = (word & 0x000F0000) >> 16; + break; + } + + case 4: { + offset = (word & 0x0000F000) >> 12; + break; + } + + case 5: { + offset = (word & 0x00000F00) >> 8; + break; + } + + case 6: { + offset = (word & 0x000000F0) >> 4; + break; + } + + case 7: { + offset = (word & 0x0000000F); + break; + } + } + + int diff = 0; + + if (offset & 4) { + diff = diff + stepTab[stepTableIndex]; + } + + if (offset & 2) { + diff = diff + (stepTab[stepTableIndex] >> 1); + } + + if (offset & 1) { + diff = diff + (stepTab[stepTableIndex] >> 2); + } + + diff = diff + (stepTab[stepTableIndex] >> 3); + + if (offset & 8) { + diff = -diff; + } + + int newSample = prevSample + diff; + + if (newSample > 32767) newSample = 32767; + if (newSample < -32768) newSample = -32768; + + decompressionBuffer[r + 1] = newSample; + + prevSample = newSample; + + stepTableIndex += indexTab[offset]; + + if (stepTableIndex > 88) stepTableIndex = 88; + if (stepTableIndex < 0) stepTableIndex = 0; + + + } +} + +void playNextBlock() { + if (!isPlayingFlag) return; + int lastBlockId = -1; + + while (IPC->adpcm.semaphore); // Wait for buffer to become free if needed + IPC->adpcm.semaphore = true; // Lock the buffer structure to prevent clashing with the ARM7 +// DC_FlushAll(); + + //-8644, 25088 + for (int block = fillPos + 1; block < fillPos + 4; block++) { + + int blockId = block & 3; + + if (IPC->streamFillNeeded[blockId]) { + + IPC->streamFillNeeded[blockId] = false; +// DC_FlushAll(); + +/* if (!(REG_KEYINPUT & KEY_R)) { + //consolePrintf("Align: %d First: %d Step:%d Res:%d\n", waveHeader.fmtBlockAlign, blockHeader.firstSample, blockHeader.stepTableIndex, blockHeader.reserved); + consolePrintf("Filling buffer %d\n", blockId); + }*/ + for (int r = blockId * BUFFER_CHUNK_SIZE; r < (blockId + 1) * BUFFER_CHUNK_SIZE; r++) { + if (isPlayingFlag) { + audioBuffer[r] = decompressionBuffer[sampleNum++]; + if (sampleNum >= waveHeader.fmtExtra) { + decompressBlock(); + sampleNum = 0; + } + } + } + + lastBlockId = blockId; + IPC->streamFillNeeded[blockId] = false; +// DC_FlushAll(); + + } + + + + } + + + + if (lastBlockId != -1) { + fillPos = lastBlockId; +/* if (!(REG_KEYINPUT & KEY_R)) { + consolePrintf("Frame fill done\n"); + }*/ + } + IPC->adpcm.semaphore = false; // Release the buffer structure +// DC_FlushAll(); +} + +void stopTrack() { + if (!isPlayingFlag) return; + + DS::std_fclose(file); + + isPlayingFlag = false; + + for (int r = 0; r < BUFFER_SIZE; r++) { + audioBuffer[r] = 0; + } + + for (int r= 0; r < waveHeader.fmtExtra; r++) { + decompressionBuffer[r] = 0; + } +// DS::stopSound(1); + +// free(audioBuffer); +// free(decompressionBuffer); + + DC_FlushAll(); +} + +bool checkCD() { + // Need to check whethe CD audio files are present - do this by trying to open Track1.wav. + consolePrintf("Attempted to open cd drive\n"); + + Common::String path = ConfMan.get("path"); + path = path + "/track2.wav"; + // 6577 153 154 + consolePrintf("Looking for %s...", path.c_str()); + + FILE* file; + if ((file = DS::std_fopen(path.c_str(), "r"))) { + consolePrintf("Success!\n"); + setActive(true); + DS::std_fclose(file); + return true; + } else { + setActive(false); + consolePrintf("Failed!\n"); + return false; + } +} + +bool isPlaying() { + return isPlayingFlag; +} + +} +} + +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "stdafx.h" +#include "cdaudio.h" +#include "ds-fs.h" +#include "config-manager.h" +#include "dsmain.h" +#include "nds/scummvm_ipc.h" +#include "console2.h" + +#define WAV_FORMAT_IMA_ADPCM 0x14 +#define BUFFER_SIZE 8192 +#define BUFFER_CHUNK_SIZE (BUFFER_SIZE >> 2) + +namespace DS { +namespace CD { + +struct WaveHeader { + + char riff[4]; // 'RIFF' + u32 size; // Size of the file + char wave[4]; // 'WAVE' + + // fmt chunk + char fmt[4]; // 'fmt ' + u32 fmtSize; // Chunk size + u16 fmtFormatTag; // Format of this file + u16 fmtChannels; // Num channels + u32 fmtSamPerSec; // Samples per second + u32 fmtBytesPerSec; // Bytes per second + u16 fmtBlockAlign; // Block alignment + u16 fmtBitsPerSam; // Bits per sample + + u16 fmtExtraData; // Number of extra fmt bytes + u16 fmtExtra; // Samples per block (only for IMA-ADPCM files) +} __attribute__ ((packed)); + +struct chunkHeader { + char name[4]; + u32 size; +} __attribute__ ((packed)); + +struct Header { + s16 firstSample; + char stepTableIndex; + char reserved; +} __attribute__ ((packed)); + +struct decoderFormat { + s16 initial; + unsigned char tableIndex; + unsigned char test; + unsigned char sample[1024]; +} __attribute__ ((packed)); + +bool active = false; +WaveHeader waveHeader; +Header blockHeader; +FILE* file; +int fillPos; +bool isPlayingFlag = false; + +s16* audioBuffer; +u32 sampleNum; +s16* decompressionBuffer; +int numLoops; +int blockCount; +int dataChunkStart; +int blocksLeft; + + +// These are from Microsoft's document on DVI ADPCM +const int stepTab[ 89 ] = { +7, 8, 9, 10, 11, 12, 13, 14, +16, 17, 19, 21, 23, 25, 28, 31, +34, 37, 41, 45, 50, 55, 60, 66, +73, 80, 88, 97, 107, 118, 130, 143, +157, 173, 190, 209, 230, 253, 279, 307, +337, 371, 408, 449, 494, 544, 598, 658, +724, 796, 876, 963, 1060, 1166, 1282, 1411, +1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, +3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, +7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, +15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, +32767 }; + +const int indexTab[ 16 ] = { -1, -1, -1, -1, 2, 4, 6, 8, +-1, -1, -1, -1, 2, 4, 6, 8 }; + +void playNextBlock(); +void decompressBlock(); + + +void allocBuffers() { + +} + +void setActive(bool active) { + active = active; +} + +bool getActive() { + return active; +} + +void playTrack(int track, int numLoops, int startFrame, int duration) { + Common::String path = ConfMan.get("path"); + + if (isPlayingFlag) { + stopTrack(); + } + + + + + + char str[100]; + sprintf(str, "/track%d.wav", track); + path = path + str; + + + //1820160 + + file = DS::std_fopen(path.c_str(), "rb"); + + if (!file) { + consolePrintf("Failed to open %s!\n", path.c_str()); + return; + } + + + DS::std_fread((const void *) &waveHeader, sizeof(waveHeader), 1, file); + + consolePrintf("Playing track %d\n", track); + consolePrintf("Format: %d\n", waveHeader.fmtFormatTag); + consolePrintf("Rate : %d\n", waveHeader.fmtSamPerSec); + consolePrintf("Bits : %d\n", waveHeader.fmtBitsPerSam); + consolePrintf("BlkSz : %d\n", waveHeader.fmtExtra); + + if ((waveHeader.fmtFormatTag != 17) && (waveHeader.fmtFormatTag != 20)) { + consolePrintf("Wave file is in the wrong format! You must use IMA-ADPCM 4-bit mono.\n"); + return; + } + + for (int r = 0; r < 8; r++) { + IPC->adpcm.buffer[r] = (u8 * volatile) (decoderFormat *) malloc(waveHeader.fmtBlockAlign); + IPC->adpcm.filled[r] = false; + IPC->adpcm.arm7Dirty[r] = false; + } + + // Skip chunks until we reach the data chunk + chunkHeader chunk; + DS::std_fread((const void *) &chunk, sizeof(chunkHeader), 1, file); + + while (!((chunk.name[0] == 'd') && (chunk.name[1] == 'a') && (chunk.name[2] == 't') && (chunk.name[3] == 'a'))) { + DS::std_fseek(file, chunk.size, SEEK_CUR); + DS::std_fread((const void *) &chunk, sizeof(chunkHeader), 1, file); + } + + dataChunkStart = DS::std_ftell(file); + + + static bool started = false; + sampleNum = 0; + blockCount = 0; + + IPC->streamFillNeeded[0] = true; + IPC->streamFillNeeded[1] = true; + IPC->streamFillNeeded[2] = true; + IPC->streamFillNeeded[3] = true; + if (!started) { + fillPos = 0; + audioBuffer = (s16 *) malloc(BUFFER_SIZE * 2); + decompressionBuffer = (s16 *) malloc(waveHeader.fmtExtra * 2); + started = true; +// consolePrintf("****Starting buffer*****\n"); + memset(audioBuffer, 0, BUFFER_SIZE * 2); + memset(decompressionBuffer, 0, waveHeader.fmtExtra * 2); + DS::playSound(audioBuffer, BUFFER_SIZE * 2, false, false, waveHeader.fmtSamPerSec); + + } + fillPos = (IPC->streamPlayingSection + 1) & 3; + isPlayingFlag = true; + + + // Startframe is a 75Hz timer. Dunno why, since nothing else + // seems to run at that rate. + int tenths = (startFrame * 10) / 75; + + // Seek to the nearest block start to the start time + int samples = (tenths * waveHeader.fmtSamPerSec) / 10; + int block = samples / waveHeader.fmtExtra; + + + if (duration == 0) { + blocksLeft = 0; + } else { + blocksLeft = ((((duration * 100) / 75) * (waveHeader.fmtSamPerSec)) / (waveHeader.fmtExtra) / 100) + 10; + } +// consolePrintf("Playing %d blocks (%d)\n\n", blocksLeft, duration); + + // No need to seek if we're starting from the beginning + if (block != 0) { + DS::std_fseek(file, dataChunkStart + block * waveHeader.fmtBlockAlign, SEEK_SET); +// consolePrintf("Startframe: %d msec: %d (%d,%d)\n", startFrame, tenthssec, samples, block); + } + + + //decompressBlock(); + playNextBlock(); + numLoops = numLoops; +} + +void update() { + playNextBlock(); +} + +void decompressBlock() { + int block[2048]; + bool loop = false; + + blockCount++; + + if (blockCount < 10) return; + + + do { + DS::std_fread((const void *) &blockHeader, sizeof(blockHeader), 1, file); + + DS::std_fread(&block[0], waveHeader.fmtBlockAlign - sizeof(blockHeader), 1, file); + + if (DS::std_feof(file) ) { + // Reached end of file, so loop + + + if ((numLoops == -1) || (numLoops > 1)) { + // Seek file to first packet + if (numLoops != -1) { + numLoops--; + } + DS::std_fseek(file, dataChunkStart, SEEK_SET); + loop = true; + } else { + // Fill decompression buffer with zeros to prevent glitching + for (int r = 0; r < waveHeader.fmtExtra; r++) { + decompressionBuffer[r] = 0; + } +// consolePrintf("Stopping music\n"); + stopTrack(); + return; + } + + } else { + loop = false; + } + + } while (loop); + + + if (blocksLeft > 0) { + blocksLeft--; + // consolePrintf("%d ", blocksLeft); + if (blocksLeft == 0) { + stopTrack(); + return; + } + } + + // First sample is in header + decompressionBuffer[0] = blockHeader.firstSample; + + // Set up initial table indeces + int stepTableIndex = blockHeader.stepTableIndex; + int prevSample = blockHeader.firstSample; + +// consolePrintf("Decompressing block step=%d fs=%d\n", stepTableIndex, prevSample); + + for (int r = 0; r < waveHeader.fmtExtra - 1; r++) { + + int word = block[r >> 3]; + int offset = 0; + + switch (7 - (r & 0x0007)) { + case 0: { + offset = (word & 0xF0000000) >> 28; + break; + } + + case 1: { + offset = (word & 0x0F000000) >> 24; + break; + } + + case 2: { + offset = (word & 0x00F00000) >> 20; + break; + } + + case 3: { + offset = (word & 0x000F0000) >> 16; + break; + } + + case 4: { + offset = (word & 0x0000F000) >> 12; + break; + } + + case 5: { + offset = (word & 0x00000F00) >> 8; + break; + } + + case 6: { + offset = (word & 0x000000F0) >> 4; + break; + } + + case 7: { + offset = (word & 0x0000000F); + break; + } + } + + int diff = 0; + + if (offset & 4) { + diff = diff + stepTab[stepTableIndex]; + } + + if (offset & 2) { + diff = diff + (stepTab[stepTableIndex] >> 1); + } + + if (offset & 1) { + diff = diff + (stepTab[stepTableIndex] >> 2); + } + + diff = diff + (stepTab[stepTableIndex] >> 3); + + if (offset & 8) { + diff = -diff; + } + + int newSample = prevSample + diff; + + if (newSample > 32767) newSample = 32767; + if (newSample < -32768) newSample = -32768; + + decompressionBuffer[r + 1] = newSample; + + prevSample = newSample; + + stepTableIndex += indexTab[offset]; + + if (stepTableIndex > 88) stepTableIndex = 88; + if (stepTableIndex < 0) stepTableIndex = 0; + + + } +} + +void playNextBlock() { + if (!isPlayingFlag) return; + int lastBlockId = -1; + + while (IPC->adpcm.semaphore); // Wait for buffer to become free if needed + IPC->adpcm.semaphore = true; // Lock the buffer structure to prevent clashing with the ARM7 +// DC_FlushAll(); + + //-8644, 25088 + for (int block = fillPos + 1; block < fillPos + 4; block++) { + + int blockId = block & 3; + + if (IPC->streamFillNeeded[blockId]) { + + IPC->streamFillNeeded[blockId] = false; +// DC_FlushAll(); + +/* if (!(REG_KEYINPUT & KEY_R)) { + //consolePrintf("Align: %d First: %d Step:%d Res:%d\n", waveHeader.fmtBlockAlign, blockHeader.firstSample, blockHeader.stepTableIndex, blockHeader.reserved); + consolePrintf("Filling buffer %d\n", blockId); + }*/ + for (int r = blockId * BUFFER_CHUNK_SIZE; r < (blockId + 1) * BUFFER_CHUNK_SIZE; r++) { + if (isPlayingFlag) { + audioBuffer[r] = decompressionBuffer[sampleNum++]; + if (sampleNum >= waveHeader.fmtExtra) { + decompressBlock(); + sampleNum = 0; + } + } + } + + lastBlockId = blockId; + IPC->streamFillNeeded[blockId] = false; +// DC_FlushAll(); + + } + + + + } + + + + if (lastBlockId != -1) { + fillPos = lastBlockId; +/* if (!(REG_KEYINPUT & KEY_R)) { + consolePrintf("Frame fill done\n"); + }*/ + } + IPC->adpcm.semaphore = false; // Release the buffer structure +// DC_FlushAll(); +} + +void stopTrack() { + if (!isPlayingFlag) return; + + DS::std_fclose(file); + + isPlayingFlag = false; + + for (int r = 0; r < BUFFER_SIZE; r++) { + audioBuffer[r] = 0; + } + + for (int r= 0; r < waveHeader.fmtExtra; r++) { + decompressionBuffer[r] = 0; + } +// DS::stopSound(1); + +// free(audioBuffer); +// free(decompressionBuffer); + + DC_FlushAll(); +} + +bool checkCD() { + // Need to check whethe CD audio files are present - do this by trying to open Track1.wav. + consolePrintf("Attempted to open cd drive\n"); + + Common::String path = ConfMan.get("path"); + path = path + "/track2.wav"; + // 6577 153 154 + consolePrintf("Looking for %s...", path.c_str()); + + FILE* file; + if ((file = DS::std_fopen(path.c_str(), "r"))) { + consolePrintf("Success!\n"); + setActive(true); + DS::std_fclose(file); + return true; + } else { + setActive(false); + consolePrintf("Failed!\n"); + return false; + } +} + +bool isPlaying() { + return isPlayingFlag; +} + +} +} + diff --git a/backends/platform/ds/arm9/source/cdaudio.h b/backends/platform/ds/arm9/source/cdaudio.h new file mode 100644 index 0000000000..8abee25e77 --- /dev/null +++ b/backends/platform/ds/arm9/source/cdaudio.h @@ -0,0 +1,74 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + #ifndef _CDAUDIO_H_ +#define _CDAUDIO_H_ + +namespace DS { +namespace CD { + +void setActive(bool active); +void playTrack(int track, int numLoops, int startFrame, int duration); +void stopTrack(); +bool checkCD(); +bool getActive(); +bool isPlaying(); +void update(); + +} +} + +#endif +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + #ifndef _CDAUDIO_H_ +#define _CDAUDIO_H_ + +namespace DS { +namespace CD { + +void setActive(bool active); +void playTrack(int track, int numLoops, int startFrame, int duration); +void stopTrack(); +bool checkCD(); +bool getActive(); +bool isPlaying(); +void update(); + +} +} + +#endif diff --git a/backends/platform/ds/arm9/source/compressor/lz.cpp b/backends/platform/ds/arm9/source/compressor/lz.cpp new file mode 100644 index 0000000000..237b432ce8 --- /dev/null +++ b/backends/platform/ds/arm9/source/compressor/lz.cpp @@ -0,0 +1,1078 @@ +/************************************************************************* +* Name: lz.c +* Author: Marcus Geelnard +* Description: LZ77 coder/decoder implementation. +* Reentrant: Yes +* $Id$ +* +* The LZ77 compression scheme is a substitutional compression scheme +* proposed by Abraham Lempel and Jakob Ziv in 1977. It is very simple in +* its design, and uses no fancy bit level compression. +* +* This is my first attempt at an implementation of a LZ77 code/decoder. +* +* The principle of the LZ77 compression algorithm is to store repeated +* occurrences of strings as references to previous occurrences of the same +* string. The point is that the reference consumes less space than the +* string itself, provided that the string is long enough (in this +* implementation, the string has to be at least 4 bytes long, since the +* minimum coded reference is 3 bytes long). Also note that the term +* "string" refers to any kind of byte sequence (it does not have to be +* an ASCII string, for instance). +* +* The coder uses a brute force approach to finding string matches in the +* history buffer (or "sliding window", if you wish), which is very, very +* slow. I recon the complexity is somewhere between O(n^2) and O(n^3), +* depending on the input data. +* +* There is also a faster implementation that uses a large working buffer +* in which a "jump table" is stored, which is used to quickly find +* possible string matches (see the source code for LZ_CompressFast() for +* more information). The faster method is an order of magnitude faster, +* and also does a full string search in the entire input buffer (it does +* not use a sliding window). +* +* The upside is that decompression is very fast, and the compression ratio +* is often very good. +* +* The reference to a string is coded as a (length,offset) pair, where the +* length indicates the length of the string, and the offset gives the +* offset from the current data position. To distinguish between string +* references and literal strings (uncompressed bytes), a string reference +* is preceded by a marker byte, which is chosen as the least common byte +* symbol in the input data stream (this marker byte is stored in the +* output stream as the first byte). +* +* Occurrences of the marker byte in the stream are encoded as the marker +* byte followed by a zero byte, which means that occurrences of the marker +* byte have to be coded with two bytes. +* +* The lengths and offsets are coded in a variable length fashion, allowing +* values of any magnitude (up to 4294967295 in this implementation). +* +* With this compression scheme, the worst case compression result is +* (257/256)*insize + 1. +* +*------------------------------------------------------------------------- +* Copyright (c) 2003-2004 Marcus Geelnard +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would +* be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not +* be misrepresented as being the original software. +* +* 3. This notice may not be removed or altered from any source +* distribution. +* +* Marcus Geelnard +* marcus.geelnard at home.se +*************************************************************************/ + + +/************************************************************************* +* Constants used for LZ77 coding +*************************************************************************/ + +/* Maximum offset (can be any size < 2^32). Lower values gives faster + compression, while higher values gives better compression. + NOTE: LZ_CompressFast does not use this constant. */ +#define LZ_MAX_OFFSET 512 + + + +/************************************************************************* +* INTERNAL FUNCTIONS * +*************************************************************************/ + + +/************************************************************************* +* _LZ_StringCompare() - Return maximum length string match. +*************************************************************************/ + +inline static unsigned int _LZ_StringCompare( unsigned char * str1, + unsigned char * str2, unsigned int minlen, unsigned int maxlen ) +{ + unsigned int len; + + for( len = minlen; (len < maxlen) && (str1[len] == str2[len]); ++ len ); + + return len; +} + + +/************************************************************************* +* _LZ_WriteVarSize() - Write unsigned integer with variable number of +* bytes depending on value. +*************************************************************************/ + +inline static int _LZ_WriteVarSize( unsigned int x, unsigned char * buf ) +{ + unsigned int y; + int num_bytes, i, b; + + /* Determine number of bytes needed to store the number x */ + y = x >> 3; + for( num_bytes = 5; num_bytes >= 2; -- num_bytes ) + { + if( y & 0xfe000000 ) break; + y <<= 7; + } + + /* Write all bytes, seven bits in each, with 8:th bit set for all */ + /* but the last byte. */ + for( i = num_bytes-1; i >= 0; -- i ) + { + b = (x >> (i*7)) & 0x0000007f; + if( i > 0 ) + { + b |= 0x00000080; + } + *buf ++ = (unsigned char) b; + } + + /* Return number of bytes written */ + return num_bytes; +} + + +/************************************************************************* +* _LZ_ReadVarSize() - Read unsigned integer with variable number of +* bytes depending on value. +*************************************************************************/ + +inline static int _LZ_ReadVarSize( unsigned int * x, unsigned char * buf ) +{ + unsigned int y, b, num_bytes; + + /* Read complete value (stop when byte contains zero in 8:th bit) */ + y = 0; + num_bytes = 0; + do + { + b = (unsigned int) (*buf ++); + y = (y << 7) | (b & 0x0000007f); + ++ num_bytes; + } + while( b & 0x00000080 ); + + /* Store value in x */ + *x = y; + + /* Return number of bytes read */ + return num_bytes; +} + + + +/************************************************************************* +* PUBLIC FUNCTIONS * +*************************************************************************/ + + +/************************************************************************* +* LZ_Compress() - Compress a block of data using an LZ77 coder. +* in - Input (uncompressed) buffer. +* out - Output (compressed) buffer. This buffer must be 0.4% larger +* than the input buffer, plus one byte. +* insize - Number of input bytes. +* The function returns the size of the compressed data. +*************************************************************************/ + +int LZ_Compress( unsigned char *in, unsigned char *out, + unsigned int insize ) +{ + unsigned char marker, symbol; + unsigned int inpos, outpos, bytesleft, i; + unsigned int maxoffset, offset, bestoffset; + unsigned int maxlength, length, bestlength; + unsigned int histogram[ 256 ]; + unsigned char *ptr1, *ptr2; + + /* Do we have anything to compress? */ + if( insize < 1 ) + { + return 0; + } + + /* Create histogram */ + for( i = 0; i < 256; ++ i ) + { + histogram[ i ] = 0; + } + for( i = 0; i < insize; ++ i ) + { + ++ histogram[ in[ i ] ]; + } + + /* Find the least common byte, and use it as the code marker */ + marker = 0; + for( i = 1; i < 256; ++ i ) + { + if( histogram[ i ] < histogram[ marker ] ) + { + marker = i; + } + } + + /* Remember the repetition marker for the decoder */ + out[ 0 ] = marker; + + /* Start of compression */ + inpos = 0; + outpos = 1; + + /* Main compression loop */ + bytesleft = insize; + do + { + /* Determine most distant position */ + if( inpos > LZ_MAX_OFFSET ) maxoffset = LZ_MAX_OFFSET; + else maxoffset = inpos; + + /* Get pointer to current position */ + ptr1 = &in[ inpos ]; + + /* Search history window for maximum length string match */ + bestlength = 3; + bestoffset = 0; + for( offset = 3; offset <= maxoffset; ++ offset ) + { + /* Get pointer to candidate string */ + ptr2 = &ptr1[ -offset ]; + + /* Quickly determine if this is a candidate (for speed) */ + if( (ptr1[ 0 ] == ptr2[ 0 ]) && + (ptr1[ bestlength ] == ptr2[ bestlength ]) ) + { + /* Determine maximum length for this offset */ + maxlength = (bytesleft < offset ? bytesleft : offset); + + /* Count maximum length match at this offset */ + length = _LZ_StringCompare( ptr1, ptr2, 0, maxlength ); + + /* Better match than any previous match? */ + if( length > bestlength ) + { + bestlength = length; + bestoffset = offset; + } + } + } + + /* Was there a good enough match? */ + if( (bestlength >= 8) || + ((bestlength == 4) && (bestoffset <= 0x0000007f)) || + ((bestlength == 5) && (bestoffset <= 0x00003fff)) || + ((bestlength == 6) && (bestoffset <= 0x001fffff)) || + ((bestlength == 7) && (bestoffset <= 0x0fffffff)) ) + { + out[ outpos ++ ] = (unsigned char) marker; + outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] ); + outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] ); + inpos += bestlength; + bytesleft -= bestlength; + } + else + { + /* Output single byte (or two bytes if marker byte) */ + symbol = in[ inpos ++ ]; + out[ outpos ++ ] = symbol; + if( symbol == marker ) + { + out[ outpos ++ ] = 0; + } + -- bytesleft; + } + } + while( bytesleft > 3 ); + + /* Dump remaining bytes, if any */ + while( inpos < insize ) + { + if( in[ inpos ] == marker ) + { + out[ outpos ++ ] = marker; + out[ outpos ++ ] = 0; + } + else + { + out[ outpos ++ ] = in[ inpos ]; + } + ++ inpos; + } + + return outpos; +} + + +/************************************************************************* +* LZ_CompressFast() - Compress a block of data using an LZ77 coder. +* in - Input (uncompressed) buffer. +* out - Output (compressed) buffer. This buffer must be 0.4% larger +* than the input buffer, plus one byte. +* insize - Number of input bytes. +* work - Pointer to a temporary buffer (internal working buffer), which +* must be able to hold (insize+65536) unsigned integers. +* The function returns the size of the compressed data. +*************************************************************************/ + +int LZ_CompressFast( unsigned char *in, unsigned char *out, + unsigned int insize, unsigned int *work ) +{ + unsigned char marker, symbol; + unsigned int inpos, outpos, bytesleft, i, index, symbols; + unsigned int offset, bestoffset; + unsigned int maxlength, length, bestlength; + unsigned int histogram[ 256 ], *lastindex, *jumptable; + unsigned char *ptr1, *ptr2; + + /* Do we have anything to compress? */ + if( insize < 1 ) + { + return 0; + } + + /* Assign arrays to the working area */ + lastindex = work; + jumptable = &work[ 65536 ]; + + /* Build a "jump table". Here is how the jump table works: + jumptable[i] points to the nearest previous occurrence of the same + symbol pair as in[i]:in[i+1], so in[i] == in[jumptable[i]] and + in[i+1] == in[jumptable[i]+1]. Following the jump table gives a + dramatic boost for the string search'n'match loop compared to doing + a brute force search. */ + for( i = 0; i < 65536; ++ i ) + { + lastindex[ i ] = 0xffffffff; + } + for( i = 0; i < insize-1; ++ i ) + { + symbols = (((unsigned int)in[i]) << 8) | ((unsigned int)in[i+1]); + index = lastindex[ symbols ]; + lastindex[ symbols ] = i; + jumptable[ i ] = index; + } + jumptable[ insize-1 ] = 0xffffffff; + + /* Create histogram */ + for( i = 0; i < 256; ++ i ) + { + histogram[ i ] = 0; + } + for( i = 0; i < insize; ++ i ) + { + ++ histogram[ in[ i ] ]; + } + + /* Find the least common byte, and use it as the code marker */ + marker = 0; + for( i = 1; i < 256; ++ i ) + { + if( histogram[ i ] < histogram[ marker ] ) + { + marker = i; + } + } + + /* Remember the repetition marker for the decoder */ + out[ 0 ] = marker; + + /* Start of compression */ + inpos = 0; + outpos = 1; + + /* Main compression loop */ + bytesleft = insize; + do + { + /* Get pointer to current position */ + ptr1 = &in[ inpos ]; + + /* Search history window for maximum length string match */ + bestlength = 3; + bestoffset = 0; + index = jumptable[ inpos ]; + while( index != 0xffffffff ) + { + /* Get pointer to candidate string */ + ptr2 = &in[ index ]; + + /* Quickly determine if this is a candidate (for speed) */ + if( ptr2[ bestlength ] == ptr1[ bestlength ] ) + { + /* Determine maximum length for this offset */ + offset = inpos - index; + maxlength = (bytesleft < offset ? bytesleft : offset); + + /* Count maximum length match at this offset */ + length = _LZ_StringCompare( ptr1, ptr2, 2, maxlength ); + + /* Better match than any previous match? */ + if( length > bestlength ) + { + bestlength = length; + bestoffset = offset; + } + } + + /* Get next possible index from jump table */ + index = jumptable[ index ]; + } + + /* Was there a good enough match? */ + if( (bestlength >= 8) || + ((bestlength == 4) && (bestoffset <= 0x0000007f)) || + ((bestlength == 5) && (bestoffset <= 0x00003fff)) || + ((bestlength == 6) && (bestoffset <= 0x001fffff)) || + ((bestlength == 7) && (bestoffset <= 0x0fffffff)) ) + { + out[ outpos ++ ] = (unsigned char) marker; + outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] ); + outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] ); + inpos += bestlength; + bytesleft -= bestlength; + } + else + { + /* Output single byte (or two bytes if marker byte) */ + symbol = in[ inpos ++ ]; + out[ outpos ++ ] = symbol; + if( symbol == marker ) + { + out[ outpos ++ ] = 0; + } + -- bytesleft; + } + } + while( bytesleft > 3 ); + + /* Dump remaining bytes, if any */ + while( inpos < insize ) + { + if( in[ inpos ] == marker ) + { + out[ outpos ++ ] = marker; + out[ outpos ++ ] = 0; + } + else + { + out[ outpos ++ ] = in[ inpos ]; + } + ++ inpos; + } + + return outpos; +} + + +/************************************************************************* +* LZ_Uncompress() - Uncompress a block of data using an LZ77 decoder. +* in - Input (compressed) buffer. +* out - Output (uncompressed) buffer. This buffer must be large +* enough to hold the uncompressed data. +* insize - Number of input bytes. +*************************************************************************/ + +void LZ_Uncompress( unsigned char *in, unsigned char *out, + unsigned int insize ) +{ + unsigned char marker, symbol; + unsigned int i, inpos, outpos, length, offset; + + /* Do we have anything to compress? */ + if( insize < 1 ) + { + return; + } + + /* Get marker symbol from input stream */ + marker = in[ 0 ]; + inpos = 1; + + /* Main decompression loop */ + outpos = 0; + do + { + symbol = in[ inpos ++ ]; + if( symbol == marker ) + { + /* We had a marker byte */ + if( in[ inpos ] == 0 ) + { + /* It was a single occurrence of the marker byte */ + out[ outpos ++ ] = marker; + ++ inpos; + } + else + { + /* Extract true length and offset */ + inpos += _LZ_ReadVarSize( &length, &in[ inpos ] ); + inpos += _LZ_ReadVarSize( &offset, &in[ inpos ] ); + + /* Copy corresponding data from history window */ + for( i = 0; i < length; ++ i ) + { + out[ outpos ] = out[ outpos - offset ]; + ++ outpos; + } + } + } + else + { + /* No marker, plain copy */ + out[ outpos ++ ] = symbol; + } + } + while( inpos < insize ); +} +/************************************************************************* +* Name: lz.c +* Author: Marcus Geelnard +* Description: LZ77 coder/decoder implementation. +* Reentrant: Yes +* $Id$ +* +* The LZ77 compression scheme is a substitutional compression scheme +* proposed by Abraham Lempel and Jakob Ziv in 1977. It is very simple in +* its design, and uses no fancy bit level compression. +* +* This is my first attempt at an implementation of a LZ77 code/decoder. +* +* The principle of the LZ77 compression algorithm is to store repeated +* occurrences of strings as references to previous occurrences of the same +* string. The point is that the reference consumes less space than the +* string itself, provided that the string is long enough (in this +* implementation, the string has to be at least 4 bytes long, since the +* minimum coded reference is 3 bytes long). Also note that the term +* "string" refers to any kind of byte sequence (it does not have to be +* an ASCII string, for instance). +* +* The coder uses a brute force approach to finding string matches in the +* history buffer (or "sliding window", if you wish), which is very, very +* slow. I recon the complexity is somewhere between O(n^2) and O(n^3), +* depending on the input data. +* +* There is also a faster implementation that uses a large working buffer +* in which a "jump table" is stored, which is used to quickly find +* possible string matches (see the source code for LZ_CompressFast() for +* more information). The faster method is an order of magnitude faster, +* and also does a full string search in the entire input buffer (it does +* not use a sliding window). +* +* The upside is that decompression is very fast, and the compression ratio +* is often very good. +* +* The reference to a string is coded as a (length,offset) pair, where the +* length indicates the length of the string, and the offset gives the +* offset from the current data position. To distinguish between string +* references and literal strings (uncompressed bytes), a string reference +* is preceded by a marker byte, which is chosen as the least common byte +* symbol in the input data stream (this marker byte is stored in the +* output stream as the first byte). +* +* Occurrences of the marker byte in the stream are encoded as the marker +* byte followed by a zero byte, which means that occurrences of the marker +* byte have to be coded with two bytes. +* +* The lengths and offsets are coded in a variable length fashion, allowing +* values of any magnitude (up to 4294967295 in this implementation). +* +* With this compression scheme, the worst case compression result is +* (257/256)*insize + 1. +* +*------------------------------------------------------------------------- +* Copyright (c) 2003-2004 Marcus Geelnard +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would +* be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not +* be misrepresented as being the original software. +* +* 3. This notice may not be removed or altered from any source +* distribution. +* +* Marcus Geelnard +* marcus.geelnard at home.se +*************************************************************************/ + + +/************************************************************************* +* Constants used for LZ77 coding +*************************************************************************/ + +/* Maximum offset (can be any size < 2^32). Lower values gives faster + compression, while higher values gives better compression. + NOTE: LZ_CompressFast does not use this constant. */ +#define LZ_MAX_OFFSET 512 + + + +/************************************************************************* +* INTERNAL FUNCTIONS * +*************************************************************************/ + + +/************************************************************************* +* _LZ_StringCompare() - Return maximum length string match. +*************************************************************************/ + +inline static unsigned int _LZ_StringCompare( unsigned char * str1, + unsigned char * str2, unsigned int minlen, unsigned int maxlen ) +{ + unsigned int len; + + for( len = minlen; (len < maxlen) && (str1[len] == str2[len]); ++ len ); + + return len; +} + + +/************************************************************************* +* _LZ_WriteVarSize() - Write unsigned integer with variable number of +* bytes depending on value. +*************************************************************************/ + +inline static int _LZ_WriteVarSize( unsigned int x, unsigned char * buf ) +{ + unsigned int y; + int num_bytes, i, b; + + /* Determine number of bytes needed to store the number x */ + y = x >> 3; + for( num_bytes = 5; num_bytes >= 2; -- num_bytes ) + { + if( y & 0xfe000000 ) break; + y <<= 7; + } + + /* Write all bytes, seven bits in each, with 8:th bit set for all */ + /* but the last byte. */ + for( i = num_bytes-1; i >= 0; -- i ) + { + b = (x >> (i*7)) & 0x0000007f; + if( i > 0 ) + { + b |= 0x00000080; + } + *buf ++ = (unsigned char) b; + } + + /* Return number of bytes written */ + return num_bytes; +} + + +/************************************************************************* +* _LZ_ReadVarSize() - Read unsigned integer with variable number of +* bytes depending on value. +*************************************************************************/ + +inline static int _LZ_ReadVarSize( unsigned int * x, unsigned char * buf ) +{ + unsigned int y, b, num_bytes; + + /* Read complete value (stop when byte contains zero in 8:th bit) */ + y = 0; + num_bytes = 0; + do + { + b = (unsigned int) (*buf ++); + y = (y << 7) | (b & 0x0000007f); + ++ num_bytes; + } + while( b & 0x00000080 ); + + /* Store value in x */ + *x = y; + + /* Return number of bytes read */ + return num_bytes; +} + + + +/************************************************************************* +* PUBLIC FUNCTIONS * +*************************************************************************/ + + +/************************************************************************* +* LZ_Compress() - Compress a block of data using an LZ77 coder. +* in - Input (uncompressed) buffer. +* out - Output (compressed) buffer. This buffer must be 0.4% larger +* than the input buffer, plus one byte. +* insize - Number of input bytes. +* The function returns the size of the compressed data. +*************************************************************************/ + +int LZ_Compress( unsigned char *in, unsigned char *out, + unsigned int insize ) +{ + unsigned char marker, symbol; + unsigned int inpos, outpos, bytesleft, i; + unsigned int maxoffset, offset, bestoffset; + unsigned int maxlength, length, bestlength; + unsigned int histogram[ 256 ]; + unsigned char *ptr1, *ptr2; + + /* Do we have anything to compress? */ + if( insize < 1 ) + { + return 0; + } + + /* Create histogram */ + for( i = 0; i < 256; ++ i ) + { + histogram[ i ] = 0; + } + for( i = 0; i < insize; ++ i ) + { + ++ histogram[ in[ i ] ]; + } + + /* Find the least common byte, and use it as the code marker */ + marker = 0; + for( i = 1; i < 256; ++ i ) + { + if( histogram[ i ] < histogram[ marker ] ) + { + marker = i; + } + } + + /* Remember the repetition marker for the decoder */ + out[ 0 ] = marker; + + /* Start of compression */ + inpos = 0; + outpos = 1; + + /* Main compression loop */ + bytesleft = insize; + do + { + /* Determine most distant position */ + if( inpos > LZ_MAX_OFFSET ) maxoffset = LZ_MAX_OFFSET; + else maxoffset = inpos; + + /* Get pointer to current position */ + ptr1 = &in[ inpos ]; + + /* Search history window for maximum length string match */ + bestlength = 3; + bestoffset = 0; + for( offset = 3; offset <= maxoffset; ++ offset ) + { + /* Get pointer to candidate string */ + ptr2 = &ptr1[ -offset ]; + + /* Quickly determine if this is a candidate (for speed) */ + if( (ptr1[ 0 ] == ptr2[ 0 ]) && + (ptr1[ bestlength ] == ptr2[ bestlength ]) ) + { + /* Determine maximum length for this offset */ + maxlength = (bytesleft < offset ? bytesleft : offset); + + /* Count maximum length match at this offset */ + length = _LZ_StringCompare( ptr1, ptr2, 0, maxlength ); + + /* Better match than any previous match? */ + if( length > bestlength ) + { + bestlength = length; + bestoffset = offset; + } + } + } + + /* Was there a good enough match? */ + if( (bestlength >= 8) || + ((bestlength == 4) && (bestoffset <= 0x0000007f)) || + ((bestlength == 5) && (bestoffset <= 0x00003fff)) || + ((bestlength == 6) && (bestoffset <= 0x001fffff)) || + ((bestlength == 7) && (bestoffset <= 0x0fffffff)) ) + { + out[ outpos ++ ] = (unsigned char) marker; + outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] ); + outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] ); + inpos += bestlength; + bytesleft -= bestlength; + } + else + { + /* Output single byte (or two bytes if marker byte) */ + symbol = in[ inpos ++ ]; + out[ outpos ++ ] = symbol; + if( symbol == marker ) + { + out[ outpos ++ ] = 0; + } + -- bytesleft; + } + } + while( bytesleft > 3 ); + + /* Dump remaining bytes, if any */ + while( inpos < insize ) + { + if( in[ inpos ] == marker ) + { + out[ outpos ++ ] = marker; + out[ outpos ++ ] = 0; + } + else + { + out[ outpos ++ ] = in[ inpos ]; + } + ++ inpos; + } + + return outpos; +} + + +/************************************************************************* +* LZ_CompressFast() - Compress a block of data using an LZ77 coder. +* in - Input (uncompressed) buffer. +* out - Output (compressed) buffer. This buffer must be 0.4% larger +* than the input buffer, plus one byte. +* insize - Number of input bytes. +* work - Pointer to a temporary buffer (internal working buffer), which +* must be able to hold (insize+65536) unsigned integers. +* The function returns the size of the compressed data. +*************************************************************************/ + +int LZ_CompressFast( unsigned char *in, unsigned char *out, + unsigned int insize, unsigned int *work ) +{ + unsigned char marker, symbol; + unsigned int inpos, outpos, bytesleft, i, index, symbols; + unsigned int offset, bestoffset; + unsigned int maxlength, length, bestlength; + unsigned int histogram[ 256 ], *lastindex, *jumptable; + unsigned char *ptr1, *ptr2; + + /* Do we have anything to compress? */ + if( insize < 1 ) + { + return 0; + } + + /* Assign arrays to the working area */ + lastindex = work; + jumptable = &work[ 65536 ]; + + /* Build a "jump table". Here is how the jump table works: + jumptable[i] points to the nearest previous occurrence of the same + symbol pair as in[i]:in[i+1], so in[i] == in[jumptable[i]] and + in[i+1] == in[jumptable[i]+1]. Following the jump table gives a + dramatic boost for the string search'n'match loop compared to doing + a brute force search. */ + for( i = 0; i < 65536; ++ i ) + { + lastindex[ i ] = 0xffffffff; + } + for( i = 0; i < insize-1; ++ i ) + { + symbols = (((unsigned int)in[i]) << 8) | ((unsigned int)in[i+1]); + index = lastindex[ symbols ]; + lastindex[ symbols ] = i; + jumptable[ i ] = index; + } + jumptable[ insize-1 ] = 0xffffffff; + + /* Create histogram */ + for( i = 0; i < 256; ++ i ) + { + histogram[ i ] = 0; + } + for( i = 0; i < insize; ++ i ) + { + ++ histogram[ in[ i ] ]; + } + + /* Find the least common byte, and use it as the code marker */ + marker = 0; + for( i = 1; i < 256; ++ i ) + { + if( histogram[ i ] < histogram[ marker ] ) + { + marker = i; + } + } + + /* Remember the repetition marker for the decoder */ + out[ 0 ] = marker; + + /* Start of compression */ + inpos = 0; + outpos = 1; + + /* Main compression loop */ + bytesleft = insize; + do + { + /* Get pointer to current position */ + ptr1 = &in[ inpos ]; + + /* Search history window for maximum length string match */ + bestlength = 3; + bestoffset = 0; + index = jumptable[ inpos ]; + while( index != 0xffffffff ) + { + /* Get pointer to candidate string */ + ptr2 = &in[ index ]; + + /* Quickly determine if this is a candidate (for speed) */ + if( ptr2[ bestlength ] == ptr1[ bestlength ] ) + { + /* Determine maximum length for this offset */ + offset = inpos - index; + maxlength = (bytesleft < offset ? bytesleft : offset); + + /* Count maximum length match at this offset */ + length = _LZ_StringCompare( ptr1, ptr2, 2, maxlength ); + + /* Better match than any previous match? */ + if( length > bestlength ) + { + bestlength = length; + bestoffset = offset; + } + } + + /* Get next possible index from jump table */ + index = jumptable[ index ]; + } + + /* Was there a good enough match? */ + if( (bestlength >= 8) || + ((bestlength == 4) && (bestoffset <= 0x0000007f)) || + ((bestlength == 5) && (bestoffset <= 0x00003fff)) || + ((bestlength == 6) && (bestoffset <= 0x001fffff)) || + ((bestlength == 7) && (bestoffset <= 0x0fffffff)) ) + { + out[ outpos ++ ] = (unsigned char) marker; + outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] ); + outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] ); + inpos += bestlength; + bytesleft -= bestlength; + } + else + { + /* Output single byte (or two bytes if marker byte) */ + symbol = in[ inpos ++ ]; + out[ outpos ++ ] = symbol; + if( symbol == marker ) + { + out[ outpos ++ ] = 0; + } + -- bytesleft; + } + } + while( bytesleft > 3 ); + + /* Dump remaining bytes, if any */ + while( inpos < insize ) + { + if( in[ inpos ] == marker ) + { + out[ outpos ++ ] = marker; + out[ outpos ++ ] = 0; + } + else + { + out[ outpos ++ ] = in[ inpos ]; + } + ++ inpos; + } + + return outpos; +} + + +/************************************************************************* +* LZ_Uncompress() - Uncompress a block of data using an LZ77 decoder. +* in - Input (compressed) buffer. +* out - Output (uncompressed) buffer. This buffer must be large +* enough to hold the uncompressed data. +* insize - Number of input bytes. +*************************************************************************/ + +void LZ_Uncompress( unsigned char *in, unsigned char *out, + unsigned int insize ) +{ + unsigned char marker, symbol; + unsigned int i, inpos, outpos, length, offset; + + /* Do we have anything to compress? */ + if( insize < 1 ) + { + return; + } + + /* Get marker symbol from input stream */ + marker = in[ 0 ]; + inpos = 1; + + /* Main decompression loop */ + outpos = 0; + do + { + symbol = in[ inpos ++ ]; + if( symbol == marker ) + { + /* We had a marker byte */ + if( in[ inpos ] == 0 ) + { + /* It was a single occurrence of the marker byte */ + out[ outpos ++ ] = marker; + ++ inpos; + } + else + { + /* Extract true length and offset */ + inpos += _LZ_ReadVarSize( &length, &in[ inpos ] ); + inpos += _LZ_ReadVarSize( &offset, &in[ inpos ] ); + + /* Copy corresponding data from history window */ + for( i = 0; i < length; ++ i ) + { + out[ outpos ] = out[ outpos - offset ]; + ++ outpos; + } + } + } + else + { + /* No marker, plain copy */ + out[ outpos ++ ] = symbol; + } + } + while( inpos < insize ); +} diff --git a/backends/platform/ds/arm9/source/compressor/lz.h b/backends/platform/ds/arm9/source/compressor/lz.h new file mode 100644 index 0000000000..dc10210742 --- /dev/null +++ b/backends/platform/ds/arm9/source/compressor/lz.h @@ -0,0 +1,100 @@ +/************************************************************************* +* Name: lz.h +* Author: Marcus Geelnard +* Description: LZ77 coder/decoder interface. +* Reentrant: Yes +* $Id$ +*------------------------------------------------------------------------- +* Copyright (c) 2003-2004 Marcus Geelnard +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would +* be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not +* be misrepresented as being the original software. +* +* 3. This notice may not be removed or altered from any source +* distribution. +* +* Marcus Geelnard +* marcus.geelnard at home.se +*************************************************************************/ + +#ifndef _lz_h_ +#define _lz_h_ + + + +/************************************************************************* +* Function prototypes +*************************************************************************/ + +int LZ_Compress( unsigned char *in, unsigned char *out, + unsigned int insize ); +int LZ_CompressFast( unsigned char *in, unsigned char *out, + unsigned int insize, unsigned int *work ); +void LZ_Uncompress( unsigned char *in, unsigned char *out, + unsigned int insize ); + + +#endif /* _lz_h_ */ +/************************************************************************* +* Name: lz.h +* Author: Marcus Geelnard +* Description: LZ77 coder/decoder interface. +* Reentrant: Yes +* $Id$ +*------------------------------------------------------------------------- +* Copyright (c) 2003-2004 Marcus Geelnard +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would +* be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not +* be misrepresented as being the original software. +* +* 3. This notice may not be removed or altered from any source +* distribution. +* +* Marcus Geelnard +* marcus.geelnard at home.se +*************************************************************************/ + +#ifndef _lz_h_ +#define _lz_h_ + + + +/************************************************************************* +* Function prototypes +*************************************************************************/ + +int LZ_Compress( unsigned char *in, unsigned char *out, + unsigned int insize ); +int LZ_CompressFast( unsigned char *in, unsigned char *out, + unsigned int insize, unsigned int *work ); +void LZ_Uncompress( unsigned char *in, unsigned char *out, + unsigned int insize ); + + +#endif /* _lz_h_ */ diff --git a/backends/platform/ds/arm9/source/console2.h b/backends/platform/ds/arm9/source/console2.h new file mode 100644 index 0000000000..6b9a677cc7 --- /dev/null +++ b/backends/platform/ds/arm9/source/console2.h @@ -0,0 +1,124 @@ +////////////////////////////////////////////////////////////////////// +// +// consol.h --provides basic consol type print functionality +// +// version 0.1, February 14, 2005 +// +// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto) +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any +// damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any +// purpose, including commercial applications, and to alter it and +// redistribute it freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you +// must not claim that you wrote the original software. If you use +// this software in a product, an acknowledgment in the product +// documentation would be appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and +// must not be misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source +// distribution. +// +// Changelog: +// 0.1: First version +// 0.2: Fixed sprite mapping bug. 1D mapping should work now. +// Changed some register defines for consistency. +// +////////////////////////////////////////////////////////////////////// +#ifndef CONSOLE_H2 +#define CONSOLE_H2 + +#define CONSOLE_USE_COLOR255 16 + +#ifdef __cplusplus +extern "C" { +#endif + +void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth); +void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth); + +void consolePrintf(const char* s, ...); + +void consolePrintSet(int x, int y); + +void consolePrintChar(char c); + +void consolePutString(int x, int y, char* s); +void consolePutInt(int x, int y, int d); +void consolePutX(int x, int y, int d); +void consolePutChar(int x, int y, char c); +void consolePutBin(int x, int y, int b); + +void consoleClear(void); + +#ifdef __cplusplus +} +#endif + +#endif +////////////////////////////////////////////////////////////////////// +// +// consol.h --provides basic consol type print functionality +// +// version 0.1, February 14, 2005 +// +// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto) +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any +// damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any +// purpose, including commercial applications, and to alter it and +// redistribute it freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you +// must not claim that you wrote the original software. If you use +// this software in a product, an acknowledgment in the product +// documentation would be appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and +// must not be misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source +// distribution. +// +// Changelog: +// 0.1: First version +// 0.2: Fixed sprite mapping bug. 1D mapping should work now. +// Changed some register defines for consistency. +// +////////////////////////////////////////////////////////////////////// +#ifndef CONSOLE_H2 +#define CONSOLE_H2 + +#define CONSOLE_USE_COLOR255 16 + +#ifdef __cplusplus +extern "C" { +#endif + +void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth); +void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth); + +void consolePrintf(const char* s, ...); + +void consolePrintSet(int x, int y); + +void consolePrintChar(char c); + +void consolePutString(int x, int y, char* s); +void consolePutInt(int x, int y, int d); +void consolePutX(int x, int y, int d); +void consolePutChar(int x, int y, char c); +void consolePutBin(int x, int y, int b); + +void consoleClear(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/backends/platform/ds/arm9/source/dsmain.cpp b/backends/platform/ds/arm9/source/dsmain.cpp new file mode 100644 index 0000000000..b42d9ed15e --- /dev/null +++ b/backends/platform/ds/arm9/source/dsmain.cpp @@ -0,0 +1,3888 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include + +#include //basic print funcionality + +#include +#include "dsmain.h" +#include "string.h" +#include "system.h" +#include "osystem_ds.h" +#include "icons_raw.h" +#include "gba_nds_fat.h" +#include "disc_io.h" +#include "config-manager.h" +#include "engines/scumm/scumm.h" +#include "keyboard_raw.h" +#include "keyboard_pal_raw.h" +#define V16(a, b) ((a << 12) | b) +#include "touchkeyboard.h" +#include "registers_alt.h" +//#include "compact_flash.h" +#include "dsoptions.h" + +namespace DS { + +// From console.c in NDSLib + +//location of cursor +extern u8 row; +extern u8 col; + +// Mouse mode +enum MouseMode { + MOUSE_LEFT, MOUSE_RIGHT, MOUSE_HOVER, MOUSE_NUM_MODES +}; + +// Defines +#define FRAME_TIME 17 +#define SCUMM_GAME_HEIGHT 142 +#define SCUMM_GAME_WIDTH 232 + +int textureID; +u16* texture; + +int frameCount; +int currentTimeMillis; + +// Timer Callback +int callbackInterval; +int callbackTimer; +OSystem::TimerProc callback; + +// Scaled +bool scaledMode; +int scX; +int scY; + +int subScX; +int subScY; +int subScTargetX; +int subScTargetY; +int subScreenWidth = SCUMM_GAME_WIDTH; +int subScreenHeight = SCUMM_GAME_HEIGHT; +int subScreenScale = 256; + + + +// Sound +int bufferSize; +s16* soundBuffer; +int bufferFrame; +int bufferRate; +int bufferSamples; +bool soundHiPart; + +// Events +int lastEventFrame; +bool indyFightState; +bool indyFightRight; + +OSystem::SoundProc soundCallback; +void* soundParam; +int lastCallbackFrame; +bool bufferFirstHalf; +bool bufferSecondHalf; + +// Saved buffers +u8* savedBuffer = NULL; +bool highBuffer; +bool displayModeIs8Bit; + +// Game id +u8 gameID; + +bool consoleEnable = true; +bool gameScreenSwap = false; + +MouseMode mouseMode; + +// Sprites +SpriteEntry sprites[128]; +SpriteEntry spritesMain[128]; +int tweak; + +// Shake +int shakePos = 0; + +// Keyboard +bool keyboardEnable = false; +bool leftHandedMode = false; +bool keyboardIcon = false; + +// Touch +int touchScX, touchScY, touchX, touchY; + +// Dragging +int dragStartX, dragStartY; +bool dragging = false; +int dragScX, dragScY; + +// Interface styles +char gameName[32]; + +// 8-bit surface size +int gameWidth = 320; +int gameHeight = 200; + +enum controlType { + CONT_SCUMM_ORIGINAL, + CONT_SCUMM_SAMNMAX, + CONT_SKY, + CONT_SIMON, +}; + +struct gameListType { + char gameId[16]; + controlType control; +}; + +#define NUM_SUPPORTED_GAMES 15 + +gameListType gameList[NUM_SUPPORTED_GAMES] = { + // Unknown game - use normal SCUMM controls + {"unknown", CONT_SCUMM_ORIGINAL}, + + // SCUMM games + {"maniac", CONT_SCUMM_ORIGINAL}, + {"zak", CONT_SCUMM_ORIGINAL}, + {"loom", CONT_SCUMM_ORIGINAL}, + {"indy3", CONT_SCUMM_ORIGINAL}, + {"atlantis", CONT_SCUMM_ORIGINAL}, + {"monkey", CONT_SCUMM_ORIGINAL}, + {"monkey2", CONT_SCUMM_ORIGINAL}, + {"tentacle", CONT_SCUMM_ORIGINAL}, + {"samnmax", CONT_SCUMM_SAMNMAX}, + + // Non-SCUMM games + {"sky", CONT_SKY}, + {"simon1", CONT_SIMON}, + {"simon2", CONT_SIMON}, + {"gob1", CONT_SCUMM_ORIGINAL}, + {"queen", CONT_SCUMM_ORIGINAL} +}; + +gameListType* currentGame = NULL; + +// Stylus +#define ABS(x) ((x)>0?(x):-(x)) + +bool penDown; +bool penHeld; +bool penReleased; +bool penDownLastFrame; +f32 penX, penY; +int keysDownSaved; +int keysReleasedSaved; + +bool penDownSaved; +bool penReleasedSaved; +int penDownFrames; +int touchXOffset = 0; +int touchYOffset = 0; + +u16 savedPalEntry255 = RGB15(31, 31, 31); + + +extern "C" int scummvm_main(int argc, char *argv[]); +void updateStatus(); + +TransferSound soundControl; + +//plays an 8 bit mono sample at 11025Hz +void playSound(const void* data, u32 length, bool loop, bool adpcm, int rate) +{ + + if (!IPC->soundData) { + soundControl.count = 0; + } + + soundControl.data[soundControl.count].data = data; + soundControl.data[soundControl.count].len = length | (loop? 0x80000000: 0x00000000); + soundControl.data[soundControl.count].rate = rate; // 367 samples per frame + soundControl.data[soundControl.count].pan = 64; + soundControl.data[soundControl.count].vol = 127; + soundControl.data[soundControl.count].format = adpcm? 2: 0; + + soundControl.count++; + + DC_FlushAll(); + IPC->soundData = &soundControl; +} + +void stopSound(int channel) { + playSound(NULL, 0, false, false, -channel); +} + +void updateOAM() { + DC_FlushAll(); + dmaCopy(sprites, OAM_SUB, 128 * sizeof(SpriteEntry)); + dmaCopy(spritesMain, OAM, 128 * sizeof(SpriteEntry)); +} + +void setGameSize(int width, int height) { + gameWidth = width; + gameHeight = height; +} + +int getGameWidth() { + return gameWidth; +} + +int getGameHeight() { + return gameHeight; +} + +void initSprites() { + for(int i = 0; i < 128; i++) { + sprites[i].attribute[0] = ATTR0_DISABLED; + sprites[i].attribute[1] = 0; + sprites[i].attribute[2] = 0; + sprites[i].attribute[3] = 0; + } + + for(int i = 0; i < 128; i++) { + spritesMain[i].attribute[0] = ATTR0_DISABLED; + spritesMain[i].attribute[1] = 0; + spritesMain[i].attribute[2] = 0; + spritesMain[i].attribute[3] = 0; + } + + updateOAM(); +} + + +void saveGameBackBuffer() { +#ifdef DISABLE_SCUMM + if (savedBuffer == NULL) savedBuffer = new u8[gameWidth * gameHeight]; + for (int r = 0; r < 200; r++) { + memcpy(savedBuffer + (r * gameWidth), ((u8 *) (get8BitBackBuffer())) + (r * 512), gameWidth); + } +#endif +} + +void restoreGameBackBuffer() { +#ifdef DISABLE_SCUMM + if (savedBuffer) { + for (int r = 0; r < 200; r++) { + memcpy(((u8 *) (BG_GFX_SUB)) + (r * 512), savedBuffer + (r * gameWidth), gameWidth); + memcpy(((u8 *) (get8BitBackBuffer())) + (r * 512), savedBuffer + (r * gameWidth), gameWidth); + } + delete savedBuffer; + savedBuffer = NULL; + } +#endif + +#ifndef DISABLE_SCUMM + memset(get8BitBackBuffer(), 0, 512 * 256); + memset(BG_GFX_SUB, 0, 512 * 256); + if (Scumm::g_scumm) { + Scumm::g_scumm->markRectAsDirty(Scumm::kMainVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1); + Scumm::g_scumm->markRectAsDirty(Scumm::kTextVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1); + Scumm::g_scumm->markRectAsDirty(Scumm::kVerbVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1); + } +#endif + +} + + +void initGame() { + // This is a good time to check for left handed mode since the mode change is done as the game starts. + // There's probably a better way, but hey. +// consolePrintf("initing game\n"); + + setOptions(); + + //strcpy(gameName, ConfMan.getActiveDomain().c_str()); + strcpy(gameName, ConfMan.get("gameid").c_str()); +// consolePrintf("\n\n\n\nCurrent game: '%s' %d\n", gameName, gameName[0]); + + currentGame = &gameList[0]; // Default game + + for (int r = 0; r < NUM_SUPPORTED_GAMES; r++) { + if (!stricmp(gameName, gameList[r].gameId)) { + currentGame = &gameList[r]; +// consolePrintf("Game list num: %d\n", currentGame); + } + } + + +} + +void setLeftHanded(bool enable) { + leftHandedMode = enable; +} + +void setTouchXOffset(int x) { + touchXOffset = x; +} + +void setTouchYOffset(int y) { + touchYOffset = y; +} + +void setUnscaledMode(bool enable) { + scaledMode = !enable; +} + +void displayMode8Bit() { + + u16 buffer[32 * 32]; + + setKeyboardEnable(false); + + if (!displayModeIs8Bit) { + for (int r = 0; r < 32 * 32; r++) { + buffer[r] = ((u16 *) SCREEN_BASE_BLOCK_SUB(4))[r]; + } + } + + + + videoSetMode(MODE_5_2D | (consoleEnable? DISPLAY_BG0_ACTIVE: 0) | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); + videoSetModeSub(MODE_3_2D /*| DISPLAY_BG0_ACTIVE*/ | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); //sub bg 0 will be used to print text + + vramSetBankA(VRAM_A_MAIN_BG_0x6000000); + vramSetBankB(VRAM_B_MAIN_BG_0x6020000); + + vramSetBankC(VRAM_C_SUB_BG_0x6200000); + vramSetBankD(VRAM_D_MAIN_BG_0x6040000); + + vramSetBankH(VRAM_H_LCD); + + BG3_CR = BG_BMP8_512x256 | BG_BMP_BASE(8); + + + + BG3_XDX = (int) (((float) (gameWidth) / 256.0f) * 256); + BG3_XDY = 0; + BG3_YDX = 0; + BG3_YDY = (int) ((200.0f / 192.0f) * 256); + + SUB_BG3_CR = BG_BMP8_512x256; + + + + SUB_BG3_XDX = (int) (subScreenWidth / 256.0f * 256); + SUB_BG3_XDY = 0; + SUB_BG3_YDX = 0; + SUB_BG3_YDY = (int) (subScreenHeight / 192.0f * 256); + + + // Do text stuff + BG0_CR = BG_MAP_BASE(0) | BG_TILE_BASE(1); + BG0_Y0 = 0; + + // Restore palette entry used by text in the front-end + PALETTE_SUB[255] = savedPalEntry255; + + consoleInitDefault((u16*)SCREEN_BASE_BLOCK(0), (u16*)CHAR_BASE_BLOCK(1), 16); + consolePrintSet(0, 23); + + if (!displayModeIs8Bit) { + for (int r = 0; r < 32 * 32; r++) { + ((u16 *) SCREEN_BASE_BLOCK(0))[r] = buffer[r]; + } +// dmaCopyHalfWords(3, (u16 *) SCREEN_BASE_BLOCK(0), buffer, 32 * 32 * 2); + } + + + if (!displayModeIs8Bit) restoreGameBackBuffer(); + displayModeIs8Bit = true; + + POWER_CR &= ~POWER_SWAP_LCDS; + + keyboardEnable = false; + initGame(); + +} + +void setGameID(int id) { + gameID = id; +} + +void dummyHandler() { + REG_IF = IRQ_VBLANK; +} + +void checkSleepMode() { + if (IPC->performArm9SleepMode) { + + consolePrintf("ARM9 Entering sleep mode\n"); + + int intSave = REG_IE; + irqSet(IRQ_VBLANK, dummyHandler); +// int irqHandlerSave = (int) IRQ_HANDLER; + REG_IE = IRQ_VBLANK; + //IRQ_HANDLER = dummyHandler; + + int powerSave = POWER_CR; + POWER_CR &= ~POWER_ALL; + + while (IPC->performArm9SleepMode) { + swiWaitForVBlank(); + } + + POWER_CR = powerSave; +// IRQ_HANDLER = (void (*)()) irqHandlerSave; + irqSet(IRQ_VBLANK, VBlankHandler); + REG_IE = intSave; + + consolePrintf("ARM9 Waking from sleep mode\n"); + } +} + +void setCursorIcon(const u8* icon, uint w, uint h, byte keycolor) { + if (currentGame->control != CONT_SCUMM_SAMNMAX) + return; + + uint16 border = RGB15(24,24,24) | 0x8000; + + + int off = 48*64; + memset(SPRITE_GFX_SUB+off, 0, 64*64*2); + + int pos = 190 - (w+2); + + + + // make border + for (uint i=0; i> 1; + + for (uint y=0; yplayingSection; r < IPC->playingSection + 4; r++) { + int chunk = r & 3; + + if (IPC->fillNeeded[chunk]) { + IPC->fillNeeded[chunk] = false; + DC_FlushAll(); + soundCallback(soundParam, (byte *) (soundBuffer + ((bufferSamples >> 2) * chunk)), bufferSamples >> 1); + IPC->fillNeeded[chunk] = false; + DC_FlushAll(); + } + + } + + } +} + +void doTimerCallback() { + if (callback) { + if (callbackTimer <= 0) { + callbackTimer += callbackInterval; + callback(callbackInterval); + } + } +} + +void soundUpdate() { + if ((bufferFrame == 0)) { +// playSound(soundBuffer, (bufferSamples * 2), true); + } +// consolePrintf("%x\n", IPC->test); + + + if (bufferFrame == 0) { +// bufferFirstHalf = true; + } + if (bufferFrame == bufferSize >> 1) { + //bufferSecondHalf = true; + } + + bufferFrame++; + if (bufferFrame == bufferSize) { + bufferFrame = 0; + } +} + +void memoryReport() { + int r = 0; + int* p; + do { + p = (int *) malloc(r * 8192); + free(p); + r++; + } while ((p) && (r < 512)); + + int t = -1; + void* block[1024]; + do { + t++; + block[t] = (int *) malloc(4096); + } while ((t < 1024) && (block[t])); + + for (int q = 0; q < t; q++) { + free(block[q]); + } + + consolePrintf("Free: %dK, Largest: %dK\n", t * 4, r * 8); +} + + +void addIndyFightingKeys() { + OSystem_DS* system = OSystem_DS::instance(); + OSystem::Event event; + + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.flags = 0; + + if ((getKeysDown() & KEY_L)) { + indyFightRight = false; + } + + if ((getKeysDown() & KEY_R)) { + indyFightRight = true; + } + + if ((getKeysDown() & KEY_UP)) { + event.kbd.keycode = '8'; + event.kbd.ascii = '8'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_LEFT)) { + event.kbd.keycode = '4'; + event.kbd.ascii = '4'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_RIGHT)) { + event.kbd.keycode = '6'; + event.kbd.ascii = '6'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_DOWN)) { + event.kbd.keycode = '2'; + event.kbd.ascii = '2'; + system->addEvent(event); + } + + if (indyFightRight) { + + if ((getKeysDown() & KEY_X)) { + event.kbd.keycode = '9'; + event.kbd.ascii = '9'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_A)) { + event.kbd.keycode = '6'; + event.kbd.ascii = '6'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_B)) { + event.kbd.keycode = '3'; + event.kbd.ascii = '3'; + system->addEvent(event); + } + + } else { + + if ((getKeysDown() & KEY_X)) { + event.kbd.keycode = '7'; + event.kbd.ascii = '7'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_A)) { + event.kbd.keycode = '4'; + event.kbd.ascii = '4'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_B)) { + event.kbd.keycode = '1'; + event.kbd.ascii = '1'; + system->addEvent(event); + } + + } + + + if ((getKeysDown() & KEY_Y)) { + event.kbd.keycode = '5'; + event.kbd.ascii = '5'; + system->addEvent(event); + } +} + + +void setKeyboardEnable(bool en) { + if (en == keyboardEnable) return; + keyboardEnable = en; + u16* backupBank = (u16 *) 0x6040000; + + if (keyboardEnable) { + + + DS::drawKeyboard(1, 12, backupBank); + + + SUB_BG1_CR = BG_TILE_BASE(1) | BG_MAP_BASE(12); + + if (displayModeIs8Bit) { + SUB_DISPLAY_CR |= DISPLAY_BG1_ACTIVE; // Turn on keyboard layer + SUB_DISPLAY_CR &= ~DISPLAY_BG3_ACTIVE; // Turn off game layer + } else { + SUB_DISPLAY_CR |= DISPLAY_BG1_ACTIVE; // Turn on keyboard layer + SUB_DISPLAY_CR &= ~DISPLAY_BG0_ACTIVE; // Turn off console layer + } + lcdSwap(); + } else { + + + // Restore the palette that the keyboard has used + for (int r = 0; r < 256; r++) { + BG_PALETTE_SUB[r] = BG_PALETTE[r]; + } + + + //restoreVRAM(1, 12, backupBank); + + if (displayModeIs8Bit) { + // Copy the sub screen VRAM from the top screen - they should always be + // the same. + for (int r = 0; r < (512 * 256) >> 1; r++) { + BG_GFX_SUB[r] = get8BitBackBuffer()[r]; + } + + SUB_DISPLAY_CR &= ~DISPLAY_BG1_ACTIVE; // Turn off keyboard layer + SUB_DISPLAY_CR |= DISPLAY_BG3_ACTIVE; // Turn on game layer + } else { + SUB_DISPLAY_CR &= ~DISPLAY_BG1_ACTIVE; // Turn off keyboard layer + SUB_DISPLAY_CR |= DISPLAY_BG0_ACTIVE; // Turn on console layer + } + + lcdSwap(); + } +} + +bool getKeyboardEnable() { + return keyboardEnable; +} + +bool getIsDisplayMode8Bit() { + return displayModeIs8Bit; +} + +void addEventsToQueue() { + OSystem_DS* system = OSystem_DS::instance(); + OSystem::Event event; + + + + + if (system->isEventQueueEmpty()) { + +/* + if (getKeysDown() & KEY_L) { + tweak--; + consolePrintf("Tweak: %d\n", tweak); + IPC->tweakChanged = true; + } + + + if (getKeysDown() & KEY_R) { + tweak++; + consolePrintf("Tweak: %d\n", tweak); + IPC->tweakChanged = true; + } + */ + if ((keysHeld() & KEY_L) && (keysHeld() & KEY_R)) { + memoryReport(); + } + + if (displayModeIs8Bit) { + + if (!indyFightState) { + + + if ((getKeysDown() & KEY_B) && (!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) { + // consolePrintf("Pressing Esc"); + + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = 27; + event.kbd.ascii = 27; + event.kbd.flags = 0; + system->addEvent(event); + } + + } + + + + if ((!getIndyFightState()) && (getKeysDown() & KEY_Y)) { + consoleEnable = !consoleEnable; + if (displayModeIs8Bit) { + displayMode8Bit(); + } else { + displayMode16Bit(); + } + } + + + if (!((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) && (!getIndyFightState())) { + + if ((getKeysDown() & KEY_A) && (!indyFightState)) { + gameScreenSwap = !gameScreenSwap; + } + + if (!getPenHeld() || (mouseMode != MOUSE_HOVER)) { + if (getKeysDown() & KEY_LEFT) { + mouseMode = MOUSE_LEFT; + } + if (getKeysDown() & KEY_RIGHT) { + if (currentGame->control != CONT_SCUMM_SAMNMAX) { + mouseMode = MOUSE_RIGHT; + } else { + // If we're playing sam and max, click and release the right mouse + // button to change verb + OSystem::Event event; + + event.type = OSystem::EVENT_RBUTTONDOWN; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + + event.type = OSystem::EVENT_RBUTTONUP; + system->addEvent(event); + } + } + if (getKeysDown() & KEY_UP) { + mouseMode = MOUSE_HOVER; + } + } + + + + } + + if ((getKeysDown() & KEY_SELECT)) { + //scaledMode = !scaledMode; + //scY = 4; + showOptionsDialog(); + } + + + } + + if (!getIndyFightState() && !((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) && (getKeysDown() & KEY_X)) { + setKeyboardEnable(!keyboardEnable); + } + + updateStatus(); + + OSystem::Event event; + + if ((!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) { + event.type = OSystem::EVENT_MOUSEMOVE; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + //consolePrintf("x=%d y=%d \n", getPenX(), getPenY()); + } + + if (!keyboardEnable) { + if ((mouseMode != MOUSE_HOVER) || (!displayModeIs8Bit)) { + if (getPenDown() && (!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) { + event.type = ((mouseMode == MOUSE_LEFT) || (!displayModeIs8Bit))? OSystem::EVENT_LBUTTONDOWN: OSystem::EVENT_RBUTTONDOWN; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + } + + if (getPenReleased()) { + event.type = mouseMode == MOUSE_LEFT? OSystem::EVENT_LBUTTONUP: OSystem::EVENT_RBUTTONUP; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + } + } else { + // In hover mode, D-pad left and right click the mouse when the pen is on the screen + + if (getPenHeld()) { + if (getKeysDown() & KEY_LEFT) { + event.type = OSystem::EVENT_LBUTTONDOWN; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + } + /* if (getKeysReleased() & KEY_LEFT) { + event.type = OSystem::EVENT_LBUTTONUP; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + }*/ + + if (getKeysDown() & KEY_RIGHT) { + event.type = OSystem::EVENT_RBUTTONDOWN; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + } + /*if (getKeysReleased() & KEY_RIGHT) { + event.type = OSystem::EVENT_RBUTTONUP; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + }*/ + } + } + + if ((!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R)) && (displayModeIs8Bit)) { + // Controls specific to the control method + + + if (currentGame->control == CONT_SKY) { + // Extra controls for Benieth a Steel Sky + if ((getKeysDown() & KEY_DOWN)) { + penY = 0; + penX = 0; // Show inventory by moving mouse onto top line + } + } + + if (currentGame->control == CONT_SIMON) { + // Extra controls for Simon the Sorcerer + if ((getKeysDown() & KEY_DOWN)) { + OSystem::Event event; + + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = '#'; // F10 or # - show hotspots + event.kbd.ascii = '#'; + event.kbd.flags = 0; + system->addEvent(event); +// consolePrintf("F10\n"); + } + } + + if (currentGame->control == CONT_SCUMM_ORIGINAL) { + // Extra controls for Scumm v1-5 games + if ((getKeysDown() & KEY_DOWN)) { + OSystem::Event event; + + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = '.'; // Full stop - skips current dialogue line + event.kbd.ascii = '.'; + event.kbd.flags = 0; + system->addEvent(event); + } + + if (indyFightState) { + addIndyFightingKeys(); + } + + } + + } + } + + if (!displayModeIs8Bit) { + // Front end controls + + if (leftHandedSwap(getKeysDown()) & KEY_UP) { + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = SDLK_UP; + event.kbd.ascii = 0; + event.kbd.flags = 0; + system->addEvent(event); + + event.type = OSystem::EVENT_KEYUP; + system->addEvent(event); + } + + if (leftHandedSwap(getKeysDown()) & KEY_DOWN) { + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = SDLK_DOWN; + event.kbd.ascii = 0; + event.kbd.flags = 0; + system->addEvent(event); + + event.type = OSystem::EVENT_KEYUP; + system->addEvent(event); + } + + if (leftHandedSwap(getKeysDown()) & KEY_A) { + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = SDLK_RETURN; + event.kbd.ascii = 0; + event.kbd.flags = 0; + system->addEvent(event); + + event.type = OSystem::EVENT_KEYUP; + system->addEvent(event); + } + + } + + + if ((getKeysDown() & KEY_START)) { + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = 319; // F5 + event.kbd.ascii = 319; + event.kbd.flags = 0; + system->addEvent(event); +/* + event.type = OSystem::EVENT_KEYUP; + event.kbd.keycode = 319; // F5 + event.kbd.ascii = 319; + system->addEvent(event);*/ + +// consolePrintf("Pressing F5"); + } + + + if (keyboardEnable) { + DS::addKeyboardEvents(); + } + + consumeKeys(); + + consumePenEvents(); + + } +} + +void updateStatus() { + int offs; + + if (displayModeIs8Bit) { + switch (mouseMode) { + case MOUSE_LEFT: { + offs = 16; + break; + } + case MOUSE_RIGHT: { + offs = 32; + break; + } + case MOUSE_HOVER: { + offs = 0; + break; + } + default: { + // Nothing! + offs = 0; + break; + } + } + + + sprites[0].attribute[0] = ATTR0_BMP | 150; + sprites[0].attribute[1] = ATTR1_SIZE_32 | 208; + sprites[0].attribute[2] = ATTR2_ALPHA(1)| offs; + + if (indyFightState) { + sprites[2].attribute[0] = ATTR0_BMP | 150; + sprites[2].attribute[1] = ATTR1_SIZE_32 | (190 - 32) | (indyFightRight? 0: ATTR1_FLIP_X); + sprites[2].attribute[2] = ATTR2_ALPHA(1)| 48; + } else { + sprites[2].attribute[0] = ATTR0_DISABLED; + sprites[2].attribute[1] = 0; + sprites[2].attribute[2] = 0; + } + } else { + sprites[0].attribute[0] = ATTR0_DISABLED; + sprites[1].attribute[0] = ATTR0_DISABLED; + sprites[2].attribute[0] = ATTR0_DISABLED; + sprites[3].attribute[0] = ATTR0_DISABLED; + } + + if ((keyboardIcon) && (!keyboardEnable) && (!displayModeIs8Bit)) { + spritesMain[0].attribute[0] = ATTR0_BMP | 160; + spritesMain[0].attribute[1] = ATTR1_SIZE_32 | 0; + spritesMain[0].attribute[2] = ATTR2_ALPHA(1) | 64; + } else { + spritesMain[0].attribute[0] = ATTR0_DISABLED; + spritesMain[0].attribute[1] = 0; + spritesMain[0].attribute[2] = 0; + spritesMain[0].attribute[3] = 0; + } + +} + +void soundBufferEmptyHandler() { + REG_IF = IRQ_TIMER2; + + if (soundHiPart) { +// bufferSecondHalf = true; + } else { +// bufferFirstHalf = true; + } + + soundHiPart = !soundHiPart; +} + +void setMainScreenScroll(int x, int y) { + if (gameScreenSwap) { + SUB_BG3_CX = x + (((frameCount & 1) == 0)? 64: 0); + SUB_BG3_CY = y; + } else { + BG3_CX = x + (((frameCount & 1) == 0)? 64: 0); + BG3_CY = y; + + touchX = x >> 8; + touchY = y >> 8; + } +} + +void setMainScreenScale(int x, int y) { + if (gameScreenSwap) { + SUB_BG3_XDX = x; + SUB_BG3_XDY = 0; + SUB_BG3_YDX = 0; + SUB_BG3_YDY = y; + } else { + BG3_XDX = x; + BG3_XDY = 0; + BG3_YDX = 0; + BG3_YDY = y; + + touchScX = x; + touchScY = y; + } +} + +void setZoomedScreenScroll(int x, int y) { + if (gameScreenSwap) { + BG3_CX = x + (((frameCount & 1) == 0)? 64: 0); + BG3_CY = y; + + touchX = x >> 8; + touchY = y >> 8; + } else { + SUB_BG3_CX = x + (((frameCount & 1) == 0)? 64: 0); + SUB_BG3_CY = y; + } +} + +void setZoomedScreenScale(int x, int y) { + if (gameScreenSwap) { + BG3_XDX = x; + BG3_XDY = 0; + BG3_YDX = 0; + BG3_YDY = y; + + touchScX = x; + touchScY = y; + } else { + SUB_BG3_XDX = x; + SUB_BG3_XDY = 0; + SUB_BG3_YDX = 0; + SUB_BG3_YDY = y; + } +} + +void VBlankHandler(void) { +// BG_PALETTE[0] = RGB15(31, 31, 31); +// if (*((int *) (0x023FFF00)) != 0xBEEFCAFE) { + // consolePrintf("Guard band overwritten!"); +// } + +// consolePri ntf("X:%d Y:%d\n", getPenX(), getPenY()); + + + IPC->tweak = tweak; + soundUpdate(); + + + + + if ((!gameScreenSwap) && (!(getKeysHeld() & KEY_L) && !(getKeysHeld() & KEY_R))) { + if (currentGame) { + if (currentGame->control != CONT_SCUMM_SAMNMAX) { + if (getPenHeld() && (getPenY() < SCUMM_GAME_HEIGHT)) { + setTopScreenTarget(getPenX(), getPenY()); + } + } else { + if (getPenHeld()) { + setTopScreenTarget(getPenX(), getPenY()); + } + } + } + } + + + penUpdate(); + keysUpdate(); + + + frameCount++; + + + + if (callback) { + callbackTimer -= FRAME_TIME; + } + + if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) { + + if ((!dragging) && (getPenHeld()) && (penDownFrames > 5)) { + dragging = true; + dragStartX = penX; + dragStartY = penY; + + if (gameScreenSwap) { + dragScX = subScTargetX; + dragScY = subScTargetY; + } else { + dragScX = scX; + dragScY = scY; + } + + + } + + if ((dragging) && (!getPenHeld())) { + dragging = false; + } + + if (dragging) { + + if (gameScreenSwap) { + subScTargetX = dragScX + ((dragStartX - penX) << 8); + subScTargetY = dragScY + ((dragStartY - penY) << 8); + } else { + scX = dragScX + ((dragStartX - penX)); + scY = dragScY + ((dragStartY - penY)); + } + +// consolePrintf("X:%d Y:%d\n", dragStartX - penX, dragStartY - penY); + } + } + + +/* if ((frameCount & 1) == 0) { + SUB_BG3_CX = subScX; + } else { + SUB_BG3_CX = subScX + 64; + } + + SUB_BG3_CY = subScY + (shakePos << 8);*/ + + /*SUB_BG3_XDX = (int) (subScreenWidth / 256.0f * 256); + SUB_BG3_XDY = 0; + SUB_BG3_YDX = 0; + SUB_BG3_YDY = (int) (subScreenHeight / 192.0f * 256);*/ + + + if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) { + if ((getKeysHeld() & KEY_A) && (subScreenScale < 256)) { + subScreenScale += 3; + } + + if ((getKeysHeld() & KEY_B) && (subScreenScale > 128)) { + subScreenScale -=3; + } + + int xCenter = subScTargetX + ((subScreenWidth >> 1) << 8); + int yCenter = subScTargetY + ((subScreenHeight >> 1) << 8); + + subScreenWidth = SCUMM_GAME_WIDTH * subScreenScale >> 8; + subScreenHeight = SCUMM_GAME_HEIGHT * subScreenScale >> 8; + + subScTargetX = xCenter - ((subScreenWidth >> 1) << 8); + subScTargetY = yCenter - ((subScreenHeight >> 1) << 8); + + + + + if (subScTargetX < 0) subScTargetX = 0; + if (subScTargetX > (gameWidth - subScreenWidth) << 8) subScTargetX = (gameWidth - subScreenWidth) << 8; + + if (subScTargetY < 0) subScTargetY = 0; + if (subScTargetY > (gameHeight - subScreenHeight) << 8) subScTargetY = (gameHeight - subScreenHeight) << 8; + } + + subScX += (subScTargetX - subScX) >> 2; + subScY += (subScTargetY - subScY) >> 2; + + if (displayModeIs8Bit) { + + if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) { + + int offsX = 0, offsY = 0; + + + if (getKeysHeld() & KEY_LEFT) { + offsX -= 1; + } + + if (getKeysHeld() & KEY_RIGHT) { + offsX += 1; + } + + if (getKeysHeld() & KEY_UP) { + offsY -= 1; + } + + if (getKeysHeld() & KEY_DOWN) { + offsY += 1; + } + + if (((gameScreenSwap) && (getKeysHeld() & KEY_L)) || ((!gameScreenSwap) && (getKeysHeld() & KEY_R))) { + subScTargetX += offsX << 8; + subScTargetY += offsY << 8; + } else { + scX += offsX; + scY += offsY; + } + } + + if (!scaledMode) { + + if (scX + 256 > gameWidth - 1) { + scX = gameWidth - 1 - 256; + } + + if (scX < 0) { + scX = 0; + } + + if (scY + 192 > gameHeight - 1) { + scY = gameHeight - 1 - 192; + } + + if (scY < 0) { + scY = 0; + } + + setZoomedScreenScroll(subScX, subScY); + setZoomedScreenScale(subScreenWidth, (subScreenHeight * 256) / 192); + + + setMainScreenScroll(scX << 8, (scY << 8) + (shakePos << 8)); + setMainScreenScale(256, 256); // 1:1 scale + + } else { + + if (scY > gameHeight - 192 - 1) { + scY = gameHeight - 192 - 1; + } + + if (scY < 0) { + scY = 0; + } + + setZoomedScreenScroll(subScX, subScY); + setZoomedScreenScale(subScreenWidth, (subScreenHeight * 256) / 192); + + setMainScreenScroll(64, (scY << 8) + (shakePos << 8)); + setMainScreenScale(320, 256); // 1:1 scale + + } + } else { + setZoomedScreenScroll(0, 0); + setZoomedScreenScale(320, 256); + + setMainScreenScroll(0, 0); + setMainScreenScale(320, 256); // 1:1 scale + } + + // Enable on screen keyboard when pen taps icon + if ((keyboardIcon) && (penX < 32) && (penY > 160) && (penHeld)) { + setKeyboardEnable(true); + } + + if (keyboardEnable) { + if (DS::getKeyboardClosed()) { + setKeyboardEnable(false); + } + } + + updateOAM(); + + //PALETTE[0] = RGB15(0, 0, 0); + REG_IF = IRQ_VBLANK; +} + +int getMillis() { + return currentTimeMillis; +// return frameCount * FRAME_TIME; +} + +void setTimerCallback(OSystem::TimerProc proc, int interval) { +// consolePrintf("Set timer proc %x, int %d\n", proc, interval); + callback = proc; + callbackInterval = interval; + callbackTimer = interval; +} + +void timerTickHandler() { + REG_IF = IRQ_TIMER0; + if ((callback) && (callbackTimer > 0)) { + callbackTimer--; + } + currentTimeMillis++; +} + +void setTalkPos(int x, int y) { +// if (gameID != Scumm::GID_SAMNMAX) { +// setTopScreenTarget(x, 0); +// } else { + setTopScreenTarget(x, y); +// } +} + +void setTopScreenTarget(int x, int y) { + subScTargetX = (x - (subScreenWidth >> 1)); + subScTargetY = (y - (subScreenHeight >> 1)); + + if (subScTargetX < 0) subScTargetX = 0; + if (subScTargetX > gameWidth - subScreenWidth) subScTargetX = gameWidth - subScreenWidth; + + if (subScTargetY < 0) subScTargetY = 0; + if (subScTargetY > gameHeight - subScreenHeight) subScTargetY = gameHeight - subScreenHeight; + + subScTargetX <<=8; + subScTargetY <<=8; +} + +void initHardware() { + // Guard band +//((int *) (0x023FFF00)) = 0xBEEFCAFE; + + + penInit(); + + powerON(POWER_ALL); +/* vramSetBankA(VRAM_A_MAIN_BG); + vramSetBankB(VRAM_B_MAIN_BG); + vramSetBankC(VRAM_C_SUB_BG); */ + vramSetBankI(VRAM_I_SUB_SPRITE); + vramSetBankG(VRAM_G_MAIN_SPRITE); + + currentTimeMillis = 0; + + +/* + // Set up a millisecond counter + TIMER0_CR = 0; + TIMER0_DATA = 0xFFFF; + TIMER0_CR = TIMER_ENABLE | TIMER_CASCADE; +*/ + + + + PALETTE[255] = RGB15(0,31,0); + + // Allocate save buffer for game screen +// savedBuffer = new u8[320 * 200]; + displayMode16Bit(); + + memset(BG_GFX, 0, 512 * 256 * 2); + scaledMode = true; + scX = 0; + scY = 0; + subScX = 0; + subScY = 0; + subScTargetX = 0; + subScTargetY = 0; + + //lcdSwap(); + POWER_CR &= ~POWER_SWAP_LCDS; + + frameCount = 0; + callback = NULL; + +// vramSetBankH(VRAM_H_SUB_BG); + + +// // Do text stuff + //BG0_CR = BG_MAP_BASE(0) | BG_TILE_BASE(1); +// BG0_Y0 = 48; + + PALETTE[255] = RGB15(31,31,31);//by default font will be rendered with color 255 + + //consoleInit() is a lot more flexible but this gets you up and running quick +// consoleInitDefault((u16*)SCREEN_BASE_BLOCK(0), (u16*)CHAR_BASE_BLOCK(1), 16); + //consolePrintSet(0, 6); + + //irqs are nice + irqInit(); +// irqInitHandler(); + irqSet(IRQ_VBLANK, VBlankHandler); + irqSet(IRQ_TIMER0, timerTickHandler); + irqSet(IRQ_TIMER2, soundBufferEmptyHandler); + + irqEnable(IRQ_VBLANK); + irqEnable(IRQ_TIMER0); + irqEnable(IRQ_TIMER2); + + + // Set up a millisecond timer + TIMER0_CR = 0; + TIMER0_DATA = (u32) TIMER_FREQ(1000); + TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1 | TIMER_IRQ_REQ; + REG_IME = 1; + + PALETTE[255] = RGB15(0,0,31); + + initSprites(); + +// videoSetModeSub(MODE_3_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); //sub bg 0 will be used to print text + + // Convert texture from 24bit 888 to 16bit 1555, remembering to set top bit! + u8* srcTex = (u8 *) icons_raw; + for (int r = 32 * 160 ; r >= 0; r--) { + SPRITE_GFX_SUB[r] = 0x8000 | (srcTex[r * 3] >> 3) | ((srcTex[r * 3 + 1] >> 3) << 5) | ((srcTex[r * 3 + 2] >> 3) << 10); + SPRITE_GFX[r] = 0x8000 | (srcTex[r * 3] >> 3) | ((srcTex[r * 3 + 1] >> 3) << 5) | ((srcTex[r * 3 + 2] >> 3) << 10); + } + + WAIT_CR &= ~(0x0080); + REG_WRAM_CNT = 0; + +} + + +void setKeyboardIcon(bool enable) { + keyboardIcon = enable; +} + +bool getKeyboardIcon() { + return keyboardIcon; +} + + +//////////////////// +// Pen stuff +//////////////////// + + +void penInit() { + penDown = false; + penHeld = false; + penReleased = false; + penDownLastFrame = false; + penDownSaved = false; + penReleasedSaved = false; + penDownFrames = 0; + consumeKeys(); +} + +void penUpdate() { + +// if (getKeysHeld() & KEY_L) consolePrintf("%d, %d penX=%d, penY=%d tz=%d\n", IPC->touchXpx, IPC->touchYpx, penX, penY, IPC->touchZ1); + + if ((penDownFrames > 1)) { // Is this right? Dunno, but it works for me. + + if ((penHeld)) { + penHeld = true; + penDown = false; + + if ((IPC->touchZ1 > 0) && (IPC->touchXpx > 0) && (IPC->touchYpx > 0)) { + penX = IPC->touchXpx + touchXOffset; + penY = IPC->touchYpx + touchYOffset; + } + + } else { + penDown = true; + penHeld = true; + penDownSaved = true; + + //if ( (ABS(penX - IPC->touchXpx) < 10) && (ABS(penY - IPC->touchYpx) < 10) ) { + if ((IPC->touchZ1 > 0) && (IPC->touchXpx > 0) && (IPC->touchYpx > 0)) { + penX = IPC->touchXpx; + penY = IPC->touchYpx; + } + //} + } + + } else { + if (penHeld) { + penReleased = true; + penReleasedSaved = true; + } else { + penReleased = false; + } + + penDown = false; + penHeld = false; + } + + + if ((IPC->touchZ1 > 0) || ((penDownFrames == 2)) ) { + penDownLastFrame = true; + penDownFrames++; + } else { + penDownLastFrame = false; + penDownFrames = 0; + } + +} + +int leftHandedSwap(int keys) { + // Start and select are unchanged + if (leftHandedMode) { + int result = keys & (~(KEY_R | KEY_L | KEY_Y | KEY_A | KEY_B | KEY_X | KEY_LEFT | KEY_RIGHT | KEY_UP | KEY_DOWN)); + + if (keys & KEY_L) result |= KEY_R; + if (keys & KEY_R) result |= KEY_L; + + if (keys & KEY_LEFT) result |= KEY_Y; + if (keys & KEY_RIGHT) result |= KEY_A; + if (keys & KEY_DOWN) result |= KEY_B; + if (keys & KEY_UP) result |= KEY_X; + + if (keys & KEY_Y) result |= KEY_LEFT; + if (keys & KEY_A) result |= KEY_RIGHT; + if (keys & KEY_B) result |= KEY_DOWN; + if (keys & KEY_X) result |= KEY_UP; + + return result; + } else { + return keys; + } +} + +void keysUpdate() { + scanKeys(); + keysDownSaved |= leftHandedSwap(keysDown()); + keysReleasedSaved |= leftHandedSwap(keysUp()); +} + +int getKeysDown() { + return keysDownSaved; +} + +int getKeysHeld() { + return leftHandedSwap(keysHeld()); +} + +int getKeysReleased() { + return keysReleasedSaved; +} + +void consumeKeys() { + keysDownSaved = 0; + keysReleasedSaved = 0; +} + +bool getPenDown() { + return penDownSaved; +} + +bool getPenHeld() { + return penHeld; +} + +bool getPenReleased() { + return penReleasedSaved; +} + +void consumePenEvents() { + penDownSaved = false; + penReleasedSaved = false; +} + +int getPenX() { + int x = ((penX * touchScX) >> 8) + touchX; + x = x < 0? 0: (x > gameWidth - 1? gameWidth - 1: x); + return x; +} + +int getPenY() { + int y = ((penY * touchScY) >> 8) + touchY; + y = y < 0? 0: (y > gameHeight - 1? gameHeight - 1: y); + return y; +} + +GLvector getPenPos() { + GLvector v; + + v.x = (penX * inttof32(1)) / SCREEN_WIDTH; + v.y = (penY * inttof32(1)) / SCREEN_HEIGHT; + + return v; +} + +void formatSramOption() { + consolePrintf("The following files are present in save RAM:\n"); + DSSaveFileManager::instance()->listFiles(); + + consolePrintf("\nAre you sure you want to\n"); + consolePrintf("DELETE all files?\n"); + consolePrintf("A = Yes, X = No\n"); + + while (true) { + if (keysHeld() & KEY_A) { + DSSaveFileManager::instance()->formatSram(); + consolePrintf("SRAM cleared!\n"); + return; + } + + if (keysHeld() & KEY_X) { + consolePrintf("Whew, that was close!\n"); + return; + } + } +} + + +void setIndyFightState(bool st) { + indyFightState = st; + indyFightRight = true; +} + +bool getIndyFightState() { + return indyFightState; +} + +///////////////// +// GBAMP +///////////////// + +bool GBAMPAvail = false; + +void initGBAMP() { + FAT_InitFiles(); + if (disc_IsInserted()) { + GBAMPAvail = true; + consolePrintf("Found flash card reader!\n"); + } else { + GBAMPAvail = false; + consolePrintf("Flash card reader not found!\n"); + } +} + +bool isGBAMPAvailable() { + return GBAMPAvail; +} + + + +///////////////// +// Main +///////////////// + +static const Common::String test("poo", 1, 16); + + + +int main(void) +{ + soundCallback = NULL; + + + initHardware(); + + // Let arm9 read cartridge + *((u16 *) (0x04000204)) &= ~0x0080; + + lastCallbackFrame = 0; + tweak = 0; + + indyFightState = false; + indyFightRight = true; + + // CPU speed = 67108864 + // 8 frames = 2946 368.5 bytes per fr + +// playSound(stretch, 47619, false); +// playSound(twang, 11010, true); // 18640 + +// bufferSize = 10; + bufferRate = 22050; + bufferFrame = 0; +// bufferSamples = (bufferRate * bufferSize) / 60; + bufferSamples = 4096; + + bufferFirstHalf = false; + bufferSecondHalf = true; + + lastEventFrame = 0; + mouseMode = MOUSE_LEFT; + + + + + int bytes = (2 * (bufferSamples)) + 100; + + soundBuffer = (s16 *) malloc(bytes * 2); + + + soundHiPart = true; +/* + TIMER1_CR = 0; + TIMER1_DATA = TIMER_FREQ(bufferRate); + TIMER1_CR = TIMER_ENABLE | TIMER_DIV_1; + + TIMER2_CR = 0; + TIMER2_DATA = 0xFFFF - (bufferSamples / 2); + TIMER2_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE; + */ + // 2945 - 2947 + + + +// for (int r = 2946; r < 3000; r++) { +// soundBuffer[r] = 30000; +// } + + + + consolePrintf("------------------------\n"); + consolePrintf("ScummVM DS\n"); + consolePrintf("Ported by Neil Millstone\n"); +#ifdef DS_SCUMM_BUILD + consolePrintf("Version 0.61 build A\n"); +#else + consolePrintf("Version 0.61 build B\n"); +#endif + consolePrintf("------------------------\n"); + consolePrintf("L/R + D-pad/pen: Scroll view\n"); + consolePrintf("D-pad left: Left mouse button\n"); + consolePrintf("D-pad right: Right mouse button\n"); + consolePrintf("D-pad up: Hover mouse\n"); + consolePrintf("D-pad down: Skip dialog line\n"); + consolePrintf("B button: Skip cutscenes\n"); + consolePrintf("Select: DS Options menu\n"); + consolePrintf("Start: Game menu\n"); + consolePrintf("Y (in game): Toggle console\n"); + consolePrintf("X: Toggle keyboard\n"); + consolePrintf("A: Swap screens\n"); + consolePrintf("L + R on bootup: Clear SRAM\n\n"); + consolePrintf("For a complete poo list see the\n"); + consolePrintf("help screen.\npoo\n"); + + + for (int r = 0; r < bytes; r++) { + soundBuffer[r] = 0; + } + + consolePrintf("length=%d str='%s'\n", test.size(), test.c_str()); + + swiWaitForVBlank(); + swiWaitForVBlank(); + playSound(soundBuffer, (bufferSamples * 2), true); + swiWaitForVBlank(); + swiWaitForVBlank(); + swiWaitForVBlank(); + + + + // Create a file system node to force search for a zip file in GBA rom space + DSFileSystemNode* node = new DSFileSystemNode(); + if (!node->getZip() || (!node->getZip()->isReady())) { + // If not found, init CF/SD driver + initGBAMP(); + } + delete node; + + + + updateStatus(); + + +// OSystem_DS::instance(); + g_system = new OSystem_DS(); + assert(g_system); + + if ((keysHeld() & KEY_L) && (keysHeld() & KEY_R)) { + formatSramOption(); + } + +// printf("'%s'", Common::ConfigManager::kTransientDomain.c_str()); + //printf("'%s'", Common::ConfigManager::kApplicationDomain.c_str()); + + + char* argv[2] = {"/scummvmds", "--config=scummvmb.ini"}; +#ifdef DS_NON_SCUMM_BUILD + + while (1) { + scummvm_main(2, (char **) &argv); + } +#else + while (1) { + scummvm_main(1, (char **) &argv); + } +#endif + + + return 0; +} + +} + +int main() { + DS::main(); +} +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include + +#include //basic print funcionality + +#include +#include "dsmain.h" +#include "string.h" +#include "system.h" +#include "osystem_ds.h" +#include "icons_raw.h" +#include "gba_nds_fat.h" +#include "disc_io.h" +#include "config-manager.h" +#include "engines/scumm/scumm.h" +#include "keyboard_raw.h" +#include "keyboard_pal_raw.h" +#define V16(a, b) ((a << 12) | b) +#include "touchkeyboard.h" +#include "registers_alt.h" +//#include "compact_flash.h" +#include "dsoptions.h" + +namespace DS { + +// From console.c in NDSLib + +//location of cursor +extern u8 row; +extern u8 col; + +// Mouse mode +enum MouseMode { + MOUSE_LEFT, MOUSE_RIGHT, MOUSE_HOVER, MOUSE_NUM_MODES +}; + +// Defines +#define FRAME_TIME 17 +#define SCUMM_GAME_HEIGHT 142 +#define SCUMM_GAME_WIDTH 232 + +int textureID; +u16* texture; + +int frameCount; +int currentTimeMillis; + +// Timer Callback +int callbackInterval; +int callbackTimer; +OSystem::TimerProc callback; + +// Scaled +bool scaledMode; +int scX; +int scY; + +int subScX; +int subScY; +int subScTargetX; +int subScTargetY; +int subScreenWidth = SCUMM_GAME_WIDTH; +int subScreenHeight = SCUMM_GAME_HEIGHT; +int subScreenScale = 256; + + + +// Sound +int bufferSize; +s16* soundBuffer; +int bufferFrame; +int bufferRate; +int bufferSamples; +bool soundHiPart; + +// Events +int lastEventFrame; +bool indyFightState; +bool indyFightRight; + +OSystem::SoundProc soundCallback; +void* soundParam; +int lastCallbackFrame; +bool bufferFirstHalf; +bool bufferSecondHalf; + +// Saved buffers +u8* savedBuffer = NULL; +bool highBuffer; +bool displayModeIs8Bit; + +// Game id +u8 gameID; + +bool consoleEnable = true; +bool gameScreenSwap = false; + +MouseMode mouseMode; + +// Sprites +SpriteEntry sprites[128]; +SpriteEntry spritesMain[128]; +int tweak; + +// Shake +int shakePos = 0; + +// Keyboard +bool keyboardEnable = false; +bool leftHandedMode = false; +bool keyboardIcon = false; + +// Touch +int touchScX, touchScY, touchX, touchY; + +// Dragging +int dragStartX, dragStartY; +bool dragging = false; +int dragScX, dragScY; + +// Interface styles +char gameName[32]; + +// 8-bit surface size +int gameWidth = 320; +int gameHeight = 200; + +enum controlType { + CONT_SCUMM_ORIGINAL, + CONT_SCUMM_SAMNMAX, + CONT_SKY, + CONT_SIMON, +}; + +struct gameListType { + char gameId[16]; + controlType control; +}; + +#define NUM_SUPPORTED_GAMES 15 + +gameListType gameList[NUM_SUPPORTED_GAMES] = { + // Unknown game - use normal SCUMM controls + {"unknown", CONT_SCUMM_ORIGINAL}, + + // SCUMM games + {"maniac", CONT_SCUMM_ORIGINAL}, + {"zak", CONT_SCUMM_ORIGINAL}, + {"loom", CONT_SCUMM_ORIGINAL}, + {"indy3", CONT_SCUMM_ORIGINAL}, + {"atlantis", CONT_SCUMM_ORIGINAL}, + {"monkey", CONT_SCUMM_ORIGINAL}, + {"monkey2", CONT_SCUMM_ORIGINAL}, + {"tentacle", CONT_SCUMM_ORIGINAL}, + {"samnmax", CONT_SCUMM_SAMNMAX}, + + // Non-SCUMM games + {"sky", CONT_SKY}, + {"simon1", CONT_SIMON}, + {"simon2", CONT_SIMON}, + {"gob1", CONT_SCUMM_ORIGINAL}, + {"queen", CONT_SCUMM_ORIGINAL} +}; + +gameListType* currentGame = NULL; + +// Stylus +#define ABS(x) ((x)>0?(x):-(x)) + +bool penDown; +bool penHeld; +bool penReleased; +bool penDownLastFrame; +f32 penX, penY; +int keysDownSaved; +int keysReleasedSaved; + +bool penDownSaved; +bool penReleasedSaved; +int penDownFrames; +int touchXOffset = 0; +int touchYOffset = 0; + +u16 savedPalEntry255 = RGB15(31, 31, 31); + + +extern "C" int scummvm_main(int argc, char *argv[]); +void updateStatus(); + +TransferSound soundControl; + +//plays an 8 bit mono sample at 11025Hz +void playSound(const void* data, u32 length, bool loop, bool adpcm, int rate) +{ + + if (!IPC->soundData) { + soundControl.count = 0; + } + + soundControl.data[soundControl.count].data = data; + soundControl.data[soundControl.count].len = length | (loop? 0x80000000: 0x00000000); + soundControl.data[soundControl.count].rate = rate; // 367 samples per frame + soundControl.data[soundControl.count].pan = 64; + soundControl.data[soundControl.count].vol = 127; + soundControl.data[soundControl.count].format = adpcm? 2: 0; + + soundControl.count++; + + DC_FlushAll(); + IPC->soundData = &soundControl; +} + +void stopSound(int channel) { + playSound(NULL, 0, false, false, -channel); +} + +void updateOAM() { + DC_FlushAll(); + dmaCopy(sprites, OAM_SUB, 128 * sizeof(SpriteEntry)); + dmaCopy(spritesMain, OAM, 128 * sizeof(SpriteEntry)); +} + +void setGameSize(int width, int height) { + gameWidth = width; + gameHeight = height; +} + +int getGameWidth() { + return gameWidth; +} + +int getGameHeight() { + return gameHeight; +} + +void initSprites() { + for(int i = 0; i < 128; i++) { + sprites[i].attribute[0] = ATTR0_DISABLED; + sprites[i].attribute[1] = 0; + sprites[i].attribute[2] = 0; + sprites[i].attribute[3] = 0; + } + + for(int i = 0; i < 128; i++) { + spritesMain[i].attribute[0] = ATTR0_DISABLED; + spritesMain[i].attribute[1] = 0; + spritesMain[i].attribute[2] = 0; + spritesMain[i].attribute[3] = 0; + } + + updateOAM(); +} + + +void saveGameBackBuffer() { +#ifdef DISABLE_SCUMM + if (savedBuffer == NULL) savedBuffer = new u8[gameWidth * gameHeight]; + for (int r = 0; r < 200; r++) { + memcpy(savedBuffer + (r * gameWidth), ((u8 *) (get8BitBackBuffer())) + (r * 512), gameWidth); + } +#endif +} + +void restoreGameBackBuffer() { +#ifdef DISABLE_SCUMM + if (savedBuffer) { + for (int r = 0; r < 200; r++) { + memcpy(((u8 *) (BG_GFX_SUB)) + (r * 512), savedBuffer + (r * gameWidth), gameWidth); + memcpy(((u8 *) (get8BitBackBuffer())) + (r * 512), savedBuffer + (r * gameWidth), gameWidth); + } + delete savedBuffer; + savedBuffer = NULL; + } +#endif + +#ifndef DISABLE_SCUMM + memset(get8BitBackBuffer(), 0, 512 * 256); + memset(BG_GFX_SUB, 0, 512 * 256); + if (Scumm::g_scumm) { + Scumm::g_scumm->markRectAsDirty(Scumm::kMainVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1); + Scumm::g_scumm->markRectAsDirty(Scumm::kTextVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1); + Scumm::g_scumm->markRectAsDirty(Scumm::kVerbVirtScreen, 0, gameWidth - 1, 0, gameHeight - 1, 1); + } +#endif + +} + + +void initGame() { + // This is a good time to check for left handed mode since the mode change is done as the game starts. + // There's probably a better way, but hey. +// consolePrintf("initing game\n"); + + setOptions(); + + //strcpy(gameName, ConfMan.getActiveDomain().c_str()); + strcpy(gameName, ConfMan.get("gameid").c_str()); +// consolePrintf("\n\n\n\nCurrent game: '%s' %d\n", gameName, gameName[0]); + + currentGame = &gameList[0]; // Default game + + for (int r = 0; r < NUM_SUPPORTED_GAMES; r++) { + if (!stricmp(gameName, gameList[r].gameId)) { + currentGame = &gameList[r]; +// consolePrintf("Game list num: %d\n", currentGame); + } + } + + +} + +void setLeftHanded(bool enable) { + leftHandedMode = enable; +} + +void setTouchXOffset(int x) { + touchXOffset = x; +} + +void setTouchYOffset(int y) { + touchYOffset = y; +} + +void setUnscaledMode(bool enable) { + scaledMode = !enable; +} + +void displayMode8Bit() { + + u16 buffer[32 * 32]; + + setKeyboardEnable(false); + + if (!displayModeIs8Bit) { + for (int r = 0; r < 32 * 32; r++) { + buffer[r] = ((u16 *) SCREEN_BASE_BLOCK_SUB(4))[r]; + } + } + + + + videoSetMode(MODE_5_2D | (consoleEnable? DISPLAY_BG0_ACTIVE: 0) | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); + videoSetModeSub(MODE_3_2D /*| DISPLAY_BG0_ACTIVE*/ | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); //sub bg 0 will be used to print text + + vramSetBankA(VRAM_A_MAIN_BG_0x6000000); + vramSetBankB(VRAM_B_MAIN_BG_0x6020000); + + vramSetBankC(VRAM_C_SUB_BG_0x6200000); + vramSetBankD(VRAM_D_MAIN_BG_0x6040000); + + vramSetBankH(VRAM_H_LCD); + + BG3_CR = BG_BMP8_512x256 | BG_BMP_BASE(8); + + + + BG3_XDX = (int) (((float) (gameWidth) / 256.0f) * 256); + BG3_XDY = 0; + BG3_YDX = 0; + BG3_YDY = (int) ((200.0f / 192.0f) * 256); + + SUB_BG3_CR = BG_BMP8_512x256; + + + + SUB_BG3_XDX = (int) (subScreenWidth / 256.0f * 256); + SUB_BG3_XDY = 0; + SUB_BG3_YDX = 0; + SUB_BG3_YDY = (int) (subScreenHeight / 192.0f * 256); + + + // Do text stuff + BG0_CR = BG_MAP_BASE(0) | BG_TILE_BASE(1); + BG0_Y0 = 0; + + // Restore palette entry used by text in the front-end + PALETTE_SUB[255] = savedPalEntry255; + + consoleInitDefault((u16*)SCREEN_BASE_BLOCK(0), (u16*)CHAR_BASE_BLOCK(1), 16); + consolePrintSet(0, 23); + + if (!displayModeIs8Bit) { + for (int r = 0; r < 32 * 32; r++) { + ((u16 *) SCREEN_BASE_BLOCK(0))[r] = buffer[r]; + } +// dmaCopyHalfWords(3, (u16 *) SCREEN_BASE_BLOCK(0), buffer, 32 * 32 * 2); + } + + + if (!displayModeIs8Bit) restoreGameBackBuffer(); + displayModeIs8Bit = true; + + POWER_CR &= ~POWER_SWAP_LCDS; + + keyboardEnable = false; + initGame(); + +} + +void setGameID(int id) { + gameID = id; +} + +void dummyHandler() { + REG_IF = IRQ_VBLANK; +} + +void checkSleepMode() { + if (IPC->performArm9SleepMode) { + + consolePrintf("ARM9 Entering sleep mode\n"); + + int intSave = REG_IE; + irqSet(IRQ_VBLANK, dummyHandler); +// int irqHandlerSave = (int) IRQ_HANDLER; + REG_IE = IRQ_VBLANK; + //IRQ_HANDLER = dummyHandler; + + int powerSave = POWER_CR; + POWER_CR &= ~POWER_ALL; + + while (IPC->performArm9SleepMode) { + swiWaitForVBlank(); + } + + POWER_CR = powerSave; +// IRQ_HANDLER = (void (*)()) irqHandlerSave; + irqSet(IRQ_VBLANK, VBlankHandler); + REG_IE = intSave; + + consolePrintf("ARM9 Waking from sleep mode\n"); + } +} + +void setCursorIcon(const u8* icon, uint w, uint h, byte keycolor) { + if (currentGame->control != CONT_SCUMM_SAMNMAX) + return; + + uint16 border = RGB15(24,24,24) | 0x8000; + + + int off = 48*64; + memset(SPRITE_GFX_SUB+off, 0, 64*64*2); + + int pos = 190 - (w+2); + + + + // make border + for (uint i=0; i> 1; + + for (uint y=0; yplayingSection; r < IPC->playingSection + 4; r++) { + int chunk = r & 3; + + if (IPC->fillNeeded[chunk]) { + IPC->fillNeeded[chunk] = false; + DC_FlushAll(); + soundCallback(soundParam, (byte *) (soundBuffer + ((bufferSamples >> 2) * chunk)), bufferSamples >> 1); + IPC->fillNeeded[chunk] = false; + DC_FlushAll(); + } + + } + + } +} + +void doTimerCallback() { + if (callback) { + if (callbackTimer <= 0) { + callbackTimer += callbackInterval; + callback(callbackInterval); + } + } +} + +void soundUpdate() { + if ((bufferFrame == 0)) { +// playSound(soundBuffer, (bufferSamples * 2), true); + } +// consolePrintf("%x\n", IPC->test); + + + if (bufferFrame == 0) { +// bufferFirstHalf = true; + } + if (bufferFrame == bufferSize >> 1) { + //bufferSecondHalf = true; + } + + bufferFrame++; + if (bufferFrame == bufferSize) { + bufferFrame = 0; + } +} + +void memoryReport() { + int r = 0; + int* p; + do { + p = (int *) malloc(r * 8192); + free(p); + r++; + } while ((p) && (r < 512)); + + int t = -1; + void* block[1024]; + do { + t++; + block[t] = (int *) malloc(4096); + } while ((t < 1024) && (block[t])); + + for (int q = 0; q < t; q++) { + free(block[q]); + } + + consolePrintf("Free: %dK, Largest: %dK\n", t * 4, r * 8); +} + + +void addIndyFightingKeys() { + OSystem_DS* system = OSystem_DS::instance(); + OSystem::Event event; + + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.flags = 0; + + if ((getKeysDown() & KEY_L)) { + indyFightRight = false; + } + + if ((getKeysDown() & KEY_R)) { + indyFightRight = true; + } + + if ((getKeysDown() & KEY_UP)) { + event.kbd.keycode = '8'; + event.kbd.ascii = '8'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_LEFT)) { + event.kbd.keycode = '4'; + event.kbd.ascii = '4'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_RIGHT)) { + event.kbd.keycode = '6'; + event.kbd.ascii = '6'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_DOWN)) { + event.kbd.keycode = '2'; + event.kbd.ascii = '2'; + system->addEvent(event); + } + + if (indyFightRight) { + + if ((getKeysDown() & KEY_X)) { + event.kbd.keycode = '9'; + event.kbd.ascii = '9'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_A)) { + event.kbd.keycode = '6'; + event.kbd.ascii = '6'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_B)) { + event.kbd.keycode = '3'; + event.kbd.ascii = '3'; + system->addEvent(event); + } + + } else { + + if ((getKeysDown() & KEY_X)) { + event.kbd.keycode = '7'; + event.kbd.ascii = '7'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_A)) { + event.kbd.keycode = '4'; + event.kbd.ascii = '4'; + system->addEvent(event); + } + if ((getKeysDown() & KEY_B)) { + event.kbd.keycode = '1'; + event.kbd.ascii = '1'; + system->addEvent(event); + } + + } + + + if ((getKeysDown() & KEY_Y)) { + event.kbd.keycode = '5'; + event.kbd.ascii = '5'; + system->addEvent(event); + } +} + + +void setKeyboardEnable(bool en) { + if (en == keyboardEnable) return; + keyboardEnable = en; + u16* backupBank = (u16 *) 0x6040000; + + if (keyboardEnable) { + + + DS::drawKeyboard(1, 12, backupBank); + + + SUB_BG1_CR = BG_TILE_BASE(1) | BG_MAP_BASE(12); + + if (displayModeIs8Bit) { + SUB_DISPLAY_CR |= DISPLAY_BG1_ACTIVE; // Turn on keyboard layer + SUB_DISPLAY_CR &= ~DISPLAY_BG3_ACTIVE; // Turn off game layer + } else { + SUB_DISPLAY_CR |= DISPLAY_BG1_ACTIVE; // Turn on keyboard layer + SUB_DISPLAY_CR &= ~DISPLAY_BG0_ACTIVE; // Turn off console layer + } + lcdSwap(); + } else { + + + // Restore the palette that the keyboard has used + for (int r = 0; r < 256; r++) { + BG_PALETTE_SUB[r] = BG_PALETTE[r]; + } + + + //restoreVRAM(1, 12, backupBank); + + if (displayModeIs8Bit) { + // Copy the sub screen VRAM from the top screen - they should always be + // the same. + for (int r = 0; r < (512 * 256) >> 1; r++) { + BG_GFX_SUB[r] = get8BitBackBuffer()[r]; + } + + SUB_DISPLAY_CR &= ~DISPLAY_BG1_ACTIVE; // Turn off keyboard layer + SUB_DISPLAY_CR |= DISPLAY_BG3_ACTIVE; // Turn on game layer + } else { + SUB_DISPLAY_CR &= ~DISPLAY_BG1_ACTIVE; // Turn off keyboard layer + SUB_DISPLAY_CR |= DISPLAY_BG0_ACTIVE; // Turn on console layer + } + + lcdSwap(); + } +} + +bool getKeyboardEnable() { + return keyboardEnable; +} + +bool getIsDisplayMode8Bit() { + return displayModeIs8Bit; +} + +void addEventsToQueue() { + OSystem_DS* system = OSystem_DS::instance(); + OSystem::Event event; + + + + + if (system->isEventQueueEmpty()) { + +/* + if (getKeysDown() & KEY_L) { + tweak--; + consolePrintf("Tweak: %d\n", tweak); + IPC->tweakChanged = true; + } + + + if (getKeysDown() & KEY_R) { + tweak++; + consolePrintf("Tweak: %d\n", tweak); + IPC->tweakChanged = true; + } + */ + if ((keysHeld() & KEY_L) && (keysHeld() & KEY_R)) { + memoryReport(); + } + + if (displayModeIs8Bit) { + + if (!indyFightState) { + + + if ((getKeysDown() & KEY_B) && (!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) { + // consolePrintf("Pressing Esc"); + + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = 27; + event.kbd.ascii = 27; + event.kbd.flags = 0; + system->addEvent(event); + } + + } + + + + if ((!getIndyFightState()) && (getKeysDown() & KEY_Y)) { + consoleEnable = !consoleEnable; + if (displayModeIs8Bit) { + displayMode8Bit(); + } else { + displayMode16Bit(); + } + } + + + if (!((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) && (!getIndyFightState())) { + + if ((getKeysDown() & KEY_A) && (!indyFightState)) { + gameScreenSwap = !gameScreenSwap; + } + + if (!getPenHeld() || (mouseMode != MOUSE_HOVER)) { + if (getKeysDown() & KEY_LEFT) { + mouseMode = MOUSE_LEFT; + } + if (getKeysDown() & KEY_RIGHT) { + if (currentGame->control != CONT_SCUMM_SAMNMAX) { + mouseMode = MOUSE_RIGHT; + } else { + // If we're playing sam and max, click and release the right mouse + // button to change verb + OSystem::Event event; + + event.type = OSystem::EVENT_RBUTTONDOWN; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + + event.type = OSystem::EVENT_RBUTTONUP; + system->addEvent(event); + } + } + if (getKeysDown() & KEY_UP) { + mouseMode = MOUSE_HOVER; + } + } + + + + } + + if ((getKeysDown() & KEY_SELECT)) { + //scaledMode = !scaledMode; + //scY = 4; + showOptionsDialog(); + } + + + } + + if (!getIndyFightState() && !((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) && (getKeysDown() & KEY_X)) { + setKeyboardEnable(!keyboardEnable); + } + + updateStatus(); + + OSystem::Event event; + + if ((!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) { + event.type = OSystem::EVENT_MOUSEMOVE; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + //consolePrintf("x=%d y=%d \n", getPenX(), getPenY()); + } + + if (!keyboardEnable) { + if ((mouseMode != MOUSE_HOVER) || (!displayModeIs8Bit)) { + if (getPenDown() && (!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R))) { + event.type = ((mouseMode == MOUSE_LEFT) || (!displayModeIs8Bit))? OSystem::EVENT_LBUTTONDOWN: OSystem::EVENT_RBUTTONDOWN; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + } + + if (getPenReleased()) { + event.type = mouseMode == MOUSE_LEFT? OSystem::EVENT_LBUTTONUP: OSystem::EVENT_RBUTTONUP; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + } + } else { + // In hover mode, D-pad left and right click the mouse when the pen is on the screen + + if (getPenHeld()) { + if (getKeysDown() & KEY_LEFT) { + event.type = OSystem::EVENT_LBUTTONDOWN; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + } + /* if (getKeysReleased() & KEY_LEFT) { + event.type = OSystem::EVENT_LBUTTONUP; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + }*/ + + if (getKeysDown() & KEY_RIGHT) { + event.type = OSystem::EVENT_RBUTTONDOWN; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + } + /*if (getKeysReleased() & KEY_RIGHT) { + event.type = OSystem::EVENT_RBUTTONUP; + event.mouse = Common::Point(getPenX(), getPenY()); + system->addEvent(event); + }*/ + } + } + + if ((!(getKeysHeld() & KEY_L)) && (!(getKeysHeld() & KEY_R)) && (displayModeIs8Bit)) { + // Controls specific to the control method + + + if (currentGame->control == CONT_SKY) { + // Extra controls for Benieth a Steel Sky + if ((getKeysDown() & KEY_DOWN)) { + penY = 0; + penX = 0; // Show inventory by moving mouse onto top line + } + } + + if (currentGame->control == CONT_SIMON) { + // Extra controls for Simon the Sorcerer + if ((getKeysDown() & KEY_DOWN)) { + OSystem::Event event; + + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = '#'; // F10 or # - show hotspots + event.kbd.ascii = '#'; + event.kbd.flags = 0; + system->addEvent(event); +// consolePrintf("F10\n"); + } + } + + if (currentGame->control == CONT_SCUMM_ORIGINAL) { + // Extra controls for Scumm v1-5 games + if ((getKeysDown() & KEY_DOWN)) { + OSystem::Event event; + + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = '.'; // Full stop - skips current dialogue line + event.kbd.ascii = '.'; + event.kbd.flags = 0; + system->addEvent(event); + } + + if (indyFightState) { + addIndyFightingKeys(); + } + + } + + } + } + + if (!displayModeIs8Bit) { + // Front end controls + + if (leftHandedSwap(getKeysDown()) & KEY_UP) { + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = SDLK_UP; + event.kbd.ascii = 0; + event.kbd.flags = 0; + system->addEvent(event); + + event.type = OSystem::EVENT_KEYUP; + system->addEvent(event); + } + + if (leftHandedSwap(getKeysDown()) & KEY_DOWN) { + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = SDLK_DOWN; + event.kbd.ascii = 0; + event.kbd.flags = 0; + system->addEvent(event); + + event.type = OSystem::EVENT_KEYUP; + system->addEvent(event); + } + + if (leftHandedSwap(getKeysDown()) & KEY_A) { + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = SDLK_RETURN; + event.kbd.ascii = 0; + event.kbd.flags = 0; + system->addEvent(event); + + event.type = OSystem::EVENT_KEYUP; + system->addEvent(event); + } + + } + + + if ((getKeysDown() & KEY_START)) { + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = 319; // F5 + event.kbd.ascii = 319; + event.kbd.flags = 0; + system->addEvent(event); +/* + event.type = OSystem::EVENT_KEYUP; + event.kbd.keycode = 319; // F5 + event.kbd.ascii = 319; + system->addEvent(event);*/ + +// consolePrintf("Pressing F5"); + } + + + if (keyboardEnable) { + DS::addKeyboardEvents(); + } + + consumeKeys(); + + consumePenEvents(); + + } +} + +void updateStatus() { + int offs; + + if (displayModeIs8Bit) { + switch (mouseMode) { + case MOUSE_LEFT: { + offs = 16; + break; + } + case MOUSE_RIGHT: { + offs = 32; + break; + } + case MOUSE_HOVER: { + offs = 0; + break; + } + default: { + // Nothing! + offs = 0; + break; + } + } + + + sprites[0].attribute[0] = ATTR0_BMP | 150; + sprites[0].attribute[1] = ATTR1_SIZE_32 | 208; + sprites[0].attribute[2] = ATTR2_ALPHA(1)| offs; + + if (indyFightState) { + sprites[2].attribute[0] = ATTR0_BMP | 150; + sprites[2].attribute[1] = ATTR1_SIZE_32 | (190 - 32) | (indyFightRight? 0: ATTR1_FLIP_X); + sprites[2].attribute[2] = ATTR2_ALPHA(1)| 48; + } else { + sprites[2].attribute[0] = ATTR0_DISABLED; + sprites[2].attribute[1] = 0; + sprites[2].attribute[2] = 0; + } + } else { + sprites[0].attribute[0] = ATTR0_DISABLED; + sprites[1].attribute[0] = ATTR0_DISABLED; + sprites[2].attribute[0] = ATTR0_DISABLED; + sprites[3].attribute[0] = ATTR0_DISABLED; + } + + if ((keyboardIcon) && (!keyboardEnable) && (!displayModeIs8Bit)) { + spritesMain[0].attribute[0] = ATTR0_BMP | 160; + spritesMain[0].attribute[1] = ATTR1_SIZE_32 | 0; + spritesMain[0].attribute[2] = ATTR2_ALPHA(1) | 64; + } else { + spritesMain[0].attribute[0] = ATTR0_DISABLED; + spritesMain[0].attribute[1] = 0; + spritesMain[0].attribute[2] = 0; + spritesMain[0].attribute[3] = 0; + } + +} + +void soundBufferEmptyHandler() { + REG_IF = IRQ_TIMER2; + + if (soundHiPart) { +// bufferSecondHalf = true; + } else { +// bufferFirstHalf = true; + } + + soundHiPart = !soundHiPart; +} + +void setMainScreenScroll(int x, int y) { + if (gameScreenSwap) { + SUB_BG3_CX = x + (((frameCount & 1) == 0)? 64: 0); + SUB_BG3_CY = y; + } else { + BG3_CX = x + (((frameCount & 1) == 0)? 64: 0); + BG3_CY = y; + + touchX = x >> 8; + touchY = y >> 8; + } +} + +void setMainScreenScale(int x, int y) { + if (gameScreenSwap) { + SUB_BG3_XDX = x; + SUB_BG3_XDY = 0; + SUB_BG3_YDX = 0; + SUB_BG3_YDY = y; + } else { + BG3_XDX = x; + BG3_XDY = 0; + BG3_YDX = 0; + BG3_YDY = y; + + touchScX = x; + touchScY = y; + } +} + +void setZoomedScreenScroll(int x, int y) { + if (gameScreenSwap) { + BG3_CX = x + (((frameCount & 1) == 0)? 64: 0); + BG3_CY = y; + + touchX = x >> 8; + touchY = y >> 8; + } else { + SUB_BG3_CX = x + (((frameCount & 1) == 0)? 64: 0); + SUB_BG3_CY = y; + } +} + +void setZoomedScreenScale(int x, int y) { + if (gameScreenSwap) { + BG3_XDX = x; + BG3_XDY = 0; + BG3_YDX = 0; + BG3_YDY = y; + + touchScX = x; + touchScY = y; + } else { + SUB_BG3_XDX = x; + SUB_BG3_XDY = 0; + SUB_BG3_YDX = 0; + SUB_BG3_YDY = y; + } +} + +void VBlankHandler(void) { +// BG_PALETTE[0] = RGB15(31, 31, 31); +// if (*((int *) (0x023FFF00)) != 0xBEEFCAFE) { + // consolePrintf("Guard band overwritten!"); +// } + +// consolePri ntf("X:%d Y:%d\n", getPenX(), getPenY()); + + + IPC->tweak = tweak; + soundUpdate(); + + + + + if ((!gameScreenSwap) && (!(getKeysHeld() & KEY_L) && !(getKeysHeld() & KEY_R))) { + if (currentGame) { + if (currentGame->control != CONT_SCUMM_SAMNMAX) { + if (getPenHeld() && (getPenY() < SCUMM_GAME_HEIGHT)) { + setTopScreenTarget(getPenX(), getPenY()); + } + } else { + if (getPenHeld()) { + setTopScreenTarget(getPenX(), getPenY()); + } + } + } + } + + + penUpdate(); + keysUpdate(); + + + frameCount++; + + + + if (callback) { + callbackTimer -= FRAME_TIME; + } + + if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) { + + if ((!dragging) && (getPenHeld()) && (penDownFrames > 5)) { + dragging = true; + dragStartX = penX; + dragStartY = penY; + + if (gameScreenSwap) { + dragScX = subScTargetX; + dragScY = subScTargetY; + } else { + dragScX = scX; + dragScY = scY; + } + + + } + + if ((dragging) && (!getPenHeld())) { + dragging = false; + } + + if (dragging) { + + if (gameScreenSwap) { + subScTargetX = dragScX + ((dragStartX - penX) << 8); + subScTargetY = dragScY + ((dragStartY - penY) << 8); + } else { + scX = dragScX + ((dragStartX - penX)); + scY = dragScY + ((dragStartY - penY)); + } + +// consolePrintf("X:%d Y:%d\n", dragStartX - penX, dragStartY - penY); + } + } + + +/* if ((frameCount & 1) == 0) { + SUB_BG3_CX = subScX; + } else { + SUB_BG3_CX = subScX + 64; + } + + SUB_BG3_CY = subScY + (shakePos << 8);*/ + + /*SUB_BG3_XDX = (int) (subScreenWidth / 256.0f * 256); + SUB_BG3_XDY = 0; + SUB_BG3_YDX = 0; + SUB_BG3_YDY = (int) (subScreenHeight / 192.0f * 256);*/ + + + if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) { + if ((getKeysHeld() & KEY_A) && (subScreenScale < 256)) { + subScreenScale += 3; + } + + if ((getKeysHeld() & KEY_B) && (subScreenScale > 128)) { + subScreenScale -=3; + } + + int xCenter = subScTargetX + ((subScreenWidth >> 1) << 8); + int yCenter = subScTargetY + ((subScreenHeight >> 1) << 8); + + subScreenWidth = SCUMM_GAME_WIDTH * subScreenScale >> 8; + subScreenHeight = SCUMM_GAME_HEIGHT * subScreenScale >> 8; + + subScTargetX = xCenter - ((subScreenWidth >> 1) << 8); + subScTargetY = yCenter - ((subScreenHeight >> 1) << 8); + + + + + if (subScTargetX < 0) subScTargetX = 0; + if (subScTargetX > (gameWidth - subScreenWidth) << 8) subScTargetX = (gameWidth - subScreenWidth) << 8; + + if (subScTargetY < 0) subScTargetY = 0; + if (subScTargetY > (gameHeight - subScreenHeight) << 8) subScTargetY = (gameHeight - subScreenHeight) << 8; + } + + subScX += (subScTargetX - subScX) >> 2; + subScY += (subScTargetY - subScY) >> 2; + + if (displayModeIs8Bit) { + + if ((getKeysHeld() & KEY_L) || (getKeysHeld() & KEY_R)) { + + int offsX = 0, offsY = 0; + + + if (getKeysHeld() & KEY_LEFT) { + offsX -= 1; + } + + if (getKeysHeld() & KEY_RIGHT) { + offsX += 1; + } + + if (getKeysHeld() & KEY_UP) { + offsY -= 1; + } + + if (getKeysHeld() & KEY_DOWN) { + offsY += 1; + } + + if (((gameScreenSwap) && (getKeysHeld() & KEY_L)) || ((!gameScreenSwap) && (getKeysHeld() & KEY_R))) { + subScTargetX += offsX << 8; + subScTargetY += offsY << 8; + } else { + scX += offsX; + scY += offsY; + } + } + + if (!scaledMode) { + + if (scX + 256 > gameWidth - 1) { + scX = gameWidth - 1 - 256; + } + + if (scX < 0) { + scX = 0; + } + + if (scY + 192 > gameHeight - 1) { + scY = gameHeight - 1 - 192; + } + + if (scY < 0) { + scY = 0; + } + + setZoomedScreenScroll(subScX, subScY); + setZoomedScreenScale(subScreenWidth, (subScreenHeight * 256) / 192); + + + setMainScreenScroll(scX << 8, (scY << 8) + (shakePos << 8)); + setMainScreenScale(256, 256); // 1:1 scale + + } else { + + if (scY > gameHeight - 192 - 1) { + scY = gameHeight - 192 - 1; + } + + if (scY < 0) { + scY = 0; + } + + setZoomedScreenScroll(subScX, subScY); + setZoomedScreenScale(subScreenWidth, (subScreenHeight * 256) / 192); + + setMainScreenScroll(64, (scY << 8) + (shakePos << 8)); + setMainScreenScale(320, 256); // 1:1 scale + + } + } else { + setZoomedScreenScroll(0, 0); + setZoomedScreenScale(320, 256); + + setMainScreenScroll(0, 0); + setMainScreenScale(320, 256); // 1:1 scale + } + + // Enable on screen keyboard when pen taps icon + if ((keyboardIcon) && (penX < 32) && (penY > 160) && (penHeld)) { + setKeyboardEnable(true); + } + + if (keyboardEnable) { + if (DS::getKeyboardClosed()) { + setKeyboardEnable(false); + } + } + + updateOAM(); + + //PALETTE[0] = RGB15(0, 0, 0); + REG_IF = IRQ_VBLANK; +} + +int getMillis() { + return currentTimeMillis; +// return frameCount * FRAME_TIME; +} + +void setTimerCallback(OSystem::TimerProc proc, int interval) { +// consolePrintf("Set timer proc %x, int %d\n", proc, interval); + callback = proc; + callbackInterval = interval; + callbackTimer = interval; +} + +void timerTickHandler() { + REG_IF = IRQ_TIMER0; + if ((callback) && (callbackTimer > 0)) { + callbackTimer--; + } + currentTimeMillis++; +} + +void setTalkPos(int x, int y) { +// if (gameID != Scumm::GID_SAMNMAX) { +// setTopScreenTarget(x, 0); +// } else { + setTopScreenTarget(x, y); +// } +} + +void setTopScreenTarget(int x, int y) { + subScTargetX = (x - (subScreenWidth >> 1)); + subScTargetY = (y - (subScreenHeight >> 1)); + + if (subScTargetX < 0) subScTargetX = 0; + if (subScTargetX > gameWidth - subScreenWidth) subScTargetX = gameWidth - subScreenWidth; + + if (subScTargetY < 0) subScTargetY = 0; + if (subScTargetY > gameHeight - subScreenHeight) subScTargetY = gameHeight - subScreenHeight; + + subScTargetX <<=8; + subScTargetY <<=8; +} + +void initHardware() { + // Guard band +//((int *) (0x023FFF00)) = 0xBEEFCAFE; + + + penInit(); + + powerON(POWER_ALL); +/* vramSetBankA(VRAM_A_MAIN_BG); + vramSetBankB(VRAM_B_MAIN_BG); + vramSetBankC(VRAM_C_SUB_BG); */ + vramSetBankI(VRAM_I_SUB_SPRITE); + vramSetBankG(VRAM_G_MAIN_SPRITE); + + currentTimeMillis = 0; + + +/* + // Set up a millisecond counter + TIMER0_CR = 0; + TIMER0_DATA = 0xFFFF; + TIMER0_CR = TIMER_ENABLE | TIMER_CASCADE; +*/ + + + + PALETTE[255] = RGB15(0,31,0); + + // Allocate save buffer for game screen +// savedBuffer = new u8[320 * 200]; + displayMode16Bit(); + + memset(BG_GFX, 0, 512 * 256 * 2); + scaledMode = true; + scX = 0; + scY = 0; + subScX = 0; + subScY = 0; + subScTargetX = 0; + subScTargetY = 0; + + //lcdSwap(); + POWER_CR &= ~POWER_SWAP_LCDS; + + frameCount = 0; + callback = NULL; + +// vramSetBankH(VRAM_H_SUB_BG); + + +// // Do text stuff + //BG0_CR = BG_MAP_BASE(0) | BG_TILE_BASE(1); +// BG0_Y0 = 48; + + PALETTE[255] = RGB15(31,31,31);//by default font will be rendered with color 255 + + //consoleInit() is a lot more flexible but this gets you up and running quick +// consoleInitDefault((u16*)SCREEN_BASE_BLOCK(0), (u16*)CHAR_BASE_BLOCK(1), 16); + //consolePrintSet(0, 6); + + //irqs are nice + irqInit(); +// irqInitHandler(); + irqSet(IRQ_VBLANK, VBlankHandler); + irqSet(IRQ_TIMER0, timerTickHandler); + irqSet(IRQ_TIMER2, soundBufferEmptyHandler); + + irqEnable(IRQ_VBLANK); + irqEnable(IRQ_TIMER0); + irqEnable(IRQ_TIMER2); + + + // Set up a millisecond timer + TIMER0_CR = 0; + TIMER0_DATA = (u32) TIMER_FREQ(1000); + TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1 | TIMER_IRQ_REQ; + REG_IME = 1; + + PALETTE[255] = RGB15(0,0,31); + + initSprites(); + +// videoSetModeSub(MODE_3_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); //sub bg 0 will be used to print text + + // Convert texture from 24bit 888 to 16bit 1555, remembering to set top bit! + u8* srcTex = (u8 *) icons_raw; + for (int r = 32 * 160 ; r >= 0; r--) { + SPRITE_GFX_SUB[r] = 0x8000 | (srcTex[r * 3] >> 3) | ((srcTex[r * 3 + 1] >> 3) << 5) | ((srcTex[r * 3 + 2] >> 3) << 10); + SPRITE_GFX[r] = 0x8000 | (srcTex[r * 3] >> 3) | ((srcTex[r * 3 + 1] >> 3) << 5) | ((srcTex[r * 3 + 2] >> 3) << 10); + } + + WAIT_CR &= ~(0x0080); + REG_WRAM_CNT = 0; + +} + + +void setKeyboardIcon(bool enable) { + keyboardIcon = enable; +} + +bool getKeyboardIcon() { + return keyboardIcon; +} + + +//////////////////// +// Pen stuff +//////////////////// + + +void penInit() { + penDown = false; + penHeld = false; + penReleased = false; + penDownLastFrame = false; + penDownSaved = false; + penReleasedSaved = false; + penDownFrames = 0; + consumeKeys(); +} + +void penUpdate() { + +// if (getKeysHeld() & KEY_L) consolePrintf("%d, %d penX=%d, penY=%d tz=%d\n", IPC->touchXpx, IPC->touchYpx, penX, penY, IPC->touchZ1); + + if ((penDownFrames > 1)) { // Is this right? Dunno, but it works for me. + + if ((penHeld)) { + penHeld = true; + penDown = false; + + if ((IPC->touchZ1 > 0) && (IPC->touchXpx > 0) && (IPC->touchYpx > 0)) { + penX = IPC->touchXpx + touchXOffset; + penY = IPC->touchYpx + touchYOffset; + } + + } else { + penDown = true; + penHeld = true; + penDownSaved = true; + + //if ( (ABS(penX - IPC->touchXpx) < 10) && (ABS(penY - IPC->touchYpx) < 10) ) { + if ((IPC->touchZ1 > 0) && (IPC->touchXpx > 0) && (IPC->touchYpx > 0)) { + penX = IPC->touchXpx; + penY = IPC->touchYpx; + } + //} + } + + } else { + if (penHeld) { + penReleased = true; + penReleasedSaved = true; + } else { + penReleased = false; + } + + penDown = false; + penHeld = false; + } + + + if ((IPC->touchZ1 > 0) || ((penDownFrames == 2)) ) { + penDownLastFrame = true; + penDownFrames++; + } else { + penDownLastFrame = false; + penDownFrames = 0; + } + +} + +int leftHandedSwap(int keys) { + // Start and select are unchanged + if (leftHandedMode) { + int result = keys & (~(KEY_R | KEY_L | KEY_Y | KEY_A | KEY_B | KEY_X | KEY_LEFT | KEY_RIGHT | KEY_UP | KEY_DOWN)); + + if (keys & KEY_L) result |= KEY_R; + if (keys & KEY_R) result |= KEY_L; + + if (keys & KEY_LEFT) result |= KEY_Y; + if (keys & KEY_RIGHT) result |= KEY_A; + if (keys & KEY_DOWN) result |= KEY_B; + if (keys & KEY_UP) result |= KEY_X; + + if (keys & KEY_Y) result |= KEY_LEFT; + if (keys & KEY_A) result |= KEY_RIGHT; + if (keys & KEY_B) result |= KEY_DOWN; + if (keys & KEY_X) result |= KEY_UP; + + return result; + } else { + return keys; + } +} + +void keysUpdate() { + scanKeys(); + keysDownSaved |= leftHandedSwap(keysDown()); + keysReleasedSaved |= leftHandedSwap(keysUp()); +} + +int getKeysDown() { + return keysDownSaved; +} + +int getKeysHeld() { + return leftHandedSwap(keysHeld()); +} + +int getKeysReleased() { + return keysReleasedSaved; +} + +void consumeKeys() { + keysDownSaved = 0; + keysReleasedSaved = 0; +} + +bool getPenDown() { + return penDownSaved; +} + +bool getPenHeld() { + return penHeld; +} + +bool getPenReleased() { + return penReleasedSaved; +} + +void consumePenEvents() { + penDownSaved = false; + penReleasedSaved = false; +} + +int getPenX() { + int x = ((penX * touchScX) >> 8) + touchX; + x = x < 0? 0: (x > gameWidth - 1? gameWidth - 1: x); + return x; +} + +int getPenY() { + int y = ((penY * touchScY) >> 8) + touchY; + y = y < 0? 0: (y > gameHeight - 1? gameHeight - 1: y); + return y; +} + +GLvector getPenPos() { + GLvector v; + + v.x = (penX * inttof32(1)) / SCREEN_WIDTH; + v.y = (penY * inttof32(1)) / SCREEN_HEIGHT; + + return v; +} + +void formatSramOption() { + consolePrintf("The following files are present in save RAM:\n"); + DSSaveFileManager::instance()->listFiles(); + + consolePrintf("\nAre you sure you want to\n"); + consolePrintf("DELETE all files?\n"); + consolePrintf("A = Yes, X = No\n"); + + while (true) { + if (keysHeld() & KEY_A) { + DSSaveFileManager::instance()->formatSram(); + consolePrintf("SRAM cleared!\n"); + return; + } + + if (keysHeld() & KEY_X) { + consolePrintf("Whew, that was close!\n"); + return; + } + } +} + + +void setIndyFightState(bool st) { + indyFightState = st; + indyFightRight = true; +} + +bool getIndyFightState() { + return indyFightState; +} + +///////////////// +// GBAMP +///////////////// + +bool GBAMPAvail = false; + +void initGBAMP() { + FAT_InitFiles(); + if (disc_IsInserted()) { + GBAMPAvail = true; + consolePrintf("Found flash card reader!\n"); + } else { + GBAMPAvail = false; + consolePrintf("Flash card reader not found!\n"); + } +} + +bool isGBAMPAvailable() { + return GBAMPAvail; +} + + + +///////////////// +// Main +///////////////// + +static const Common::String test("poo", 1, 16); + + + +int main(void) +{ + soundCallback = NULL; + + + initHardware(); + + // Let arm9 read cartridge + *((u16 *) (0x04000204)) &= ~0x0080; + + lastCallbackFrame = 0; + tweak = 0; + + indyFightState = false; + indyFightRight = true; + + // CPU speed = 67108864 + // 8 frames = 2946 368.5 bytes per fr + +// playSound(stretch, 47619, false); +// playSound(twang, 11010, true); // 18640 + +// bufferSize = 10; + bufferRate = 22050; + bufferFrame = 0; +// bufferSamples = (bufferRate * bufferSize) / 60; + bufferSamples = 4096; + + bufferFirstHalf = false; + bufferSecondHalf = true; + + lastEventFrame = 0; + mouseMode = MOUSE_LEFT; + + + + + int bytes = (2 * (bufferSamples)) + 100; + + soundBuffer = (s16 *) malloc(bytes * 2); + + + soundHiPart = true; +/* + TIMER1_CR = 0; + TIMER1_DATA = TIMER_FREQ(bufferRate); + TIMER1_CR = TIMER_ENABLE | TIMER_DIV_1; + + TIMER2_CR = 0; + TIMER2_DATA = 0xFFFF - (bufferSamples / 2); + TIMER2_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE; + */ + // 2945 - 2947 + + + +// for (int r = 2946; r < 3000; r++) { +// soundBuffer[r] = 30000; +// } + + + + consolePrintf("------------------------\n"); + consolePrintf("ScummVM DS\n"); + consolePrintf("Ported by Neil Millstone\n"); +#ifdef DS_SCUMM_BUILD + consolePrintf("Version 0.61 build A\n"); +#else + consolePrintf("Version 0.61 build B\n"); +#endif + consolePrintf("------------------------\n"); + consolePrintf("L/R + D-pad/pen: Scroll view\n"); + consolePrintf("D-pad left: Left mouse button\n"); + consolePrintf("D-pad right: Right mouse button\n"); + consolePrintf("D-pad up: Hover mouse\n"); + consolePrintf("D-pad down: Skip dialog line\n"); + consolePrintf("B button: Skip cutscenes\n"); + consolePrintf("Select: DS Options menu\n"); + consolePrintf("Start: Game menu\n"); + consolePrintf("Y (in game): Toggle console\n"); + consolePrintf("X: Toggle keyboard\n"); + consolePrintf("A: Swap screens\n"); + consolePrintf("L + R on bootup: Clear SRAM\n\n"); + consolePrintf("For a complete poo list see the\n"); + consolePrintf("help screen.\npoo\n"); + + + for (int r = 0; r < bytes; r++) { + soundBuffer[r] = 0; + } + + consolePrintf("length=%d str='%s'\n", test.size(), test.c_str()); + + swiWaitForVBlank(); + swiWaitForVBlank(); + playSound(soundBuffer, (bufferSamples * 2), true); + swiWaitForVBlank(); + swiWaitForVBlank(); + swiWaitForVBlank(); + + + + // Create a file system node to force search for a zip file in GBA rom space + DSFileSystemNode* node = new DSFileSystemNode(); + if (!node->getZip() || (!node->getZip()->isReady())) { + // If not found, init CF/SD driver + initGBAMP(); + } + delete node; + + + + updateStatus(); + + +// OSystem_DS::instance(); + g_system = new OSystem_DS(); + assert(g_system); + + if ((keysHeld() & KEY_L) && (keysHeld() & KEY_R)) { + formatSramOption(); + } + +// printf("'%s'", Common::ConfigManager::kTransientDomain.c_str()); + //printf("'%s'", Common::ConfigManager::kApplicationDomain.c_str()); + + + char* argv[2] = {"/scummvmds", "--config=scummvmb.ini"}; +#ifdef DS_NON_SCUMM_BUILD + + while (1) { + scummvm_main(2, (char **) &argv); + } +#else + while (1) { + scummvm_main(1, (char **) &argv); + } +#endif + + + return 0; +} + +} + +int main() { + DS::main(); +} diff --git a/backends/platform/ds/arm9/source/dsmain.h b/backends/platform/ds/arm9/source/dsmain.h new file mode 100644 index 0000000000..8c81b0f5e6 --- /dev/null +++ b/backends/platform/ds/arm9/source/dsmain.h @@ -0,0 +1,236 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DSMAIN_H +#define _DSMAIN_H + +#include +#include "stdafx.h" +#include "system.h" +#include "scummconsole.h" +#include "nds/scummvm_ipc.h" + +namespace DS { + +// Pen reading functions +void penInit(); +void penUpdate(); +bool getPenDown(); +bool getPenHeld(); +bool getPenReleased(); +int getPenX(); +int getPenY(); +GLvector getPenPos(); +void consumePenEvents(); + +// Pad reading +int getKeysHeld(); +void keysUpdate(); +int getKeysDown(); +int getKeysReleased(); +void consumeKeys(); +int leftHandedSwap(int keys); + +// Video +void displayMode8Bit(); // Switch to 8-bit mode5 +void displayMode16Bit(); // Switch to 16-bit mode5 + +// Flip double buffer +void displayMode16BitFlipBuffer(); + +// Get address of current back buffer +u16* get16BitBackBuffer(); +u16* get8BitBackBuffer(); + +void setTalkPos(int x, int y); +void setTopScreenTarget(int x, int y); + +// Timers +void setTimerCallback(OSystem::TimerProc proc, int interval); // Setup a callback function at a regular interval +int getMillis(); // Return the current runtime in milliseconds +void doTimerCallback(); // Call callback function if required + +// Sound +void setSoundProc(OSystem::SoundProc proc, void* param); // Setup a callback function for sound +void doSoundCallback(); // Call function if sound buffers need more data +void playSound(const void* data, u32 length, bool loop, bool adpcm = false, int rate = 22050); // Start a sound +void stopSound(int channel); + +// Event queue +void addEventsToQueue(); +void VBlankHandler(); + +// Sam and Max Stuff +void setGameID(int id); +void setCursorIcon(const u8* icon, uint w, uint h, byte keycolor); + +// Shake +void setShakePos(int shakePos); + +// Reports +void memoryReport(); + +// GBAMP +bool isGBAMPAvailable(); + +// Sleep (I'd like some of that right now) +void checkSleepMode(); + +// Virtual keyboard +void setKeyboardIcon(bool enable); +bool getKeyboardIcon(); +void setKeyboardEnable(bool en); +bool getKeyboardEnable(); + +// Options +void setLeftHanded(bool enable); +void setTouchXOffset(int x); +void setTouchYOffset(int y); +void setUnscaledMode(bool enable); +void setIndyFightState(bool st); +bool getIndyFightState(); + +// Display +bool getIsDisplayMode8Bit(); +void setGameSize(int width, int height); +int getGameWidth(); +int getGameHeight(); + +} + + +#endif +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DSMAIN_H +#define _DSMAIN_H + +#include +#include "stdafx.h" +#include "system.h" +#include "scummconsole.h" +#include "nds/scummvm_ipc.h" + +namespace DS { + +// Pen reading functions +void penInit(); +void penUpdate(); +bool getPenDown(); +bool getPenHeld(); +bool getPenReleased(); +int getPenX(); +int getPenY(); +GLvector getPenPos(); +void consumePenEvents(); + +// Pad reading +int getKeysHeld(); +void keysUpdate(); +int getKeysDown(); +int getKeysReleased(); +void consumeKeys(); +int leftHandedSwap(int keys); + +// Video +void displayMode8Bit(); // Switch to 8-bit mode5 +void displayMode16Bit(); // Switch to 16-bit mode5 + +// Flip double buffer +void displayMode16BitFlipBuffer(); + +// Get address of current back buffer +u16* get16BitBackBuffer(); +u16* get8BitBackBuffer(); + +void setTalkPos(int x, int y); +void setTopScreenTarget(int x, int y); + +// Timers +void setTimerCallback(OSystem::TimerProc proc, int interval); // Setup a callback function at a regular interval +int getMillis(); // Return the current runtime in milliseconds +void doTimerCallback(); // Call callback function if required + +// Sound +void setSoundProc(OSystem::SoundProc proc, void* param); // Setup a callback function for sound +void doSoundCallback(); // Call function if sound buffers need more data +void playSound(const void* data, u32 length, bool loop, bool adpcm = false, int rate = 22050); // Start a sound +void stopSound(int channel); + +// Event queue +void addEventsToQueue(); +void VBlankHandler(); + +// Sam and Max Stuff +void setGameID(int id); +void setCursorIcon(const u8* icon, uint w, uint h, byte keycolor); + +// Shake +void setShakePos(int shakePos); + +// Reports +void memoryReport(); + +// GBAMP +bool isGBAMPAvailable(); + +// Sleep (I'd like some of that right now) +void checkSleepMode(); + +// Virtual keyboard +void setKeyboardIcon(bool enable); +bool getKeyboardIcon(); +void setKeyboardEnable(bool en); +bool getKeyboardEnable(); + +// Options +void setLeftHanded(bool enable); +void setTouchXOffset(int x); +void setTouchYOffset(int y); +void setUnscaledMode(bool enable); +void setIndyFightState(bool st); +bool getIndyFightState(); + +// Display +bool getIsDisplayMode8Bit(); +void setGameSize(int width, int height); +int getGameWidth(); +int getGameHeight(); + +} + + +#endif diff --git a/backends/platform/ds/arm9/source/dsoptions.cpp b/backends/platform/ds/arm9/source/dsoptions.cpp new file mode 100644 index 0000000000..04db01389b --- /dev/null +++ b/backends/platform/ds/arm9/source/dsoptions.cpp @@ -0,0 +1,412 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "dsoptions.h" +#include "dsmain.h" +#include "gui/dialog.h" +#include "gui/newgui.h" +#include "gui/listwidget.h" +#include "osystem_ds.h" +#include "engines/scumm/scumm.h" +#include "touchkeyboard.h" + +#ifdef DS_SCUMM_BUILD +namespace Scumm { + extern Common::StringList generateSavegameList(Scumm::ScummEngine *scumm, bool saveMode); + extern Scumm::ScummEngine *g_scumm; +} +#endif + +namespace DS { + +DSOptionsDialog::DSOptionsDialog() : GUI::Dialog(20, 20, 320 - 40, 200 - 40) { + addButton(this, 10, 140, "Close", GUI::kCloseCmd, 'C'); + +#ifdef DS_SCUMM_BUILD + if (!DS::isGBAMPAvailable()) { +// addButton(this, 100, 140, "Delete Save", 'dels', 'D'); + } +#endif + + new GUI::StaticTextWidget(this, 0, 10, 280, 20, "ScummVM DS Options", GUI::kTextAlignCenter); + + _leftHandedCheckbox = new GUI::CheckboxWidget(this, 20, 30, 280, 20, "Left handed mode", 0, 'L'); + _indyFightCheckbox = new GUI::CheckboxWidget(this, 20, 50, 280, 20, "Indy fighting controls", 0, 'I'); + _unscaledCheckbox = new GUI::CheckboxWidget(this, 20, 70, 280, 20, "Unscaled lower screen", 0, 'S'); + + new GUI::StaticTextWidget(this, 20, 90, 110, 20, "Touch X Offset", GUI::kTextAlignLeft); + _touchX = new GUI::SliderWidget(this, 130, 90, 130, 12, 1); + _touchX->setMinValue(-8); + _touchX->setMaxValue(+8); + _touchX->setValue(0); + _touchX->setFlags(GUI::WIDGET_CLEARBG); + + new GUI::StaticTextWidget(this, 20, 110, 110, 20, "Touch Y Offset", GUI::kTextAlignLeft); + _touchY = new GUI::SliderWidget(this, 130, 110, 130, 12, 2); + _touchY->setMinValue(-8); + _touchY->setMaxValue(+8); + _touchY->setValue(0); + _touchY->setFlags(GUI::WIDGET_CLEARBG); + + new GUI::StaticTextWidget(this, 130 + 65 - 10, 130, 20, 20, "0", GUI::kTextAlignCenter); + new GUI::StaticTextWidget(this, 130 + 130 - 10, 130, 20, 20, "8", GUI::kTextAlignCenter); + new GUI::StaticTextWidget(this, 130 - 10, 130, 20, 20, "-8", GUI::kTextAlignCenter); + +#ifdef DS_SCUMM_BUILD + _delDialog = new Scumm::SaveLoadChooser("Delete game:", "Delete", false, Scumm::g_scumm); +#endif + + if (ConfMan.hasKey("lefthanded", "ds")) { + _leftHandedCheckbox->setState(ConfMan.getBool("lefthanded", "ds")); + } else { + _leftHandedCheckbox->setState(false); + } + + if (ConfMan.hasKey("unscaled", "ds")) { + _unscaledCheckbox->setState(ConfMan.getBool("unscaled", "ds")); + } else { + _unscaledCheckbox->setState(false); + } + + _indyFightCheckbox->setState(DS::getIndyFightState()); + + if (ConfMan.hasKey("xoffset", "ds")) { + _touchX->setValue(ConfMan.getInt("xoffset", "ds")); + } else { + _touchX->setValue(0); + } + + if (ConfMan.hasKey("yoffset", "ds")) { + _touchY->setValue(ConfMan.getInt("yoffset", "ds")); + } else { + _touchY->setValue(0); + } + +} + +DSOptionsDialog::~DSOptionsDialog() { + ConfMan.setBool("lefthanded", _leftHandedCheckbox->getState(), "ds"); + ConfMan.setBool("unscaled", _unscaledCheckbox->getState(), "ds"); + ConfMan.setInt("xoffset", _touchX->getValue(), "ds"); + ConfMan.setInt("yoffset", _touchY->getValue(), "ds"); + DS::setOptions(); + DS::setIndyFightState(_indyFightCheckbox->getState()); + ConfMan.flushToDisk(); +} + + +void DSOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { + if (cmd == GUI::kCloseCmd) { + close(); + } + +#ifdef DS_SCUMM_BUILD +/* if (cmd == 'dels') { + _delDialog->setList(Scumm::generateSavegameList(Scumm::g_scumm, false)); + _delDialog->handleCommand(NULL, GUI::kListSelectionChangedCmd, 0); + + OSystem::Event event; + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.ascii = SDLK_DOWN; + event.kbd.keycode = SDLK_DOWN; + OSystem_DS::instance()->addEvent(event); + + event.type = OSystem::EVENT_KEYUP; + OSystem_DS::instance()->addEvent(event); + + int idx = _delDialog->runModal(); + + if (idx >= 0) { + char name[256]; + Scumm::g_scumm->makeSavegameName(name, idx, false); + if (!DS::isGBAMPAvailable()) { + ((DSSaveFileManager *) (OSystem_DS::instance()->getSavefileManager()))->deleteFile(name); + } + } + + }*/ +#endif + + +} + +void showOptionsDialog() { + OSystem_DS* system = OSystem_DS::instance(); + + OSystem::Event event; + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = 'P'; // F5 + event.kbd.ascii = 'P'; + event.kbd.flags = 0; + system->addEvent(event); + + DS::displayMode16Bit(); + + + DSOptionsDialog* d = new DSOptionsDialog(); + d->runModal(); + delete d; + + + DS::displayMode8Bit(); + + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = 'P'; // F5 + event.kbd.ascii = 'P'; + event.kbd.flags = 0; + system->addEvent(event); +} + +void setOptions() { + ConfMan.addGameDomain("ds"); + + if (ConfMan.hasKey("lefthanded", "ds")) { + DS::setLeftHanded(ConfMan.getBool("lefthanded", "ds")); + } else { + DS::setLeftHanded(false); + } + + if (ConfMan.hasKey("unscaled", "ds")) { + DS::setUnscaledMode(ConfMan.getBool("unscaled", "ds")); + } else { + DS::setUnscaledMode(false); + } + + if (ConfMan.hasKey("xoffset", "ds")) { + DS::setTouchXOffset(ConfMan.getInt("xoffset", "ds")); + } else { + DS::setTouchXOffset(0); + } + + if (ConfMan.hasKey("yoffset", "ds")) { + DS::setTouchYOffset(ConfMan.getInt("yoffset", "ds")); + } else { + DS::setTouchXOffset(0); + } + +} + +} + +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "dsoptions.h" +#include "dsmain.h" +#include "gui/dialog.h" +#include "gui/newgui.h" +#include "gui/listwidget.h" +#include "osystem_ds.h" +#include "engines/scumm/scumm.h" +#include "touchkeyboard.h" + +#ifdef DS_SCUMM_BUILD +namespace Scumm { + extern Common::StringList generateSavegameList(Scumm::ScummEngine *scumm, bool saveMode); + extern Scumm::ScummEngine *g_scumm; +} +#endif + +namespace DS { + +DSOptionsDialog::DSOptionsDialog() : GUI::Dialog(20, 20, 320 - 40, 200 - 40) { + addButton(this, 10, 140, "Close", GUI::kCloseCmd, 'C'); + +#ifdef DS_SCUMM_BUILD + if (!DS::isGBAMPAvailable()) { +// addButton(this, 100, 140, "Delete Save", 'dels', 'D'); + } +#endif + + new GUI::StaticTextWidget(this, 0, 10, 280, 20, "ScummVM DS Options", GUI::kTextAlignCenter); + + _leftHandedCheckbox = new GUI::CheckboxWidget(this, 20, 30, 280, 20, "Left handed mode", 0, 'L'); + _indyFightCheckbox = new GUI::CheckboxWidget(this, 20, 50, 280, 20, "Indy fighting controls", 0, 'I'); + _unscaledCheckbox = new GUI::CheckboxWidget(this, 20, 70, 280, 20, "Unscaled lower screen", 0, 'S'); + + new GUI::StaticTextWidget(this, 20, 90, 110, 20, "Touch X Offset", GUI::kTextAlignLeft); + _touchX = new GUI::SliderWidget(this, 130, 90, 130, 12, 1); + _touchX->setMinValue(-8); + _touchX->setMaxValue(+8); + _touchX->setValue(0); + _touchX->setFlags(GUI::WIDGET_CLEARBG); + + new GUI::StaticTextWidget(this, 20, 110, 110, 20, "Touch Y Offset", GUI::kTextAlignLeft); + _touchY = new GUI::SliderWidget(this, 130, 110, 130, 12, 2); + _touchY->setMinValue(-8); + _touchY->setMaxValue(+8); + _touchY->setValue(0); + _touchY->setFlags(GUI::WIDGET_CLEARBG); + + new GUI::StaticTextWidget(this, 130 + 65 - 10, 130, 20, 20, "0", GUI::kTextAlignCenter); + new GUI::StaticTextWidget(this, 130 + 130 - 10, 130, 20, 20, "8", GUI::kTextAlignCenter); + new GUI::StaticTextWidget(this, 130 - 10, 130, 20, 20, "-8", GUI::kTextAlignCenter); + +#ifdef DS_SCUMM_BUILD + _delDialog = new Scumm::SaveLoadChooser("Delete game:", "Delete", false, Scumm::g_scumm); +#endif + + if (ConfMan.hasKey("lefthanded", "ds")) { + _leftHandedCheckbox->setState(ConfMan.getBool("lefthanded", "ds")); + } else { + _leftHandedCheckbox->setState(false); + } + + if (ConfMan.hasKey("unscaled", "ds")) { + _unscaledCheckbox->setState(ConfMan.getBool("unscaled", "ds")); + } else { + _unscaledCheckbox->setState(false); + } + + _indyFightCheckbox->setState(DS::getIndyFightState()); + + if (ConfMan.hasKey("xoffset", "ds")) { + _touchX->setValue(ConfMan.getInt("xoffset", "ds")); + } else { + _touchX->setValue(0); + } + + if (ConfMan.hasKey("yoffset", "ds")) { + _touchY->setValue(ConfMan.getInt("yoffset", "ds")); + } else { + _touchY->setValue(0); + } + +} + +DSOptionsDialog::~DSOptionsDialog() { + ConfMan.setBool("lefthanded", _leftHandedCheckbox->getState(), "ds"); + ConfMan.setBool("unscaled", _unscaledCheckbox->getState(), "ds"); + ConfMan.setInt("xoffset", _touchX->getValue(), "ds"); + ConfMan.setInt("yoffset", _touchY->getValue(), "ds"); + DS::setOptions(); + DS::setIndyFightState(_indyFightCheckbox->getState()); + ConfMan.flushToDisk(); +} + + +void DSOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { + if (cmd == GUI::kCloseCmd) { + close(); + } + +#ifdef DS_SCUMM_BUILD +/* if (cmd == 'dels') { + _delDialog->setList(Scumm::generateSavegameList(Scumm::g_scumm, false)); + _delDialog->handleCommand(NULL, GUI::kListSelectionChangedCmd, 0); + + OSystem::Event event; + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.ascii = SDLK_DOWN; + event.kbd.keycode = SDLK_DOWN; + OSystem_DS::instance()->addEvent(event); + + event.type = OSystem::EVENT_KEYUP; + OSystem_DS::instance()->addEvent(event); + + int idx = _delDialog->runModal(); + + if (idx >= 0) { + char name[256]; + Scumm::g_scumm->makeSavegameName(name, idx, false); + if (!DS::isGBAMPAvailable()) { + ((DSSaveFileManager *) (OSystem_DS::instance()->getSavefileManager()))->deleteFile(name); + } + } + + }*/ +#endif + + +} + +void showOptionsDialog() { + OSystem_DS* system = OSystem_DS::instance(); + + OSystem::Event event; + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = 'P'; // F5 + event.kbd.ascii = 'P'; + event.kbd.flags = 0; + system->addEvent(event); + + DS::displayMode16Bit(); + + + DSOptionsDialog* d = new DSOptionsDialog(); + d->runModal(); + delete d; + + + DS::displayMode8Bit(); + + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.keycode = 'P'; // F5 + event.kbd.ascii = 'P'; + event.kbd.flags = 0; + system->addEvent(event); +} + +void setOptions() { + ConfMan.addGameDomain("ds"); + + if (ConfMan.hasKey("lefthanded", "ds")) { + DS::setLeftHanded(ConfMan.getBool("lefthanded", "ds")); + } else { + DS::setLeftHanded(false); + } + + if (ConfMan.hasKey("unscaled", "ds")) { + DS::setUnscaledMode(ConfMan.getBool("unscaled", "ds")); + } else { + DS::setUnscaledMode(false); + } + + if (ConfMan.hasKey("xoffset", "ds")) { + DS::setTouchXOffset(ConfMan.getInt("xoffset", "ds")); + } else { + DS::setTouchXOffset(0); + } + + if (ConfMan.hasKey("yoffset", "ds")) { + DS::setTouchYOffset(ConfMan.getInt("yoffset", "ds")); + } else { + DS::setTouchXOffset(0); + } + +} + +} + diff --git a/backends/platform/ds/arm9/source/dsoptions.h b/backends/platform/ds/arm9/source/dsoptions.h new file mode 100644 index 0000000000..ce3d77c7da --- /dev/null +++ b/backends/platform/ds/arm9/source/dsoptions.h @@ -0,0 +1,122 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DSOPTIONS_H_ +#define _DSOPTIONS_H_ + +#include "stdafx.h" + +#include "common/scummsys.h" +#include "common/str.h" + +#include "gui/object.h" +#include "gui/widget.h" +#include "gui/dialog.h" +#include "scumm/dialogs.h" + +namespace DS { + +class DSOptionsDialog : public GUI::Dialog { + +public: + DSOptionsDialog(); + ~DSOptionsDialog(); + +protected: + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + + + GUI::SliderWidget* _touchX; + GUI::SliderWidget* _touchY; + GUI::CheckboxWidget* _leftHandedCheckbox; + GUI::CheckboxWidget* _unscaledCheckbox; + GUI::CheckboxWidget* _indyFightCheckbox; +#ifdef DS_SCUMM_BUILD + Scumm::SaveLoadChooser* _delDialog; +#endif + +}; + +extern void showOptionsDialog(); +extern void setOptions(); + +} + +#endif +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DSOPTIONS_H_ +#define _DSOPTIONS_H_ + +#include "stdafx.h" + +#include "common/scummsys.h" +#include "common/str.h" + +#include "gui/object.h" +#include "gui/widget.h" +#include "gui/dialog.h" +#include "scumm/dialogs.h" + +namespace DS { + +class DSOptionsDialog : public GUI::Dialog { + +public: + DSOptionsDialog(); + ~DSOptionsDialog(); + +protected: + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + + + GUI::SliderWidget* _touchX; + GUI::SliderWidget* _touchY; + GUI::CheckboxWidget* _leftHandedCheckbox; + GUI::CheckboxWidget* _unscaledCheckbox; + GUI::CheckboxWidget* _indyFightCheckbox; +#ifdef DS_SCUMM_BUILD + Scumm::SaveLoadChooser* _delDialog; +#endif + +}; + +extern void showOptionsDialog(); +extern void setOptions(); + +} + +#endif diff --git a/backends/platform/ds/arm9/source/fat/disc_io.c b/backends/platform/ds/arm9/source/fat/disc_io.c new file mode 100644 index 0000000000..f2fbee5d65 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/disc_io.c @@ -0,0 +1,844 @@ +/* + + disc_io.c + + uniformed io-interface to work with Chishm's FAT library + + Written by MightyMax + + Modified by Chishm: + 2005-11-06 + * Added WAIT_CR modifications for NDS + + Modified by www.neoflash.com: + 2006-02-03 + * Added SUPPORT_* defines, comment out any of the SUPPORT_* defines in disc_io.h to remove support + for the given interface and stop code being linked to the binary + + * Added support for MK2 MMC interface + + * Added disc_Cache* functions + + Modified by Chishm: + 2006-02-05 + * Added Supercard SD support + + Modified by CyteX: + 2006-02-26 + * Added EFA2 support +*/ + +#include "disc_io.h" + +#ifdef NDS + #include +#endif + + +// Include known io-interfaces: +#ifdef SUPPORT_MPCF + #include "io_mpcf.h" +#endif + +#ifdef SUPPORT_M3CF + #include "io_m3cf.h" +#endif + +#ifdef SUPPORT_M3SD + #include "io_m3sd.h" +#endif + +#ifdef SUPPORT_SCCF + #include "io_sccf.h" +#endif + +#ifdef SUPPORT_SCSD + #include "io_scsd.h" +#endif + +#ifdef SUPPORT_FCSR + #include "io_fcsr.h" +#endif + +#ifdef SUPPORT_NMMC + #include "io_nmmc.h" +#endif + +#ifdef SUPPORT_EFA2 + #include "io_efa2.h" +#endif + +// Keep a pointer to the active interface +LPIO_INTERFACE active_interface = 0; + + +/* + + Disc Cache functions + 2006-02-03: + Added by www.neoflash.com + +*/ + +#ifdef DISC_CACHE + +#include + +#define CACHE_FREE 0xFFFFFFFF + +static u8 cacheBuffer[ DISC_CACHE_COUNT * 512 ]; + +static struct { + u32 sector; + u32 dirty; + u32 count; +} cache[ DISC_CACHE_COUNT ]; + +static u32 disc_CacheFind(u32 sector) { + u32 i; + + for( i = 0; i < DISC_CACHE_COUNT; i++ ) { + if( cache[ i ].sector == sector ) + return i; + } + + return CACHE_FREE; +} + +static u32 disc_CacheFindFree(void) { + + u32 i = 0, j; + u32 count = -1; + + for( j = 0; j < DISC_CACHE_COUNT; j++ ) { + + if( cache[ j ].sector == CACHE_FREE ) { + i = j; + break; + } + + if( cache[ j ].count < count ) { + count = cache[ j ].count; + i = j; + } + } + + if( cache[ i ].sector != CACHE_FREE && cache[i].dirty != 0 ) { + + active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] ); + /* todo: handle write error here */ + + cache[ i ].sector = CACHE_FREE; + cache[ i ].dirty = 0; + cache[ i ].count = 0; + } + + return i; +} + +void disc_CacheInit(void) { + + u32 i; + + for( i = 0; i < DISC_CACHE_COUNT; i++ ) { + cache[ i ].sector = CACHE_FREE; + cache[ i ].dirty = 0; + cache[ i ].count = 0; + } + +} + +bool disc_CacheFlush(void) { + + u32 i; + + if( !active_interface ) return false; + + for( i = 0; i < DISC_CACHE_COUNT; i++ ) { + if( cache[ i ].sector != CACHE_FREE && cache[ i ].dirty != 0 ) { + if( active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] ) == false ) + return false; + + cache[ i ].dirty = 0; + } + } + return true; +} + +bool disc_CacheReadSector( void *buffer, u32 sector) { + u32 i = disc_CacheFind( sector ); + if( i == CACHE_FREE ) { + i = disc_CacheFindFree(); + cache[ i ].sector = sector; + if( active_interface->fn_ReadSectors( sector, 1, &cacheBuffer[ i * 512 ] ) == false ) + return false; + } +#ifdef DISK_CACHE_DMA + DMA3_SRC = (u32)&cacheBuffer[ i * 512 ] + DMA3_DEST = (u32)buffer; + DMA3_CR = 128 | DMA_COPY_WORDS; +#else + memcpy( buffer, &cacheBuffer[ i * 512 ], 512 ); +#endif + cache[ i ].count++; + return true; +} + +bool disc_CacheWriteSector( void *buffer, u32 sector ) { + u32 i = disc_CacheFind( sector ); + if( i == CACHE_FREE ) { + i = disc_CacheFindFree(); + cache [ i ].sector = sector; + } +#ifdef DISK_CACHE_DMA + DMA3_SRC = (u32)buffer; + DMA3_DEST = (u32)&cacheBuffer[ i * 512 ]; + DMA3_CR = 128 | DMA_COPY_WORDS; +#else + memcpy( &cacheBuffer[ i * 512 ], buffer, 512 ); +#endif + cache[ i ].dirty=1; + cache[ i ].count++; + return true; +} + +#endif + +/* + + Hardware level disc funtions + +*/ + +bool disc_setGbaSlotInterface (void) +{ + // If running on an NDS, make sure the correct CPU can access + // the GBA cart. First implemented by SaTa. +#ifdef NDS + #ifdef ARM9 +// WAIT_CR &= ~(0x8080); + #endif + #ifdef ARM7 +// WAIT_CR |= (0x8080); + #endif +#endif + +#ifdef SUPPORT_M3CF + // check if we have a M3 perfect CF plugged in + active_interface = M3CF_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set M3 CF as default IO + return true ; + } ; +#endif + +#ifdef SUPPORT_M3SD + // check if we have a M3 perfect SD plugged in + active_interface = M3SD_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set M3 SD as default IO + return true ; + } ; +#endif + +#ifdef SUPPORT_MPCF + // check if we have a GBA Movie Player plugged in + active_interface = MPCF_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set GBAMP as default IO + return true ; + } ; +#endif + +#ifdef SUPPORT_SCCF + // check if we have a SuperCard CF plugged in + active_interface = SCCF_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set SC CF as default IO + return true ; + } ; +#endif + +#ifdef SUPPORT_SCSD + // check if we have a SuperCard SD plugged in + active_interface = SCSD_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set SC SD as default IO + return true ; + } ; +#endif + + +#ifdef SUPPORT_EFA2 + // check if we have a EFA2 plugged in + active_interface = EFA2_GetInterface() ; + if (active_interface->fn_StartUp()) + { + return true ; + } ; +#endif + +#ifdef SUPPORT_FCSR + // check if we have a GBA Flash Cart plugged in + active_interface = FCSR_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set FC as default IO + return true ; + } ; +#endif + + return false; +} + +#ifdef NDS +// Check the DS card slot for a valid memory card interface +// If an interface is found, it is set as the default interace +// and it returns true. Otherwise the default interface is left +// untouched and it returns false. +bool disc_setDsSlotInterface (void) +{ +#ifdef ARM9 + WAIT_CR &= ~(1<<11); +#endif +#ifdef ARM7 + WAIT_CR |= (1<<11); +#endif + +#ifdef SUPPORT_NMMC + // check if we have a Neoflash MK2 / MK3 plugged in + active_interface = NMMC_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set Neoflash MK2 / MK3 as default IO + return true ; + } ; +#endif + + return false; +} +#endif + + +bool disc_Init(void) +{ +#ifdef DISC_CACHE + disc_CacheInit(); +#endif + + + if (active_interface != 0) { + return true; + } + +#ifdef NDS + if (disc_setDsSlotInterface()) { + return true; + } +#endif + + if (disc_setGbaSlotInterface()) { + return true; + } + + // could not find a working IO Interface + active_interface = 0 ; + return false ; +} + +bool disc_IsInserted(void) +{ + if (active_interface) return active_interface->fn_IsInserted() ; + return false ; +} + +bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer) +{ +#ifdef DISC_CACHE + u8 *p=(u8*)buffer; + u32 i; + u32 inumSecs=numSecs; + if(numSecs==0) + inumSecs=256; + for( i = 0; ifn_ReadSectors(sector,numSecs,buffer) ; + return false ; +#endif +} + +bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer) +{ +#ifdef DISC_CACHE + u8 *p=(u8*)buffer; + u32 i; + u32 inumSecs=numSecs; + if(numSecs==0) + inumSecs=256; + for( i = 0; ifn_WriteSectors(sector,numSecs,buffer) ; + return false ; +#endif +} + +bool disc_ClearStatus(void) +{ + if (active_interface) return active_interface->fn_ClearStatus() ; + return false ; +} + +bool disc_Shutdown(void) +{ +#ifdef DISC_CACHE + disc_CacheFlush(); +#endif + if (active_interface) active_interface->fn_Shutdown() ; + active_interface = 0 ; + return true ; +} + +u32 disc_HostType (void) +{ + if (active_interface) { + return active_interface->ul_ioType; + } else { + return 0; + } +} + +/* + + disc_io.c + + uniformed io-interface to work with Chishm's FAT library + + Written by MightyMax + + Modified by Chishm: + 2005-11-06 + * Added WAIT_CR modifications for NDS + + Modified by www.neoflash.com: + 2006-02-03 + * Added SUPPORT_* defines, comment out any of the SUPPORT_* defines in disc_io.h to remove support + for the given interface and stop code being linked to the binary + + * Added support for MK2 MMC interface + + * Added disc_Cache* functions + + Modified by Chishm: + 2006-02-05 + * Added Supercard SD support + + Modified by CyteX: + 2006-02-26 + * Added EFA2 support +*/ + +#include "disc_io.h" + +#ifdef NDS + #include +#endif + + +// Include known io-interfaces: +#ifdef SUPPORT_MPCF + #include "io_mpcf.h" +#endif + +#ifdef SUPPORT_M3CF + #include "io_m3cf.h" +#endif + +#ifdef SUPPORT_M3SD + #include "io_m3sd.h" +#endif + +#ifdef SUPPORT_SCCF + #include "io_sccf.h" +#endif + +#ifdef SUPPORT_SCSD + #include "io_scsd.h" +#endif + +#ifdef SUPPORT_FCSR + #include "io_fcsr.h" +#endif + +#ifdef SUPPORT_NMMC + #include "io_nmmc.h" +#endif + +#ifdef SUPPORT_EFA2 + #include "io_efa2.h" +#endif + +// Keep a pointer to the active interface +LPIO_INTERFACE active_interface = 0; + + +/* + + Disc Cache functions + 2006-02-03: + Added by www.neoflash.com + +*/ + +#ifdef DISC_CACHE + +#include + +#define CACHE_FREE 0xFFFFFFFF + +static u8 cacheBuffer[ DISC_CACHE_COUNT * 512 ]; + +static struct { + u32 sector; + u32 dirty; + u32 count; +} cache[ DISC_CACHE_COUNT ]; + +static u32 disc_CacheFind(u32 sector) { + u32 i; + + for( i = 0; i < DISC_CACHE_COUNT; i++ ) { + if( cache[ i ].sector == sector ) + return i; + } + + return CACHE_FREE; +} + +static u32 disc_CacheFindFree(void) { + + u32 i = 0, j; + u32 count = -1; + + for( j = 0; j < DISC_CACHE_COUNT; j++ ) { + + if( cache[ j ].sector == CACHE_FREE ) { + i = j; + break; + } + + if( cache[ j ].count < count ) { + count = cache[ j ].count; + i = j; + } + } + + if( cache[ i ].sector != CACHE_FREE && cache[i].dirty != 0 ) { + + active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] ); + /* todo: handle write error here */ + + cache[ i ].sector = CACHE_FREE; + cache[ i ].dirty = 0; + cache[ i ].count = 0; + } + + return i; +} + +void disc_CacheInit(void) { + + u32 i; + + for( i = 0; i < DISC_CACHE_COUNT; i++ ) { + cache[ i ].sector = CACHE_FREE; + cache[ i ].dirty = 0; + cache[ i ].count = 0; + } + +} + +bool disc_CacheFlush(void) { + + u32 i; + + if( !active_interface ) return false; + + for( i = 0; i < DISC_CACHE_COUNT; i++ ) { + if( cache[ i ].sector != CACHE_FREE && cache[ i ].dirty != 0 ) { + if( active_interface->fn_WriteSectors( cache[ i ].sector, 1, &cacheBuffer[ i * 512 ] ) == false ) + return false; + + cache[ i ].dirty = 0; + } + } + return true; +} + +bool disc_CacheReadSector( void *buffer, u32 sector) { + u32 i = disc_CacheFind( sector ); + if( i == CACHE_FREE ) { + i = disc_CacheFindFree(); + cache[ i ].sector = sector; + if( active_interface->fn_ReadSectors( sector, 1, &cacheBuffer[ i * 512 ] ) == false ) + return false; + } +#ifdef DISK_CACHE_DMA + DMA3_SRC = (u32)&cacheBuffer[ i * 512 ] + DMA3_DEST = (u32)buffer; + DMA3_CR = 128 | DMA_COPY_WORDS; +#else + memcpy( buffer, &cacheBuffer[ i * 512 ], 512 ); +#endif + cache[ i ].count++; + return true; +} + +bool disc_CacheWriteSector( void *buffer, u32 sector ) { + u32 i = disc_CacheFind( sector ); + if( i == CACHE_FREE ) { + i = disc_CacheFindFree(); + cache [ i ].sector = sector; + } +#ifdef DISK_CACHE_DMA + DMA3_SRC = (u32)buffer; + DMA3_DEST = (u32)&cacheBuffer[ i * 512 ]; + DMA3_CR = 128 | DMA_COPY_WORDS; +#else + memcpy( &cacheBuffer[ i * 512 ], buffer, 512 ); +#endif + cache[ i ].dirty=1; + cache[ i ].count++; + return true; +} + +#endif + +/* + + Hardware level disc funtions + +*/ + +bool disc_setGbaSlotInterface (void) +{ + // If running on an NDS, make sure the correct CPU can access + // the GBA cart. First implemented by SaTa. +#ifdef NDS + #ifdef ARM9 +// WAIT_CR &= ~(0x8080); + #endif + #ifdef ARM7 +// WAIT_CR |= (0x8080); + #endif +#endif + +#ifdef SUPPORT_M3CF + // check if we have a M3 perfect CF plugged in + active_interface = M3CF_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set M3 CF as default IO + return true ; + } ; +#endif + +#ifdef SUPPORT_M3SD + // check if we have a M3 perfect SD plugged in + active_interface = M3SD_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set M3 SD as default IO + return true ; + } ; +#endif + +#ifdef SUPPORT_MPCF + // check if we have a GBA Movie Player plugged in + active_interface = MPCF_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set GBAMP as default IO + return true ; + } ; +#endif + +#ifdef SUPPORT_SCCF + // check if we have a SuperCard CF plugged in + active_interface = SCCF_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set SC CF as default IO + return true ; + } ; +#endif + +#ifdef SUPPORT_SCSD + // check if we have a SuperCard SD plugged in + active_interface = SCSD_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set SC SD as default IO + return true ; + } ; +#endif + + +#ifdef SUPPORT_EFA2 + // check if we have a EFA2 plugged in + active_interface = EFA2_GetInterface() ; + if (active_interface->fn_StartUp()) + { + return true ; + } ; +#endif + +#ifdef SUPPORT_FCSR + // check if we have a GBA Flash Cart plugged in + active_interface = FCSR_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set FC as default IO + return true ; + } ; +#endif + + return false; +} + +#ifdef NDS +// Check the DS card slot for a valid memory card interface +// If an interface is found, it is set as the default interace +// and it returns true. Otherwise the default interface is left +// untouched and it returns false. +bool disc_setDsSlotInterface (void) +{ +#ifdef ARM9 + WAIT_CR &= ~(1<<11); +#endif +#ifdef ARM7 + WAIT_CR |= (1<<11); +#endif + +#ifdef SUPPORT_NMMC + // check if we have a Neoflash MK2 / MK3 plugged in + active_interface = NMMC_GetInterface() ; + if (active_interface->fn_StartUp()) + { + // set Neoflash MK2 / MK3 as default IO + return true ; + } ; +#endif + + return false; +} +#endif + + +bool disc_Init(void) +{ +#ifdef DISC_CACHE + disc_CacheInit(); +#endif + + + if (active_interface != 0) { + return true; + } + +#ifdef NDS + if (disc_setDsSlotInterface()) { + return true; + } +#endif + + if (disc_setGbaSlotInterface()) { + return true; + } + + // could not find a working IO Interface + active_interface = 0 ; + return false ; +} + +bool disc_IsInserted(void) +{ + if (active_interface) return active_interface->fn_IsInserted() ; + return false ; +} + +bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer) +{ +#ifdef DISC_CACHE + u8 *p=(u8*)buffer; + u32 i; + u32 inumSecs=numSecs; + if(numSecs==0) + inumSecs=256; + for( i = 0; ifn_ReadSectors(sector,numSecs,buffer) ; + return false ; +#endif +} + +bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer) +{ +#ifdef DISC_CACHE + u8 *p=(u8*)buffer; + u32 i; + u32 inumSecs=numSecs; + if(numSecs==0) + inumSecs=256; + for( i = 0; ifn_WriteSectors(sector,numSecs,buffer) ; + return false ; +#endif +} + +bool disc_ClearStatus(void) +{ + if (active_interface) return active_interface->fn_ClearStatus() ; + return false ; +} + +bool disc_Shutdown(void) +{ +#ifdef DISC_CACHE + disc_CacheFlush(); +#endif + if (active_interface) active_interface->fn_Shutdown() ; + active_interface = 0 ; + return true ; +} + +u32 disc_HostType (void) +{ + if (active_interface) { + return active_interface->ul_ioType; + } else { + return 0; + } +} + diff --git a/backends/platform/ds/arm9/source/fat/disc_io.h b/backends/platform/ds/arm9/source/fat/disc_io.h new file mode 100644 index 0000000000..f647f9ac02 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/disc_io.h @@ -0,0 +1,364 @@ +#ifndef DISC_IO_H +#define DISC_IO_H + +//---------------------------------------------------------------------- +// Customisable features + +// Use DMA to read the card, remove this line to use normal reads/writes +// #define _CF_USE_DMA + +// Allow buffers not aligned to 16 bits when reading files. +// Note that this will slow down access speed, so only use if you have to. +// It is also incompatible with DMA +//#define _CF_ALLOW_UNALIGNED + +// Device support options, added by www.neoflash.com + +#define SUPPORT_NMMC // comment out this line to remove Neoflash MK2 MMC Card support +#define SUPPORT_MPCF // comment out this line to remove GBA Movie Player support +#define SUPPORT_M3CF // comment out this line to remove M3 Perfect CF support +#define SUPPORT_M3SD // comment out this line to remove M3 Perfect SD support +#define SUPPORT_SCCF // comment out this line to remove Supercard CF support +#define SUPPORT_SCSD // comment out this line to remove Supercard SD support +//#define SUPPORT_EFA2 // comment out this line to remove EFA2 linker support +#define SUPPORT_FCSR // comment out this line to remove GBA Flash Cart support + +// Disk caching options, added by www.neoflash.com +// Each additional sector cache uses 512 bytes of memory +// Disk caching is disabled on GBA to conserve memory + +#define DISC_CACHE // uncomment this line to enable disc caching +#define DISC_CACHE_COUNT 16 // maximum number of sectors to cache (512 bytes per sector) +//#define DISK_CACHE_DMA // use DMA for cache copies. If this is enabled, the data buffers must be word aligned + + +//---------------------------------------------------------------------- + +#if defined _CF_USE_DMA && defined _CF_ALLOW_UNALIGNED + #error You can't use both DMA and unaligned memory +#endif + +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + +#ifdef NDS + #include +#else + #include "gba_types.h" +#endif + +// Disable NDS specific hardware and features if running on a GBA +#ifndef NDS + #undef SUPPORT_NMMC + #undef DISC_CACHE +#endif + +/* + + Interface for host program + +*/ + +#define BYTE_PER_READ 512 + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------------- +disc_Init +Detects the inserted hardware and initialises it if necessary +bool return OUT: true if a suitable device was found +-----------------------------------------------------------------*/ +extern bool disc_Init(void) ; + +/*----------------------------------------------------------------- +disc_IsInserted +Is a usable disc inserted? +bool return OUT: true if a disc is inserted +-----------------------------------------------------------------*/ +extern bool disc_IsInserted(void) ; + +/*----------------------------------------------------------------- +disc_ReadSectors +Read 512 byte sector numbered "sector" into "buffer" +u32 sector IN: address of first 512 byte sector on disc to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +extern bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer) ; +#define disc_ReadSector(sector,buffer) disc_ReadSectors(sector,1,buffer) + +/*----------------------------------------------------------------- +disc_WriteSectors +Write 512 byte sector numbered "sector" from "buffer" +u32 sector IN: address of 512 byte sector on disc to write +u8 numSecs IN: number of 512 byte sectors to write , + 1 to 256 sectors can be read, 0 = 256 +void* buffer IN: pointer to 512 byte buffer to read data from +bool return OUT: true if successful +-----------------------------------------------------------------*/ +extern bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer) ; +#define disc_WriteSector(sector,buffer) disc_WriteSectors(sector,1,buffer) + +/*----------------------------------------------------------------- +disc_ClearStatus +Tries to make the disc go back to idle mode +bool return OUT: true if the disc is idle +-----------------------------------------------------------------*/ +extern bool disc_ClearStatus(void) ; + +/*----------------------------------------------------------------- +disc_Shutdown +unload the disc interface +bool return OUT: true if successful +-----------------------------------------------------------------*/ +extern bool disc_Shutdown(void) ; + +/*----------------------------------------------------------------- +disc_HostType +Returns a unique u32 number identifying the host type +u32 return OUT: 0 if no host initialised, else the identifier of + the host +-----------------------------------------------------------------*/ +extern u32 disc_HostType(void); + +/*----------------------------------------------------------------- +disc_CacheFlush +Flushes any cache writes to disc +bool return OUT: true if successful, false if an error occurs +Added by www.neoflash.com +-----------------------------------------------------------------*/ +#ifdef DISC_CACHE +extern bool disc_CacheFlush(void); +#else +static inline bool disc_CacheFlush(void) +{ + return true; +} +#endif // DISC_CACHE + + +/* + + Interface for IO libs + +*/ + +#define FEATURE_MEDIUM_CANREAD 0x00000001 +#define FEATURE_MEDIUM_CANWRITE 0x00000002 +#define FEATURE_SLOT_GBA 0x00000010 +#define FEATURE_SLOT_NDS 0x00000020 + +typedef bool (* FN_MEDIUM_STARTUP)(void) ; +typedef bool (* FN_MEDIUM_ISINSERTED)(void) ; +typedef bool (* FN_MEDIUM_READSECTORS)(u32 sector, u8 numSecs, void* buffer) ; +typedef bool (* FN_MEDIUM_WRITESECTORS)(u32 sector, u8 numSecs, void* buffer) ; +typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ; +typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ; + + +typedef struct { + unsigned long ul_ioType ; + unsigned long ul_Features ; + FN_MEDIUM_STARTUP fn_StartUp ; + FN_MEDIUM_ISINSERTED fn_IsInserted ; + FN_MEDIUM_READSECTORS fn_ReadSectors ; + FN_MEDIUM_WRITESECTORS fn_WriteSectors ; + FN_MEDIUM_CLEARSTATUS fn_ClearStatus ; + FN_MEDIUM_SHUTDOWN fn_Shutdown ; +} IO_INTERFACE, *LPIO_INTERFACE ; + +#ifdef __cplusplus +} +#endif + +#endif // define DISC_IO_H +#ifndef DISC_IO_H +#define DISC_IO_H + +//---------------------------------------------------------------------- +// Customisable features + +// Use DMA to read the card, remove this line to use normal reads/writes +// #define _CF_USE_DMA + +// Allow buffers not aligned to 16 bits when reading files. +// Note that this will slow down access speed, so only use if you have to. +// It is also incompatible with DMA +//#define _CF_ALLOW_UNALIGNED + +// Device support options, added by www.neoflash.com + +#define SUPPORT_NMMC // comment out this line to remove Neoflash MK2 MMC Card support +#define SUPPORT_MPCF // comment out this line to remove GBA Movie Player support +#define SUPPORT_M3CF // comment out this line to remove M3 Perfect CF support +#define SUPPORT_M3SD // comment out this line to remove M3 Perfect SD support +#define SUPPORT_SCCF // comment out this line to remove Supercard CF support +#define SUPPORT_SCSD // comment out this line to remove Supercard SD support +//#define SUPPORT_EFA2 // comment out this line to remove EFA2 linker support +#define SUPPORT_FCSR // comment out this line to remove GBA Flash Cart support + +// Disk caching options, added by www.neoflash.com +// Each additional sector cache uses 512 bytes of memory +// Disk caching is disabled on GBA to conserve memory + +#define DISC_CACHE // uncomment this line to enable disc caching +#define DISC_CACHE_COUNT 16 // maximum number of sectors to cache (512 bytes per sector) +//#define DISK_CACHE_DMA // use DMA for cache copies. If this is enabled, the data buffers must be word aligned + + +//---------------------------------------------------------------------- + +#if defined _CF_USE_DMA && defined _CF_ALLOW_UNALIGNED + #error You can't use both DMA and unaligned memory +#endif + +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + +#ifdef NDS + #include +#else + #include "gba_types.h" +#endif + +// Disable NDS specific hardware and features if running on a GBA +#ifndef NDS + #undef SUPPORT_NMMC + #undef DISC_CACHE +#endif + +/* + + Interface for host program + +*/ + +#define BYTE_PER_READ 512 + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------------- +disc_Init +Detects the inserted hardware and initialises it if necessary +bool return OUT: true if a suitable device was found +-----------------------------------------------------------------*/ +extern bool disc_Init(void) ; + +/*----------------------------------------------------------------- +disc_IsInserted +Is a usable disc inserted? +bool return OUT: true if a disc is inserted +-----------------------------------------------------------------*/ +extern bool disc_IsInserted(void) ; + +/*----------------------------------------------------------------- +disc_ReadSectors +Read 512 byte sector numbered "sector" into "buffer" +u32 sector IN: address of first 512 byte sector on disc to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +extern bool disc_ReadSectors(u32 sector, u8 numSecs, void* buffer) ; +#define disc_ReadSector(sector,buffer) disc_ReadSectors(sector,1,buffer) + +/*----------------------------------------------------------------- +disc_WriteSectors +Write 512 byte sector numbered "sector" from "buffer" +u32 sector IN: address of 512 byte sector on disc to write +u8 numSecs IN: number of 512 byte sectors to write , + 1 to 256 sectors can be read, 0 = 256 +void* buffer IN: pointer to 512 byte buffer to read data from +bool return OUT: true if successful +-----------------------------------------------------------------*/ +extern bool disc_WriteSectors(u32 sector, u8 numSecs, void* buffer) ; +#define disc_WriteSector(sector,buffer) disc_WriteSectors(sector,1,buffer) + +/*----------------------------------------------------------------- +disc_ClearStatus +Tries to make the disc go back to idle mode +bool return OUT: true if the disc is idle +-----------------------------------------------------------------*/ +extern bool disc_ClearStatus(void) ; + +/*----------------------------------------------------------------- +disc_Shutdown +unload the disc interface +bool return OUT: true if successful +-----------------------------------------------------------------*/ +extern bool disc_Shutdown(void) ; + +/*----------------------------------------------------------------- +disc_HostType +Returns a unique u32 number identifying the host type +u32 return OUT: 0 if no host initialised, else the identifier of + the host +-----------------------------------------------------------------*/ +extern u32 disc_HostType(void); + +/*----------------------------------------------------------------- +disc_CacheFlush +Flushes any cache writes to disc +bool return OUT: true if successful, false if an error occurs +Added by www.neoflash.com +-----------------------------------------------------------------*/ +#ifdef DISC_CACHE +extern bool disc_CacheFlush(void); +#else +static inline bool disc_CacheFlush(void) +{ + return true; +} +#endif // DISC_CACHE + + +/* + + Interface for IO libs + +*/ + +#define FEATURE_MEDIUM_CANREAD 0x00000001 +#define FEATURE_MEDIUM_CANWRITE 0x00000002 +#define FEATURE_SLOT_GBA 0x00000010 +#define FEATURE_SLOT_NDS 0x00000020 + +typedef bool (* FN_MEDIUM_STARTUP)(void) ; +typedef bool (* FN_MEDIUM_ISINSERTED)(void) ; +typedef bool (* FN_MEDIUM_READSECTORS)(u32 sector, u8 numSecs, void* buffer) ; +typedef bool (* FN_MEDIUM_WRITESECTORS)(u32 sector, u8 numSecs, void* buffer) ; +typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ; +typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ; + + +typedef struct { + unsigned long ul_ioType ; + unsigned long ul_Features ; + FN_MEDIUM_STARTUP fn_StartUp ; + FN_MEDIUM_ISINSERTED fn_IsInserted ; + FN_MEDIUM_READSECTORS fn_ReadSectors ; + FN_MEDIUM_WRITESECTORS fn_WriteSectors ; + FN_MEDIUM_CLEARSTATUS fn_ClearStatus ; + FN_MEDIUM_SHUTDOWN fn_Shutdown ; +} IO_INTERFACE, *LPIO_INTERFACE ; + +#ifdef __cplusplus +} +#endif + +#endif // define DISC_IO_H diff --git a/backends/platform/ds/arm9/source/fat/gba_nds_fat.c b/backends/platform/ds/arm9/source/fat/gba_nds_fat.c new file mode 100644 index 0000000000..b3b2858e41 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/gba_nds_fat.c @@ -0,0 +1,6660 @@ +/* + gba_nds_fat.c + By chishm (Michael Chisholm) + + Routines for reading a compact flash card + using the GBA Movie Player or M3. + + Some FAT routines are based on those in fat.c, which + is part of avrlib by Pascal Stang. + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +//--------------------------------------------------------------- +// Includes + +#include "gba_nds_fat.h" +#include "disc_io.h" +#include +#ifdef NDS + #include // Time on the NDS +#endif +//---------------------------------------------------------------- +// Data types +#ifndef NULL + #define NULL 0 +#endif + +//---------------------------------------------------------------- +// NDS memory access control register +#ifdef NDS + #ifndef WAIT_CR + #define WAIT_CR (*(vu16*)0x04000204) + #endif +#endif + +//--------------------------------------------------------------- +// Appropriate placement of CF functions and data +#ifdef NDS + #define _VARS_IN_RAM +#else + #define _VARS_IN_RAM __attribute__ ((section (".sbss"))) +#endif + + +//----------------------------------------------------------------- +// FAT constants +#define CLUSTER_EOF_16 0xFFFF +#define CLUSTER_EOF 0x0FFFFFFF +#define CLUSTER_FREE 0x0000 +#define CLUSTER_FIRST 0x0002 + +#define FILE_LAST 0x00 +#define FILE_FREE 0xE5 + +#define FAT16_ROOT_DIR_CLUSTER 0x00 + + +//----------------------------------------------------------------- +// long file name constants +#define LFN_END 0x40 +#define LFN_DEL 0x80 + +//----------------------------------------------------------------- +// Data Structures + +// Take care of packing for GCC - it doesn't obey pragma pack() +// properly for ARM targets. +#ifdef __GNUC__ + #define __PACKED __attribute__ ((__packed__)) +#else + #define __PACKED + #pragma pack(1) +#endif + +// Boot Sector - must be packed +typedef struct +{ + u8 jmpBoot[3]; + u8 OEMName[8]; + // BIOS Parameter Block + u16 bytesPerSector; + u8 sectorsPerCluster; + u16 reservedSectors; + u8 numFATs; + u16 rootEntries; + u16 numSectorsSmall; + u8 mediaDesc; + u16 sectorsPerFAT; + u16 sectorsPerTrk; + u16 numHeads; + u32 numHiddenSectors; + u32 numSectors; + union // Different types of extended BIOS Parameter Block for FAT16 and FAT32 + { + struct + { + // Ext BIOS Parameter Block for FAT16 + u8 driveNumber; + u8 reserved1; + u8 extBootSig; + u32 volumeID; + u8 volumeLabel[11]; + u8 fileSysType[8]; + // Bootcode + u8 bootCode[448]; + } __PACKED fat16; + struct + { + // FAT32 extended block + u32 sectorsPerFAT32; + u16 extFlags; + u16 fsVer; + u32 rootClus; + u16 fsInfo; + u16 bkBootSec; + u8 reserved[12]; + // Ext BIOS Parameter Block for FAT16 + u8 driveNumber; + u8 reserved1; + u8 extBootSig; + u32 volumeID; + u8 volumeLabel[11]; + u8 fileSysType[8]; + // Bootcode + u8 bootCode[420]; + } __PACKED fat32; + } __PACKED extBlock; + + u16 bootSig; + +} __PACKED BOOT_SEC; + +// Directory entry - must be packed +typedef struct +{ + u8 name[8]; + u8 ext[3]; + u8 attrib; + u8 reserved; + u8 cTime_ms; + u16 cTime; + u16 cDate; + u16 aDate; + u16 startClusterHigh; + u16 mTime; + u16 mDate; + u16 startCluster; + u32 fileSize; +} __PACKED DIR_ENT; + +// Long file name directory entry - must be packed +typedef struct +{ + u8 ordinal; // Position within LFN + u16 char0; + u16 char1; + u16 char2; + u16 char3; + u16 char4; + u8 flag; // Should be equal to ATTRIB_LFN + u8 reserved1; // Always 0x00 + u8 checkSum; // Checksum of short file name (alias) + u16 char5; + u16 char6; + u16 char7; + u16 char8; + u16 char9; + u16 char10; + u16 reserved2; // Always 0x0000 + u16 char11; + u16 char12; +} __PACKED DIR_ENT_LFN; + +const char lfn_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E}; + +// End of packed structs +#ifdef __PACKED + #undef __PACKED +#endif +#ifndef __GNUC__ + #pragma pack() +#endif + +//----------------------------------------------------------------- +// Global Variables + +// _VARS_IN_RAM variables are stored in the largest section of WRAM +// available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA + +// Files +_VARS_IN_RAM FAT_FILE openFiles[MAX_FILES_OPEN]; + +// Long File names +_VARS_IN_RAM char lfnName[MAX_FILENAME_LENGTH]; +bool lfnExists; + +// Locations on card +int filesysRootDir; +int filesysRootDirClus; +int filesysFAT; +int filesysSecPerFAT; +int filesysNumSec; +int filesysData; +int filesysBytePerSec; +int filesysSecPerClus; +int filesysBytePerClus; + +FS_TYPE filesysType = FS_UNKNOWN; +u32 filesysTotalSize; + +// Info about FAT +u32 fatLastCluster; +u32 fatFirstFree; + +// fatBuffer used to reduce wear on the CF card from multiple writes +_VARS_IN_RAM char fatBuffer[BYTE_PER_READ]; +u32 fatBufferCurSector; + +// Current working directory +u32 curWorkDirCluster; + +// Position of the directory entry last retreived with FAT_GetDirEntry +u32 wrkDirCluster; +int wrkDirSector; +int wrkDirOffset; + +// Global sector buffer to save on stack space +_VARS_IN_RAM unsigned char globalBuffer[BYTE_PER_READ]; + +//----------------------------------------------------------------- +// Functions contained in this file - predeclarations +char ucase (char character); +u16 getRTCtoFileTime (void); +u16 getRTCtoFileDate (void); + +bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry); +bool FAT_ClearLinks (u32 cluster); +DIR_ENT FAT_DirEntFromPath (const char* path); +u32 FAT_FirstFreeCluster(void); +DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin); +u32 FAT_LinkFreeCluster(u32 cluster); +u32 FAT_NextCluster(u32 cluster); +bool FAT_WriteFatEntry (u32 cluster, u32 value); +bool FAT_GetFilename (DIR_ENT dirEntry, char* alias); + +bool FAT_InitFiles (void); +bool FAT_FreeFiles (void); +int FAT_remove (const char* path); +bool FAT_chdir (const char* path); +FILE_TYPE FAT_FindFirstFile (char* filename); +FILE_TYPE FAT_FindNextFile (char* filename); +FILE_TYPE FAT_FileExists (const char* filename); +bool FAT_GetAlias (char* alias); +bool FAT_GetLongFilename (char* filename); +u32 FAT_GetFileSize (void); +u32 FAT_GetFileCluster (void); + +FAT_FILE* FAT_fopen(const char* path, const char* mode); +bool FAT_fclose (FAT_FILE* file); +bool FAT_feof(FAT_FILE* file); +int FAT_fseek(FAT_FILE* file, s32 offset, int origin); +u32 FAT_ftell (FAT_FILE* file); +u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file); +u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file); +char FAT_fgetc (FAT_FILE* file); +char FAT_fputc (char c, FAT_FILE* file); + +/*----------------------------------------------------------------- +ucase +Returns the uppercase version of the given char +char IN: a character +char return OUT: uppercase version of character +-----------------------------------------------------------------*/ +char ucase (char character) +{ + if ((character > 0x60) && (character < 0x7B)) + character = character - 0x20; + return (character); +} + + +/*----------------------------------------------------------------- +getRTCtoFileTime and getRTCtoFileDate +Returns the time / date in Dir Entry styled format +u16 return OUT: time / date in Dir Entry styled format +-----------------------------------------------------------------*/ +u16 getRTCtoFileTime (void) +{ +#ifdef NDS + return ( + ( ( (IPC->rtc_hours > 11 ? IPC->rtc_hours - 40 : IPC->rtc_hours) & 0x1F) << 11) | + ( (IPC->rtc_minutes & 0x3F) << 5) | + ( (IPC->rtc_seconds >> 1) & 0x1F) ); +#else + return 0; +#endif +} + +u16 getRTCtoFileDate (void) +{ +#ifdef NDS + return ( + ( ((IPC->rtc_year + 20) & 0x7F) <<9) | + ( (IPC->rtc_month & 0xF) << 5) | + (IPC->rtc_day & 0x1F) ); +#else + return 0; +#endif +} + + +/*----------------------------------------------------------------- +Disc level FAT routines +-----------------------------------------------------------------*/ +#define FAT_ClustToSect(m) \ + (((m-2) * filesysSecPerClus) + filesysData) + +/*----------------------------------------------------------------- +FAT_NextCluster +Internal function - gets the cluster linked from input cluster +-----------------------------------------------------------------*/ +u32 FAT_NextCluster(u32 cluster) +{ + u32 nextCluster = CLUSTER_FREE; + u32 sector; + int offset; + + switch (filesysType) + { + case FS_UNKNOWN: + nextCluster = CLUSTER_FREE; + break; + + case FS_FAT12: + sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ); + offset = ((cluster * 3) / 2) % BYTE_PER_READ; + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + nextCluster = ((u8*)fatBuffer)[offset]; + offset++; + + if (offset >= BYTE_PER_READ) { + offset = 0; + fatBufferCurSector++; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + nextCluster |= (((u8*)fatBuffer)[offset]) << 8; + + if (cluster & 0x01) { + nextCluster = nextCluster >> 4; + } else { + nextCluster &= 0x0FFF; + } + + if (nextCluster >= 0x0FF7) + { + nextCluster = CLUSTER_EOF; + } + + break; + + case FS_FAT16: + sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 1); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // read the nextCluster value + nextCluster = ((u16*)fatBuffer)[offset]; + + if (nextCluster >= 0xFFF7) + { + nextCluster = CLUSTER_EOF; + } + break; + + case FS_FAT32: + sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 2); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // read the nextCluster value + nextCluster = (((u32*)fatBuffer)[offset]) & 0x0FFFFFFF; + + if (nextCluster >= 0x0FFFFFF7) + { + nextCluster = CLUSTER_EOF; + } + break; + + default: + nextCluster = CLUSTER_FREE; + break; + } + + return nextCluster; +} + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_WriteFatEntry +Internal function - writes FAT information about a cluster +-----------------------------------------------------------------*/ +bool FAT_WriteFatEntry (u32 cluster, u32 value) +{ + u32 sector; + int offset; + + if ((cluster < 0x0002) || (cluster > fatLastCluster)) + { + return false; + } + + switch (filesysType) + { + case FS_UNKNOWN: + return false; + break; + + case FS_FAT12: + sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ); + offset = ((cluster * 3) / 2) % BYTE_PER_READ; + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + if (cluster & 0x01) { + + ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4); + + offset++; + if (offset >= BYTE_PER_READ) { + offset = 0; + // write the buffer back to disc + disc_WriteSector(fatBufferCurSector, fatBuffer); + // read the next sector + fatBufferCurSector++; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4; + + } else { + + ((u8*)fatBuffer)[offset] = value & 0xFF; + + offset++; + if (offset >= BYTE_PER_READ) { + offset = 0; + // write the buffer back to disc + disc_WriteSector(fatBufferCurSector, fatBuffer); + // read the next sector + fatBufferCurSector++; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F); + } + + break; + + case FS_FAT16: + sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 1); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // write the value to the FAT buffer + ((u16*)fatBuffer)[offset] = (value & 0xFFFF); + + break; + + case FS_FAT32: + sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 2); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // write the value to the FAT buffer + (((u32*)fatBuffer)[offset]) = value; + + break; + + default: + return false; + break; + } + + // write the buffer back to disc + disc_WriteSector(fatBufferCurSector, fatBuffer); + + return true; +} +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_ReadWriteFatEntryBuffered +Internal function - writes FAT information about a cluster to a + buffer that should then be flushed to disc using + FAT_WriteFatEntryFlushBuffer() + Call FAT_WriteFatEntry first so as not to ruin the disc. + Also returns the entry being replaced +-----------------------------------------------------------------*/ +u32 FAT_ReadWriteFatEntryBuffered (u32 cluster, u32 value) +{ + u32 sector; + int offset; + u32 oldValue; + + if ((cluster < 0x0002) || (cluster > fatLastCluster)) + return CLUSTER_FREE; + + + switch (filesysType) + { + case FS_UNKNOWN: + oldValue = CLUSTER_FREE; + break; + + case FS_FAT12: + sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ); + offset = ((cluster * 3) / 2) % BYTE_PER_READ; + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // write the old buffer to disc + if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT))) + disc_WriteSector(fatBufferCurSector, fatBuffer); + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + if (cluster & 0x01) { + + oldValue = (((u8*)fatBuffer)[offset] & 0xF0) >> 4; + ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4); + + offset++; + if (offset >= BYTE_PER_READ) { + offset = 0; + // write the buffer back to disc + disc_WriteSector(fatBufferCurSector, fatBuffer); + // read the next sector + fatBufferCurSector++; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + oldValue |= ((((u8*)fatBuffer)[offset]) << 4) & 0x0FF0; + ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4; + + } else { + + oldValue = ((u8*)fatBuffer)[offset] & 0xFF; + ((u8*)fatBuffer)[offset] = value & 0xFF; + + offset++; + if (offset >= BYTE_PER_READ) { + offset = 0; + // write the buffer back to disc + disc_WriteSector(fatBufferCurSector, fatBuffer); + // read the next sector + fatBufferCurSector++; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + oldValue |= (((u8*)fatBuffer)[offset] & 0x0F) << 8; + ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F); + } + + if (oldValue >= 0x0FF7) + { + oldValue = CLUSTER_EOF; + } + + break; + + case FS_FAT16: + sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 1); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // write the old buffer to disc + if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT))) + disc_WriteSector(fatBufferCurSector, fatBuffer); + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // write the value to the FAT buffer + oldValue = ((u16*)fatBuffer)[offset]; + ((u16*)fatBuffer)[offset] = value; + + if (oldValue >= 0xFFF7) + { + oldValue = CLUSTER_EOF; + } + + break; + + case FS_FAT32: + sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 2); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // write the old buffer to disc + if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT))) + disc_WriteSector(fatBufferCurSector, fatBuffer); + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // write the value to the FAT buffer + oldValue = ((u32*)fatBuffer)[offset]; + ((u32*)fatBuffer)[offset] = value; + + if (oldValue >= 0x0FFFFFF7) + { + oldValue = CLUSTER_EOF; + } + + break; + + default: + oldValue = CLUSTER_FREE; + break; + } + + return oldValue; +} +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_WriteFatEntryFlushBuffer +Flush the FAT buffer back to the disc +-----------------------------------------------------------------*/ +bool FAT_WriteFatEntryFlushBuffer (void) +{ + // write the buffer disc + if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT))) + { + disc_WriteSector(fatBufferCurSector, fatBuffer); + return true; + } else { + return false; + } +} +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_FirstFreeCluster +Internal function - gets the first available free cluster +-----------------------------------------------------------------*/ +u32 FAT_FirstFreeCluster(void) +{ + // Start at first valid cluster + if (fatFirstFree < CLUSTER_FIRST) + fatFirstFree = CLUSTER_FIRST; + + while ((FAT_NextCluster(fatFirstFree) != CLUSTER_FREE) && (fatFirstFree <= fatLastCluster)) + { + fatFirstFree++; + } + if (fatFirstFree > fatLastCluster) + { + return CLUSTER_EOF; + } + return fatFirstFree; +} +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_LinkFreeCluster +Internal function - gets the first available free cluster, sets it +to end of file, links the input cluster to it then returns the +cluster number +-----------------------------------------------------------------*/ +u32 FAT_LinkFreeCluster(u32 cluster) +{ + u32 firstFree; + u32 curLink; + + if (cluster > fatLastCluster) + { + return CLUSTER_FREE; + } + + // Check if the cluster already has a link, and return it if so + curLink = FAT_NextCluster (cluster); + if ((curLink >= CLUSTER_FIRST) && (curLink < fatLastCluster)) + { + return curLink; // Return the current link - don't allocate a new one + } + + // Get a free cluster + firstFree = FAT_FirstFreeCluster(); + + // If couldn't get a free cluster then return + if (firstFree == CLUSTER_EOF) + { + return CLUSTER_FREE; + } + + if ((cluster >= CLUSTER_FIRST) && (cluster < fatLastCluster)) + { + // Update the linked from FAT entry + FAT_WriteFatEntry (cluster, firstFree); + } + // Create the linked to FAT entry + FAT_WriteFatEntry (firstFree, CLUSTER_EOF); + + return firstFree; +} +#endif + + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_ClearLinks +Internal function - frees any cluster used by a file +-----------------------------------------------------------------*/ +bool FAT_ClearLinks (u32 cluster) +{ + u32 nextCluster; + + if ((cluster < 0x0002) || (cluster > fatLastCluster)) + return false; + + // Store next cluster before erasing the link + nextCluster = FAT_NextCluster (cluster); + + // Erase the link + FAT_WriteFatEntry (cluster, CLUSTER_FREE); + + // Move onto next cluster + cluster = nextCluster; + + while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE)) + { + cluster = FAT_ReadWriteFatEntryBuffered (cluster, CLUSTER_FREE); + } + + // Flush fat write buffer + FAT_WriteFatEntryFlushBuffer (); + + return true; +} +#endif + + +/*----------------------------------------------------------------- +FAT_InitFiles +Reads the FAT information from the CF card. +You need to call this before reading any files. +bool return OUT: true if successful. +-----------------------------------------------------------------*/ +bool FAT_InitFiles (void) +{ + int i; + int bootSector; + BOOT_SEC* bootSec; + + if (!disc_Init()) + { + return (false); + } + + // Read first sector of CF card + if ( !disc_ReadSector(0, globalBuffer)) { + return false; + } + + // Make sure it is a valid MBR or boot sector + if ( (globalBuffer[0x1FE] != 0x55) || (globalBuffer[0x1FF] != 0xAA)) { + return false; + } + + // Check if there is a FAT string, which indicates this is a boot sector + if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T')) + { + bootSector = 0; + } + // Check for FAT32 + else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T')) + { + bootSector = 0; + } + else // This is an MBR + { + // Find first valid partition from MBR + // First check for an active partition + for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i] != 0x80); i+= 0x10); + // If it didn't find an active partition, search for any valid partition + if (i == 0x1FE) + for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10); + + // Go to first valid partition + if ( i != 0x1FE) // Make sure it found a partition + { + bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F); + } else { + bootSector = 0; // No partition found, assume this is a MBR free disk + } + } + + // Read in boot sector + bootSec = (BOOT_SEC*) globalBuffer; + if (!disc_ReadSector (bootSector, bootSec)) { + return false; + } + + // Store required information about the file system + if (bootSec->sectorsPerFAT != 0) + { + filesysSecPerFAT = bootSec->sectorsPerFAT; + } + else + { + filesysSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32; + } + + if (bootSec->numSectorsSmall != 0) + { + filesysNumSec = bootSec->numSectorsSmall; + } + else + { + filesysNumSec = bootSec->numSectors; + } + + filesysBytePerSec = BYTE_PER_READ; // Sector size is redefined to be 512 bytes + filesysSecPerClus = bootSec->sectorsPerCluster * bootSec->bytesPerSector / BYTE_PER_READ; + filesysBytePerClus = filesysBytePerSec * filesysSecPerClus; + filesysFAT = bootSector + bootSec->reservedSectors; + + filesysRootDir = filesysFAT + (bootSec->numFATs * filesysSecPerFAT); + filesysData = filesysRootDir + ((bootSec->rootEntries * sizeof(DIR_ENT)) / filesysBytePerSec); + + filesysTotalSize = (filesysNumSec - filesysData) * filesysBytePerSec; + + // Store info about FAT + fatLastCluster = (filesysNumSec - filesysData) / bootSec->sectorsPerCluster; + fatFirstFree = CLUSTER_FIRST; + fatBufferCurSector = 0; + disc_ReadSector(fatBufferCurSector, fatBuffer); + + if (fatLastCluster < 4085) + { + filesysType = FS_FAT12; // FAT12 volume - unsupported + } + else if (fatLastCluster < 65525) + { + filesysType = FS_FAT16; // FAT16 volume + } + else + { + filesysType = FS_FAT32; // FAT32 volume + } + + if (filesysType != FS_FAT32) + { + filesysRootDirClus = FAT16_ROOT_DIR_CLUSTER; + } + else // Set up for the FAT32 way + { + filesysRootDirClus = bootSec->extBlock.fat32.rootClus; + // Check if FAT mirroring is enabled + if (!(bootSec->extBlock.fat32.extFlags & 0x80)) + { + // Use the active FAT + filesysFAT = filesysFAT + ( filesysSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F)); + } + } + + // Set current directory to the root + curWorkDirCluster = filesysRootDirClus; + wrkDirCluster = filesysRootDirClus; + wrkDirSector = 0; + wrkDirOffset = 0; + + // Set all files to free + for (i=0; i < MAX_FILES_OPEN; i++) + { + openFiles[i].inUse = false; + } + + // No long filenames so far + lfnExists = false; + for (i = 0; i < MAX_FILENAME_LENGTH; i++) + { + lfnName[i] = '\0'; + } + + return (true); +} + +/*----------------------------------------------------------------- +FAT_FreeFiles +Closes all open files then resets the CF card. +Call this before exiting back to the GBAMP +bool return OUT: true if successful. +-----------------------------------------------------------------*/ +bool FAT_FreeFiles (void) +{ + int i; + + // Close all open files + for (i=0; i < MAX_FILES_OPEN; i++) + { + if (openFiles[i].inUse == true) + { + FAT_fclose(&openFiles[i]); + } + } + + // Flush any sectors in disc cache + disc_CacheFlush(); + + // Clear card status + disc_ClearStatus(); + + // Return status of card + return disc_IsInserted(); +} + + +/*----------------------------------------------------------------- +FAT_GetDirEntry +Return the file info structure of the next valid file entry +u32 dirCluster: IN cluster of subdirectory table +int entry: IN the desired file entry +int origin IN: relative position of the entry +DIR_ENT return OUT: desired dirEntry. First char will be FILE_FREE if + the entry does not exist. +-----------------------------------------------------------------*/ +DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin) +{ + DIR_ENT dir; + DIR_ENT_LFN lfn; + int firstSector = 0; + bool notFound = false; + bool found = false; + int maxSectors; + int lfnPos, aliasPos; + u8 lfnChkSum, chkSum; + + int i; + + dir.name[0] = FILE_FREE; // default to no file found + dir.attrib = 0x00; + + // Check if fat has been initialised + if (filesysBytePerSec == 0) + { + return (dir); + } + + switch (origin) + { + case SEEK_SET: + wrkDirCluster = dirCluster; + wrkDirSector = 0; + wrkDirOffset = -1; + break; + case SEEK_CUR: // Don't change anything + break; + case SEEK_END: // Find entry signifying end of directory + // Subtraction will never reach 0, so it keeps going + // until reaches end of directory + wrkDirCluster = dirCluster; + wrkDirSector = 0; + wrkDirOffset = -1; + entry = -1; + break; + default: + return dir; + } + + lfnChkSum = 0; + maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus); + + // Scan Dir for correct entry + firstSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)); + disc_ReadSector (firstSector + wrkDirSector, globalBuffer); + found = false; + notFound = false; + do { + wrkDirOffset++; + if (wrkDirOffset == BYTE_PER_READ / sizeof (DIR_ENT)) + { + wrkDirOffset = 0; + wrkDirSector++; + if ((wrkDirSector == filesysSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER)) + { + wrkDirSector = 0; + wrkDirCluster = FAT_NextCluster(wrkDirCluster); + if (wrkDirCluster == CLUSTER_EOF) + { + notFound = true; + } + firstSector = FAT_ClustToSect(wrkDirCluster); + } + else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (filesysData - filesysRootDir))) + { + notFound = true; // Got to end of root dir + } + disc_ReadSector (firstSector + wrkDirSector, globalBuffer); + } + dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset]; + if ((dir.name[0] != FILE_FREE) && (dir.name[0] > 0x20) && ((dir.attrib & ATTRIB_VOL) != ATTRIB_VOL)) + { + entry--; + if (lfnExists) + { + // Calculate file checksum + chkSum = 0; + for (aliasPos=0; aliasPos < 11; aliasPos++) + { + // NOTE: The operation is an unsigned char rotate right + chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + (aliasPos < 8 ? dir.name[aliasPos] : dir.ext[aliasPos - 8]); + } + if (chkSum != lfnChkSum) + { + lfnExists = false; + lfnName[0] = '\0'; + } + } + if (entry == 0) + { + if (!lfnExists) + { + FAT_GetFilename (dir, lfnName); + } + found = true; + } + } + else if (dir.name[0] == FILE_LAST) + { + if (origin == SEEK_END) + { + found = true; + } + else + { + notFound = true; + } + } + else if (dir.attrib == ATTRIB_LFN) + { + lfn = ((DIR_ENT_LFN*) globalBuffer)[wrkDirOffset]; + if (lfn.ordinal & LFN_DEL) + { + lfnExists = false; + } + else if (lfn.ordinal & LFN_END) // Last part of LFN, make sure it isn't deleted (Thanks MoonLight) + { + lfnExists = true; + lfnName[(lfn.ordinal & ~LFN_END) * 13] = '\0'; // Set end of lfn to null character + lfnChkSum = lfn.checkSum; + } + if (lfnChkSum != lfn.checkSum) + { + lfnExists = false; + } + if (lfnExists) + { + lfnPos = ((lfn.ordinal & ~LFN_END) - 1) * 13; + for (i = 0; i < 13; i++) { + lfnName[lfnPos + i] = ((u8*)&lfn)[(int)(lfn_offset_table[i])] /* | ((u8*)&lfn)[(int)(lfn_offset_table[i]) + 1] include this for unicode support*/; + } + } + } + } while (!found && !notFound); + + // If no file is found, return FILE_FREE + if (notFound) + { + dir.name[0] = FILE_FREE; + } + + return (dir); +} + + +/*----------------------------------------------------------------- +FAT_GetLongFilename +Get the long name of the last file or directory retrived with + GetDirEntry. Also works for FindFirstFile and FindNextFile. + If a long name doesn't exist, it returns the short name + instead. +char* filename: OUT will be filled with the filename, should be at + least 256 bytes long +bool return OUT: return true if successful +-----------------------------------------------------------------*/ +bool FAT_GetLongFilename (char* filename) +{ + if (filename == NULL) + return false; + + strncpy (filename, lfnName, MAX_FILENAME_LENGTH - 1); + filename[MAX_FILENAME_LENGTH - 1] = '\0'; + + return true; +} + + +/*----------------------------------------------------------------- +FAT_GetFilename +Get the alias (short name) of the file or directory stored in + dirEntry +DIR_ENT dirEntry: IN a valid directory table entry +char* alias OUT: will be filled with the alias (short filename), + should be at least 13 bytes long +bool return OUT: return true if successful +-----------------------------------------------------------------*/ +bool FAT_GetFilename (DIR_ENT dirEntry, char* alias) +{ + int i=0; + int j=0; + + alias[0] = '\0'; + if (dirEntry.name[0] != FILE_FREE) + { + if (dirEntry.name[0] == '.') + { + alias[0] = '.'; + if (dirEntry.name[1] == '.') + { + alias[1] = '.'; + alias[2] = '\0'; + } + else + { + alias[1] = '\0'; + } + } + else + { + // Copy the filename from the dirEntry to the string + for (i = 0; (i < 8) && (dirEntry.name[i] != ' '); i++) + { + alias[i] = dirEntry.name[i]; + } + // Copy the extension from the dirEntry to the string + if (dirEntry.ext[0] != ' ') + { + alias[i++] = '.'; + for ( j = 0; (j < 3) && (dirEntry.ext[j] != ' '); j++) + { + alias[i++] = dirEntry.ext[j]; + } + } + alias[i] = '\0'; + } + } + + return (alias[0] != '\0'); +} + +/*----------------------------------------------------------------- +FAT_GetAlias +Get the alias (short name) of the last file or directory entry read + using GetDirEntry. Works for FindFirstFile and FindNextFile +char* alias OUT: will be filled with the alias (short filename), + should be at least 13 bytes long +bool return OUT: return true if successful +-----------------------------------------------------------------*/ +bool FAT_GetAlias (char* alias) +{ + if (alias == NULL) + { + return false; + } + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return FAT_GetFilename (((DIR_ENT*)globalBuffer)[wrkDirOffset], alias); +} + +/*----------------------------------------------------------------- +FAT_GetFileSize +Get the file size of the last file found or openned. +This idea is based on a modification by MoonLight +u32 return OUT: the file size +-----------------------------------------------------------------*/ +u32 FAT_GetFileSize (void) +{ + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return ((DIR_ENT*)globalBuffer)[wrkDirOffset].fileSize; +} + +/*----------------------------------------------------------------- +FAT_GetFileCluster +Get the first cluster of the last file found or openned. +u32 return OUT: the file start cluster +-----------------------------------------------------------------*/ +u32 FAT_GetFileCluster (void) +{ + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return (((DIR_ENT*)globalBuffer)[wrkDirOffset].startCluster) | (((DIR_ENT*)globalBuffer)[wrkDirOffset].startClusterHigh << 16); +} + +/*----------------------------------------------------------------- +FAT_GetFileAttributes +Get the attributes of the last file found or openned. +u8 return OUT: the file's attributes +-----------------------------------------------------------------*/ +u8 FAT_GetFileAttributes (void) +{ + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib; +} + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_SetFileAttributes +Set the attributes of a file. +const char* filename IN: The name and path of the file to modify +u8 attributes IN: The attribute values to assign +u8 mask IN: Detemines which attributes are changed +u8 return OUT: the file's new attributes +-----------------------------------------------------------------*/ +u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask) +{ + // Get the file + if (!FAT_FileExists(filename)) { + return 0xff; + } + + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib = (((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib & ~(mask & 0x27)) | (attributes & 0x27); // 0x27 is he settable attributes + + disc_WriteSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib; +} +#endif + +#ifdef FILE_TIME_SUPPORT +time_t FAT_FileTimeToCTime (u16 fileTime, u16 fileDate) +{ + struct tm timeInfo; + + timeInfo.tm_year = (fileDate >> 9) + 80; // years since midnight January 1970 + timeInfo.tm_mon = ((fileDate >> 5) & 0xf) - 1; // Months since january + timeInfo.tm_mday = fileDate & 0x1f; // Day of the month + + timeInfo.tm_hour = fileTime >> 11; // hours past midnight + timeInfo.tm_min = (fileTime >> 5) & 0x3f; // minutes past the hour + timeInfo.tm_sec = (fileTime & 0x1f) * 2; // seconds past the minute + + return mktime(&timeInfo); +} + +/*----------------------------------------------------------------- +FAT_GetFileCreationTime +Get the creation time of the last file found or openned. +time_t return OUT: the file's creation time +-----------------------------------------------------------------*/ +time_t FAT_GetFileCreationTime (void) +{ + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].cTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].cDate); +} + +/*----------------------------------------------------------------- +FAT_GetFileLastWriteTime +Get the creation time of the last file found or openned. +time_t return OUT: the file's creation time +-----------------------------------------------------------------*/ +time_t FAT_GetFileLastWriteTime (void) +{ + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].mTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].mDate); +} +#endif + +/*----------------------------------------------------------------- +FAT_DirEntFromPath +Finds the directory entry for a file or directory from a path +Path separator is a forward slash / +const char* path: IN null terminated string of path. +DIR_ENT return OUT: dirEntry of found file. First char will be FILE_FREE + if the file was not found +-----------------------------------------------------------------*/ +DIR_ENT FAT_DirEntFromPath (const char* path) +{ + int pathPos; + char name[MAX_FILENAME_LENGTH]; + char alias[13]; + int namePos; + bool found, notFound; + DIR_ENT dirEntry; + u32 dirCluster; + bool flagLFN, dotSeen; + + // Start at beginning of path + pathPos = 0; + + if (path[pathPos] == '/') + { + dirCluster = filesysRootDirClus; // Start at root directory + } + else + { + dirCluster = curWorkDirCluster; // Start at current working dir + } + + // Eat any slash / + while ((path[pathPos] == '/') && (path[pathPos] != '\0')) + { + pathPos++; + } + + // Search until can't continue + found = false; + notFound = false; + while (!notFound && !found) + { + flagLFN = false; + // Copy name from path + namePos = 0; + if ((path[pathPos] == '.') && ((path[pathPos + 1] == '\0') || (path[pathPos + 1] == '/'))) { + // Dot entry + name[namePos++] = '.'; + pathPos++; + } else if ((path[pathPos] == '.') && (path[pathPos + 1] == '.') && ((path[pathPos + 2] == '\0') || (path[pathPos + 2] == '/'))){ + // Double dot entry + name[namePos++] = '.'; + pathPos++; + name[namePos++] = '.'; + pathPos++; + } else { + // Copy name from path + if (path[pathPos] == '.') { + flagLFN = true; + } + dotSeen = false; + while ((namePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0') && (path[pathPos] != '/')) + { + name[namePos] = ucase(path[pathPos]); + if ((name[namePos] <= ' ') || ((name[namePos] >= ':') && (name[namePos] <= '?'))) // Invalid character + { + flagLFN = true; + } + if (name[namePos] == '.') { + if (!dotSeen) { + dotSeen = true; + } else { + flagLFN = true; + } + } + namePos++; + pathPos++; + } + // Check if a long filename was specified + if (namePos > 12) + { + flagLFN = true; + } + } + + // Add end of string char + name[namePos] = '\0'; + + // Move through path to correct place + while ((path[pathPos] != '/') && (path[pathPos] != '\0')) + pathPos++; + // Eat any slash / + while ((path[pathPos] == '/') && (path[pathPos] != '\0')) + { + pathPos++; + } + + // Search current Dir for correct entry + dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_SET); + while ( !found && !notFound) + { + // Match filename + found = true; + for (namePos = 0; (namePos < MAX_FILENAME_LENGTH) && found && (name[namePos] != '\0') && (lfnName[namePos] != '\0'); namePos++) + { + if (name[namePos] != ucase(lfnName[namePos])) + { + found = false; + } + } + if ((name[namePos] == '\0') != (lfnName[namePos] == '\0')) + { + found = false; + } + + // Check against alias as well. + if (!found) + { + FAT_GetFilename(dirEntry, alias); + found = true; + for (namePos = 0; (namePos < 13) && found && (name[namePos] != '\0') && (alias[namePos] != '\0'); namePos++) + { + if (name[namePos] != ucase(alias[namePos])) + { + found = false; + } + } + if ((name[namePos] == '\0') != (alias[namePos] == '\0')) + { + found = false; + } + } + + if (dirEntry.name[0] == FILE_FREE) + // Couldn't find specified file + { + found = false; + notFound = true; + } + if (!found && !notFound) + { + dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_CUR); + } + } + + if (found && ((dirEntry.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (path[pathPos] != '\0')) + // It has found a directory from within the path that needs to be followed + { + found = false; + dirCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16); + } + } + + if (notFound) + { + dirEntry.name[0] = FILE_FREE; + dirEntry.attrib = 0x00; + } + + return (dirEntry); +} + + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_AddDirEntry +Creates a new dir entry for a file +Path separator is a forward slash / +const char* path: IN null terminated string of path to file. +DIR_ENT newDirEntry IN: The directory entry to use. +int file IN: The file being added (optional, use -1 if not used) +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry) +{ + char filename[MAX_FILENAME_LENGTH]; + int filePos, pathPos, aliasPos; + char tempChar; + bool flagLFN, dotSeen; + char fileAlias[13] = {0}; + int tailNum; + + unsigned char chkSum = 0; + + u32 oldWorkDirCluster; + + DIR_ENT* dirEntries = (DIR_ENT*)globalBuffer; + u32 dirCluster; + int secOffset; + int entryOffset; + int maxSectors; + u32 firstSector; + + DIR_ENT_LFN lfnEntry; + int lfnPos = 0; + + int dirEntryLength = 0; + int dirEntryRemain = 0; + u32 tempDirCluster; + int tempSecOffset; + int tempEntryOffset; + bool dirEndFlag = false; + + int i; + + // Store current working directory + oldWorkDirCluster = curWorkDirCluster; + + // Find filename within path and change to correct directory + if (path[0] == '/') + { + curWorkDirCluster = filesysRootDirClus; + } + + pathPos = 0; + filePos = 0; + flagLFN = false; + + while (path[pathPos + filePos] != '\0') + { + if (path[pathPos + filePos] == '/') + { + filename[filePos] = '\0'; + if (FAT_chdir(filename) == false) + { + curWorkDirCluster = oldWorkDirCluster; + return false; // Couldn't change directory + } + pathPos += filePos + 1; + filePos = 0; + } + filename[filePos] = path[pathPos + filePos]; + filePos++; + } + + // Skip over last slashes + while (path[pathPos] == '/') + pathPos++; + + // Check if the filename has a leading "." + // If so, it is an LFN + if (path[pathPos] == '.') { + flagLFN = true; + } + + // Copy name from path + filePos = 0; + dotSeen = false; + + while ((filePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0')) + { + filename[filePos] = path[pathPos]; + if ((filename[filePos] <= ' ') || ((filename[filePos] >= ':') && (filename[filePos] <= '?'))) // Invalid character + { + flagLFN = true; + } + if (filename[filePos] == '.') { + if (!dotSeen) { + dotSeen = true; + } else { + flagLFN = true; + } + } + filePos++; + pathPos++; + if ((filePos > 8) && !dotSeen) { + flagLFN = true; + } + } + + if (filePos == 0) // No filename + { + return false; + } + + // Check if a long filename was specified + if (filePos > 12) + { + flagLFN = true; + } + + // Check if extension is > 3 characters long + if (!flagLFN && (strrchr (filename, '.') != NULL) && (strlen(strrchr(filename, '.')) > 4)) { + flagLFN = true; + } + + lfnPos = (filePos - 1) / 13; + + // Add end of string char + filename[filePos++] = '\0'; + // Clear remaining chars + while (filePos < MAX_FILENAME_LENGTH) + filename[filePos++] = 0x01; // Set for LFN compatibility + + + if (flagLFN) + { + // Generate short filename - always a 2 digit number for tail + // Get first 5 chars of alias from LFN + aliasPos = 0; + filePos = 0; + if (filename[filePos] == '.') { + filePos++; + } + for ( ; (aliasPos < 5) && (filename[filePos] != '\0') && (filename[filePos] != '.') ; filePos++) + { + tempChar = ucase(filename[filePos]); + if (((tempChar > ' ' && tempChar < ':') || tempChar > '?') && tempChar != '.') + fileAlias[aliasPos++] = tempChar; + } + // Pad Alias with underscores + while (aliasPos < 5) + fileAlias[aliasPos++] = '_'; + + fileAlias[5] = '~'; + fileAlias[8] = '.'; + fileAlias[9] = ' '; + fileAlias[10] = ' '; + fileAlias[11] = ' '; + if (strchr (filename, '.') != NULL) { + while(filename[filePos] != '\0') + { + filePos++; + if (filename[filePos] == '.') + { + pathPos = filePos; + } + } + filePos = pathPos + 1; //pathPos is used as a temporary variable + // Copy first 3 characters of extension + for (aliasPos = 9; (aliasPos < 12) && (filename[filePos] != '\0'); filePos++) + { + tempChar = ucase(filename[filePos]); + if ((tempChar > ' ' && tempChar < ':') || tempChar > '?') + fileAlias[aliasPos++] = tempChar; + } + } else { + aliasPos = 9; + } + + // Pad Alias extension with spaces + while (aliasPos < 12) + fileAlias[aliasPos++] = ' '; + + fileAlias[12] = '\0'; + + + // Get a valid tail number + tailNum = 0; + do { + tailNum++; + fileAlias[6] = 0x30 + ((tailNum / 10) % 10); // 10's digit + fileAlias[7] = 0x30 + (tailNum % 10); // 1's digit + } while ((FAT_DirEntFromPath(fileAlias).name[0] != FILE_FREE) && (tailNum < 100)); + + if (tailNum < 100) // Found an alias not being used + { + // Calculate file checksum + chkSum = 0; + for (aliasPos=0; aliasPos < 12; aliasPos++) + { + // Skip '.' + if (fileAlias[aliasPos] == '.') + aliasPos++; + // NOTE: The operation is an unsigned char rotate right + chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + fileAlias[aliasPos]; + } + } + else // Couldn't find a valid alias + { + return false; + } + + dirEntryLength = lfnPos + 2; + } + else // Its not a long file name + { + // Just copy alias straight from filename + for (aliasPos = 0; aliasPos < 13; aliasPos++) + { + tempChar = ucase(filename[aliasPos]); + if ((tempChar > ' ' && tempChar < ':') || tempChar > '?') + fileAlias[aliasPos] = tempChar; + } + fileAlias[12] = '\0'; + + lfnPos = -1; + + dirEntryLength = 1; + } + + // Change dirEntry name to match alias + for (aliasPos = 0; ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0') && (aliasPos < 8)); aliasPos++) + { + newDirEntry.name[aliasPos] = fileAlias[aliasPos]; + } + while (aliasPos < 8) + { + newDirEntry.name[aliasPos++] = ' '; + } + aliasPos = 0; + while ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0')) + aliasPos++; + filePos = 0; + while (( filePos < 3 ) && (fileAlias[aliasPos] != '\0')) + { + tempChar = fileAlias[aliasPos++]; + if ((tempChar > ' ' && tempChar < ':' && tempChar!='.') || tempChar > '?') + newDirEntry.ext[filePos++] = tempChar; + } + while (filePos < 3) + { + newDirEntry.ext[filePos++] = ' '; + } + + // Scan Dir for free entry + dirCluster = curWorkDirCluster; + secOffset = 0; + entryOffset = 0; + maxSectors = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus); + firstSector = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(dirCluster)); + disc_ReadSector (firstSector + secOffset, dirEntries); + + dirEntryRemain = dirEntryLength; + tempDirCluster = dirCluster; + tempSecOffset = secOffset; + tempEntryOffset = entryOffset; + + // Search for a large enough space to fit in new directory entry + while ((dirEntries[entryOffset].name[0] != FILE_LAST) && (dirEntryRemain > 0)) + { + + entryOffset++; + + if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT)) + { + entryOffset = 0; + secOffset++; + if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER)) + { + secOffset = 0; + if (FAT_NextCluster(dirCluster) == CLUSTER_EOF) + { + dirCluster = FAT_LinkFreeCluster(dirCluster); + dirEntries[0].name[0] = FILE_LAST; + } + else + { + dirCluster = FAT_NextCluster(dirCluster); + } + firstSector = FAT_ClustToSect(dirCluster); + } + else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir))) + { + return false; // Got to end of root dir - can't fit in more files + } + disc_ReadSector (firstSector + secOffset, dirEntries); + } + + if ((dirEntries[entryOffset].name[0] == FILE_FREE) || (dirEntries[entryOffset].name[0] == FILE_LAST) ) + { + dirEntryRemain--; + } else { + dirEntryRemain = dirEntryLength; + tempDirCluster = dirCluster; + tempSecOffset = secOffset; + tempEntryOffset = entryOffset; + } + } + + // Modifying the last directory is a special case - have to erase following entries + if (dirEntries[entryOffset].name[0] == FILE_LAST) + { + dirEndFlag = true; + } + + // Recall last used entry + dirCluster = tempDirCluster; + secOffset = tempSecOffset; + entryOffset = tempEntryOffset; + dirEntryRemain = dirEntryLength; + + // Re-read in first sector that will be written to + if (dirEndFlag && (entryOffset == 0)) { + memset (dirEntries, FILE_LAST, BYTE_PER_READ); + } else { + disc_ReadSector (firstSector + secOffset, dirEntries); + } + + // Add new directory entry + while (dirEntryRemain > 0) + { + // Move to next entry, first pass advances from last used entry + entryOffset++; + if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT)) + { + // Write out the current sector if we need to + entryOffset = 0; + if (dirEntryRemain < dirEntryLength) // Don't write out sector on first pass + { + disc_WriteSector (firstSector + secOffset, dirEntries); + } + secOffset++; + if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER)) + { + secOffset = 0; + if (FAT_NextCluster(dirCluster) == CLUSTER_EOF) + { + dirCluster = FAT_LinkFreeCluster(dirCluster); + dirEntries[0].name[0] = FILE_LAST; + } + else + { + dirCluster = FAT_NextCluster(dirCluster); + } + firstSector = FAT_ClustToSect(dirCluster); + } + else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir))) + { + return false; // Got to end of root dir - can't fit in more files + } + if (dirEndFlag) + { + memset (dirEntries, FILE_LAST, BYTE_PER_READ); + } else { + disc_ReadSector (firstSector + secOffset, dirEntries); + } + } + + // Generate LFN entries + if (lfnPos >= 0) + { + lfnEntry.ordinal = (lfnPos + 1) | (dirEntryRemain == dirEntryLength ? LFN_END : 0); + for (i = 0; i < 13; i++) { + if (filename [lfnPos * 13 + i] == 0x01) { + ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = 0xff; + ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0xff; + } else { + ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = filename [lfnPos * 13 + i]; + ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0x00; + } + } + + lfnEntry.checkSum = chkSum; + lfnEntry.flag = ATTRIB_LFN; + lfnEntry.reserved1 = 0; + lfnEntry.reserved2 = 0; + + *((DIR_ENT_LFN*)&dirEntries[entryOffset]) = lfnEntry; + lfnPos --; + lfnEntry.ordinal = 0; + } // end writing long filename entries + else + { + dirEntries[entryOffset] = newDirEntry; + if (dirEndFlag && (entryOffset < (BYTE_PER_READ / sizeof (DIR_ENT))) ) + dirEntries[entryOffset+1].name[0] = FILE_LAST; + } + + dirEntryRemain--; + } + + // Write directory back to disk + disc_WriteSector (firstSector + secOffset, dirEntries); + + // Change back to Working DIR + curWorkDirCluster = oldWorkDirCluster; + + return true; +} +#endif + +/*----------------------------------------------------------------- +FAT_FindNextFile +Gets the name of the next directory entry + (can be a file or subdirectory) +char* filename: OUT filename, must be at least 13 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindNextFile(char* filename) +{ + // Get the next directory entry + DIR_ENT file; + file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_CUR); + + if (file.name[0] == FILE_FREE) + { + return FT_NONE; // Did not find a file + } + + // Get the filename + if (filename != NULL) + FAT_GetFilename (file, filename); + + if ((file.attrib & ATTRIB_DIR) != 0) + { + return FT_DIR; // Found a directory + } + else + { + return FT_FILE; // Found a file + } +} + +/*----------------------------------------------------------------- +FAT_FindFirstFile +Gets the name of the first directory entry and resets the count + (can be a file or subdirectory) +char* filename: OUT filename, must be at least 13 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindFirstFile(char* filename) +{ + // Get the first directory entry + DIR_ENT file; + file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_SET); + + if (file.name[0] == FILE_FREE) + { + return FT_NONE; // Did not find a file + } + + // Get the filename + if (filename != NULL) + FAT_GetFilename (file, filename); + + if ((file.attrib & ATTRIB_DIR) != 0) + { + return FT_DIR; // Found a directory + } + else + { + return FT_FILE; // Found a file + } +} + +/*----------------------------------------------------------------- +FAT_FindFirstFileLFN +Gets the long file name of the first directory entry and resets + the count (can be a file or subdirectory) +char* lfn: OUT long file name, must be at least 256 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindFirstFileLFN(char* lfn) +{ + FILE_TYPE type; + type = FAT_FindFirstFile(NULL); + FAT_GetLongFilename (lfn); + return type; +} + +/*----------------------------------------------------------------- +FAT_FindNextFileLFN +Gets the long file name of the next directory entry + (can be a file or subdirectory) +char* lfn: OUT long file name, must be at least 256 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindNextFileLFN(char* lfn) +{ + FILE_TYPE type; + type = FAT_FindNextFile(NULL); + FAT_GetLongFilename (lfn); + return type; +} + + +/*----------------------------------------------------------------- +FAT_FileExists +Returns the type of file +char* filename: IN filename of the file to look for +FILE_TYPE return: OUT returns FT_NONE if there is now file with + that name, FT_FILE if it is a file and FT_DIR if it is a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FileExists(const char* filename) +{ + DIR_ENT dirEntry; + // Get the dirEntry for the path specified + dirEntry = FAT_DirEntFromPath (filename); + + if (dirEntry.name[0] == FILE_FREE) + { + return FT_NONE; + } + else if (dirEntry.attrib & ATTRIB_DIR) + { + return FT_DIR; + } + else + { + return FT_FILE; + } +} + +/*----------------------------------------------------------------- +FAT_GetFileSystemType +FS_TYPE return: OUT returns the current file system type +-----------------------------------------------------------------*/ +FS_TYPE FAT_GetFileSystemType (void) +{ + return filesysType; +} + +/*----------------------------------------------------------------- +FAT_GetFileSystemTotalSize +u32 return: OUT returns the total disk space (used + free) +-----------------------------------------------------------------*/ +u32 FAT_GetFileSystemTotalSize (void) +{ + return filesysTotalSize; +} + + + +/*----------------------------------------------------------------- +FAT_chdir +Changes the current working directory +const char* path: IN null terminated string of directory separated by + forward slashes, / is root +bool return: OUT returns true if successful +-----------------------------------------------------------------*/ +bool FAT_chdir (const char* path) +{ + DIR_ENT dir; + if (path[0] == '/' && path[1] == '\0') + { + curWorkDirCluster = filesysRootDirClus; + return true; + } + if (path[0] == '\0') // Return true if changing relative to nothing + { + return true; + } + + dir = FAT_DirEntFromPath (path); + + if (((dir.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (dir.name[0] != FILE_FREE)) + { + // Change directory + curWorkDirCluster = dir.startCluster | (dir.startClusterHigh << 16); + + // Move to correct cluster for root directory + if (curWorkDirCluster == FAT16_ROOT_DIR_CLUSTER) + { + curWorkDirCluster = filesysRootDirClus; + } + + // Reset file position in directory + wrkDirCluster = curWorkDirCluster; + wrkDirSector = 0; + wrkDirOffset = -1; + return true; + } + else + { + // Couldn't change directory - wrong path specified + return false; + } +} + +/*----------------------------------------------------------------- +FAT_fopen(filename, mode) +Opens a file +const char* path: IN null terminated string of filename and path + separated by forward slashes, / is root +const char* mode: IN mode to open file in + Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use + "b" or "t" in any mode, as all files are openned in binary mode +FAT_FILE* return: OUT handle to open file, returns NULL if the file + couldn't be openned +-----------------------------------------------------------------*/ +FAT_FILE* FAT_fopen(const char* path, const char* mode) +{ + int fileNum; + FAT_FILE* file; + DIR_ENT dirEntry; +#ifdef CAN_WRITE_TO_DISC + u32 startCluster; + int clusCount; +#endif + + char* pchTemp; + // Check that a valid mode was specified + pchTemp = strpbrk ( mode, "rRwWaA" ); + if (pchTemp == NULL) + { + return NULL; + } + if (strpbrk ( pchTemp+1, "rRwWaA" ) != NULL) + { + return NULL; + } + + // Get the dirEntry for the path specified + dirEntry = FAT_DirEntFromPath (path); + + // Check that it is not a directory + if (dirEntry.attrib & ATTRIB_DIR) + { + return NULL; + } + +#ifdef CAN_WRITE_TO_DISC + // Check that it is not a read only file being openned in a writing mode + if ( (strpbrk(mode, "wWaA+") != NULL) && (dirEntry.attrib & ATTRIB_RO)) + { + return NULL; + } +#else + if ( (strpbrk(mode, "wWaA+") != NULL)) + { + return NULL; + } +#endif + + // Find a free file buffer + for (fileNum = 0; (fileNum < MAX_FILES_OPEN) && (openFiles[fileNum].inUse == true); fileNum++); + + if (fileNum == MAX_FILES_OPEN) // No free files + { + return NULL; + } + + file = &openFiles[fileNum]; + // Remember where directory entry was + file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector; + file->dirEntOffset = wrkDirOffset; + + if ( strpbrk(mode, "rR") != NULL ) //(ucase(mode[0]) == 'R') + { + if (dirEntry.name[0] == FILE_FREE) // File must exist + { + return NULL; + } + + file->read = true; +#ifdef CAN_WRITE_TO_DISC + file->write = ( strchr(mode, '+') != NULL ); //(mode[1] == '+'); +#else + file->write = false; +#endif + file->append = false; + + // Store information about position within the file, for use + // by FAT_fread, FAT_fseek, etc. + file->firstCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16); + +#ifdef CAN_WRITE_TO_DISC + // Check if file is openned for random. If it is, and currently has no cluster, one must be + // assigned to it. + if (file->write && file->firstCluster == CLUSTER_FREE) + { + file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE); + if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster + { + return NULL; + } + + // Store cluster position into the directory entry + dirEntry.startCluster = (file->firstCluster & 0xFFFF); + dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF); + disc_ReadSector (file->dirEntSector, globalBuffer); + ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry; + disc_WriteSector (file->dirEntSector, globalBuffer); + } +#endif + + file->length = dirEntry.fileSize; + file->curPos = 0; + file->curClus = dirEntry.startCluster | (dirEntry.startClusterHigh << 16); + file->curSect = 0; + file->curByte = 0; + + // Not appending + file->appByte = 0; + file->appClus = 0; + file->appSect = 0; + + disc_ReadSector( FAT_ClustToSect( file->curClus), file->readBuffer); + file->inUse = true; // We're using this file now + + return file; + } // mode "r" + +#ifdef CAN_WRITE_TO_DISC + if ( strpbrk(mode, "wW") != NULL ) // (ucase(mode[0]) == 'W') + { + if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist + { + dirEntry.attrib = ATTRIB_ARCH; + dirEntry.reserved = 0; + + // Time and date set to system time and date + dirEntry.cTime_ms = 0; + dirEntry.cTime = getRTCtoFileTime(); + dirEntry.cDate = getRTCtoFileDate(); + dirEntry.aDate = getRTCtoFileDate(); + dirEntry.mTime = getRTCtoFileTime(); + dirEntry.mDate = getRTCtoFileDate(); + } + else // Already a file entry + { + // Free any clusters used + FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16)); + } + + // Get a cluster to use + startCluster = FAT_LinkFreeCluster (CLUSTER_FREE); + if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster + { + return NULL; + } + + // Store cluster position into the directory entry + dirEntry.startCluster = (startCluster & 0xFFFF); + dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF); + + // The file has no data in it - its over written so should be empty + dirEntry.fileSize = 0; + + if (dirEntry.name[0] == FILE_FREE) // No file + { + // Have to create a new entry + if(!FAT_AddDirEntry (path, dirEntry)) + { + return NULL; + } + // Get the newly created dirEntry + dirEntry = FAT_DirEntFromPath (path); + + // Remember where directory entry was + file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector; + file->dirEntOffset = wrkDirOffset; + } + else // Already a file + { + // Just modify the old entry + disc_ReadSector (file->dirEntSector, globalBuffer); + ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry; + disc_WriteSector (file->dirEntSector, globalBuffer); + } + + + // Now that file is created, open it + file->read = ( strchr(mode, '+') != NULL ); //(mode[1] == '+'); + file->write = true; + file->append = false; + + // Store information about position within the file, for use + // by FAT_fread, FAT_fseek, etc. + file->firstCluster = startCluster; + file->length = 0; // Should always have 0 bytes if openning in "w" mode + file->curPos = 0; + file->curClus = startCluster; + file->curSect = 0; + file->curByte = 0; + + // Not appending + file->appByte = 0; + file->appClus = 0; + file->appSect = 0; + + // Empty file, so empty read buffer + memset (file->readBuffer, 0, BYTE_PER_READ); + file->inUse = true; // We're using this file now + + return file; + } + + if ( strpbrk(mode, "aA") != NULL ) // (ucase(mode[0]) == 'A') + { + if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist + { + dirEntry.attrib = ATTRIB_ARCH; + dirEntry.reserved = 0; + + // Time and date set to system time and date + dirEntry.cTime_ms = 0; + dirEntry.cTime = getRTCtoFileTime(); + dirEntry.cDate = getRTCtoFileDate(); + dirEntry.aDate = getRTCtoFileDate(); + dirEntry.mTime = getRTCtoFileTime(); + dirEntry.mDate = getRTCtoFileDate(); + + // The file has no data in it + dirEntry.fileSize = 0; + + // Get a cluster to use + startCluster = FAT_LinkFreeCluster (CLUSTER_FREE); + if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster + { + return NULL; + } + dirEntry.startCluster = (startCluster & 0xFFFF); + dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF); + + if(!FAT_AddDirEntry (path, dirEntry)) + return NULL; + + // Get the newly created dirEntry + dirEntry = FAT_DirEntFromPath (path); + + // Store append cluster + file->appClus = startCluster; + + // Remember where directory entry was + file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector; + file->dirEntOffset = wrkDirOffset; + } + else // File already exists - reuse the old directory entry + { + startCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16); + // If it currently has no cluster, one must be assigned to it. + if (startCluster == CLUSTER_FREE) + { + file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE); + if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster + { + return NULL; + } + + // Store cluster position into the directory entry + dirEntry.startCluster = (file->firstCluster & 0xFFFF); + dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF); + disc_ReadSector (file->dirEntSector, globalBuffer); + ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry; + disc_WriteSector (file->dirEntSector, globalBuffer); + + // Store append cluster + file->appClus = startCluster; + + } else { + + // Follow cluster list until last one is found + clusCount = dirEntry.fileSize / filesysBytePerClus; + file->appClus = startCluster; + while ((clusCount--) && (FAT_NextCluster (file->appClus) != CLUSTER_FREE) && (FAT_NextCluster (file->appClus) != CLUSTER_EOF)) + { + file->appClus = FAT_NextCluster (file->appClus); + } + if (clusCount >= 0) // Check if ran out of clusters + { + // Set flag to allocate new cluster when needed + file->appSect = filesysSecPerClus; + file->appByte = 0; + } + } + } + + // Now that file is created, open it + file->read = ( strchr(mode, '+') != NULL ); + file->write = false; + file->append = true; + + // Calculate the sector and byte of the current position, + // and store them + file->appSect = (dirEntry.fileSize % filesysBytePerClus) / BYTE_PER_READ; + file->appByte = dirEntry.fileSize % BYTE_PER_READ; + + // Store information about position within the file, for use + // by FAT_fread, FAT_fseek, etc. + file->firstCluster = startCluster; + file->length = dirEntry.fileSize; + file->curPos = dirEntry.fileSize; + file->curClus = file->appClus; + file->curSect = file->appSect; + file->curByte = file->appByte; + + // Read into buffer + disc_ReadSector( FAT_ClustToSect(file->curClus) + file->curSect, file->readBuffer); + file->inUse = true; // We're using this file now + return file; + } +#endif + + // Can only reach here if a bad mode was specified + return NULL; +} + +/*----------------------------------------------------------------- +FAT_fclose(file) +Closes a file +FAT_FILE* file: IN handle of the file to close +bool return OUT: true if successful, false if not +-----------------------------------------------------------------*/ +bool FAT_fclose (FAT_FILE* file) +{ + // Clear memory used by file information + if ((file != NULL) && (file->inUse == true)) + { +#ifdef CAN_WRITE_TO_DISC + if (file->write || file->append) + { + // Write new length, time and date back to directory entry + disc_ReadSector (file->dirEntSector, globalBuffer); + + ((DIR_ENT*)globalBuffer)[file->dirEntOffset].fileSize = file->length; + ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mTime = getRTCtoFileTime(); + ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mDate = getRTCtoFileDate(); + ((DIR_ENT*)globalBuffer)[file->dirEntOffset].aDate = getRTCtoFileDate(); + + disc_WriteSector (file->dirEntSector, globalBuffer); + + // Flush any sectors in disc cache + disc_CacheFlush(); + } +#endif + file->inUse = false; + return true; + } + else + { + return false; + } +} + +/*----------------------------------------------------------------- +FAT_ftell(file) +Returns the current position in a file +FAT_FILE* file: IN handle of an open file +u32 OUT: Current position +-----------------------------------------------------------------*/ +u32 FAT_ftell (FAT_FILE* file) +{ + // Return the position as specified in the FAT_FILE structure + if ((file != NULL) && (file->inUse == true)) + { + return file->curPos; + } + else + { + // Return -1 if no file was given + return -1; + } +} + +/*----------------------------------------------------------------- +FAT_fseek(file, offset, origin) +Seeks to specified byte position in file +FAT_FILE* file: IN handle of an open file +s32 offset IN: position to seek to, relative to origin +int origin IN: origin to seek from +int OUT: Returns 0 if successful, -1 if not +-----------------------------------------------------------------*/ +int FAT_fseek(FAT_FILE* file, s32 offset, int origin) +{ + u32 cluster, nextCluster; + int clusCount; + u32 position; + u32 curPos; + + if ((file == NULL) || (file->inUse == false)) // invalid file + { + return -1; + } + + // Can't seek in append only mode + if (!file->read && !file->write) + { + return -1; + } + + curPos = file->curPos; + + switch (origin) + { + case SEEK_SET: + if (offset >= 0) + { + position = offset; + } else { + // Tried to seek before start of file + position = 0; + } + break; + case SEEK_CUR: + if (offset >= 0) + { + position = curPos + offset; + } + else if ( (u32)(offset * -1) >= curPos ) + { + // Tried to seek before start of file + position = 0; + } + else + { + // Using u32 to maintain 32 bits of accuracy + position = curPos - (u32)(offset * -1); + } + break; + case SEEK_END: + if (offset >= 0) + { + // Seeking to end of file + position = file->length; // Fixed thanks to MoonLight + } + else if ( (u32)(offset * -1) >= file->length ) + { + // Tried to seek before start of file + position = 0; + } + else + { + // Using u32 to maintain 32 bits of accuracy + position = file->length - (u32)(offset * -1); + } + break; + default: + return -1; + } + + if (position > file->length) + { + // Tried to go past end of file + position = file->length; + } + + // Save position + file->curPos = position; + + + // Calculate where the correct cluster is + if (position > curPos) + { + clusCount = (position - curPos + (file->curSect * filesysBytePerSec) + file->curByte) / filesysBytePerClus; // Fixed thanks to AgentQ + cluster = file->curClus; + } else { + clusCount = position / filesysBytePerClus; + cluster = file->firstCluster; + } + + // Calculate the sector and byte of the current position, + // and store them + file->curSect = (position % filesysBytePerClus) / BYTE_PER_READ; + file->curByte = position % BYTE_PER_READ; + + // Follow cluster list until desired one is found + if (clusCount > 0) // Only look at next cluster if need to + { + nextCluster = FAT_NextCluster (cluster); + } else { + nextCluster = cluster; + } + while ((clusCount--) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) + { + cluster = nextCluster; + nextCluster = FAT_NextCluster (cluster); + } + // Check if ran out of clusters, and the file is being written to + if ((clusCount >= 0) && (file->write || file->append)) + { + // Set flag to allocate a new cluster + file->curSect = filesysSecPerClus; + file->curByte = 0; + } + file->curClus = cluster; + + // Reload sector buffer for new position in file, if it is a different sector + if ((curPos ^ position) >= BYTE_PER_READ) + { + disc_ReadSector( file->curSect + FAT_ClustToSect(file->curClus), file->readBuffer); + } + + return 0; +} + +/*----------------------------------------------------------------- +FAT_fread(buffer, size, count, file) +Reads in size * count bytes into buffer from file, starting + from current position. It then sets the current position to the + byte after the last byte read. If it reaches the end of file + before filling the buffer then it stops reading. +void* buffer OUT: Pointer to buffer to fill. Should be at least as + big as the number of bytes required +u32 size IN: size of each item to read +u32 count IN: number of items to read +FAT_FILE* file IN: Handle of an open file +u32 OUT: returns the actual number of bytes read +-----------------------------------------------------------------*/ +u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file) +{ + int curByte; + int curSect; + u32 curClus; + u32 tempNextCluster; + + int tempVar; + + char* data = (char*)buffer; + + u32 length = size * count; + u32 remain; + + bool flagNoError = true; + + // Can't read non-existant files + if ((file == NULL) || (file->inUse == false) || size == 0 || count == 0 || buffer == NULL) + return 0; + + // Can only read files openned for reading + if (!file->read) + return 0; + + // Don't read past end of file + if (length + file->curPos > file->length) + length = file->length - file->curPos; + + remain = length; + + curByte = file->curByte; + curSect = file->curSect; + curClus = file->curClus; + + // Align to sector + tempVar = BYTE_PER_READ - curByte; + if (tempVar > remain) + tempVar = remain; + + if ((tempVar < BYTE_PER_READ) && flagNoError) + { + memcpy(data, &(file->readBuffer[curByte]), tempVar); + remain -= tempVar; + data += tempVar; + + curByte += tempVar; + if (curByte >= BYTE_PER_READ) + { + curByte = 0; + curSect++; + } + } + + // align to cluster + // tempVar is number of sectors to read + if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ) + { + tempVar = filesysSecPerClus - curSect; + } else { + tempVar = remain / BYTE_PER_READ; + } + + if ((tempVar > 0) && flagNoError) + { + disc_ReadSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data); + data += tempVar * BYTE_PER_READ; + remain -= tempVar * BYTE_PER_READ; + + curSect += tempVar; + } + + // Move onto next cluster + // It should get to here without reading anything if a cluster is due to be allocated + if (curSect >= filesysSecPerClus) + { + tempNextCluster = FAT_NextCluster(curClus); + if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) + { + curSect = filesysSecPerClus; + } else { + curSect = 0; + curClus = tempNextCluster; + if (curClus == CLUSTER_FREE) + { + flagNoError = false; + } + } + } + + // Read in whole clusters + while ((remain >= filesysBytePerClus) && flagNoError) + { + disc_ReadSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data); + data += filesysBytePerClus; + remain -= filesysBytePerClus; + + // Advance to next cluster + tempNextCluster = FAT_NextCluster(curClus); + if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) + { + curSect = filesysSecPerClus; + } else { + curSect = 0; + curClus = tempNextCluster; + if (curClus == CLUSTER_FREE) + { + flagNoError = false; + } + } + } + + // Read remaining sectors + tempVar = remain / BYTE_PER_READ; // Number of sectors left + if ((tempVar > 0) && flagNoError) + { + disc_ReadSectors (FAT_ClustToSect(curClus), tempVar, data); + data += tempVar * BYTE_PER_READ; + remain -= tempVar * BYTE_PER_READ; + curSect += tempVar; + } + + // Last remaining sector + // Check if sector wanted is different to the one started with + if ( ((file->curByte + length) >= BYTE_PER_READ) && flagNoError) + { + disc_ReadSector( curSect + FAT_ClustToSect( curClus), file->readBuffer); + if (remain > 0) + { + memcpy(data, file->readBuffer, remain); + curByte += remain; + remain = 0; + } + } + + // Length read is the wanted length minus the stuff not read + length = length - remain; + + // Update file information + file->curByte = curByte; + file->curSect = curSect; + file->curClus = curClus; + file->curPos = file->curPos + length; + return length; +} + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fwrite(buffer, size, count, file) +Writes size * count bytes into file from buffer, starting + from current position. It then sets the current position to the + byte after the last byte written. If the file was openned in + append mode it always writes to the end of the file. +const void* buffer IN: Pointer to buffer containing data. Should be + at least as big as the number of bytes to be written. +u32 size IN: size of each item to write +u32 count IN: number of items to write +FAT_FILE* file IN: Handle of an open file +u32 OUT: returns the actual number of bytes written +-----------------------------------------------------------------*/ +u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file) +{ + int curByte; + int curSect; + u32 curClus; + + u32 tempNextCluster; + int tempVar; + u32 length = size * count; + u32 remain = length; + char* data = (char*)buffer; + + char* writeBuffer; + + bool flagNoError = true; + bool flagAppending = false; + + if ((file == NULL) || (file->inUse == false) || length == 0 || buffer == NULL) + return 0; + + if (file->write) + { + // Write at current read pointer + curByte = file->curByte; + curSect = file->curSect; + curClus = file->curClus; + + // Use read buffer as write buffer + writeBuffer = file->readBuffer; + + // If it is writing past the current end of file, set appending flag + if (length + file->curPos > file->length) + { + flagAppending = true; + } + } + else if (file->append) + { + // Write at end of file + curByte = file->appByte; + curSect = file->appSect; + curClus = file->appClus; + flagAppending = true; + + // Use global buffer as write buffer, don't touch read buffer + writeBuffer = (char*)globalBuffer; + disc_ReadSector(curSect + FAT_ClustToSect(curClus), writeBuffer); + } + else + { + return 0; + } + + // Move onto next cluster if needed + if (curSect >= filesysSecPerClus) + { + curSect = 0; + tempNextCluster = FAT_NextCluster(curClus); + if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) + { + // Ran out of clusters so get a new one + curClus = FAT_LinkFreeCluster(curClus); + if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort + { + flagNoError = false; + } + memset(writeBuffer, 0, BYTE_PER_READ); + } else { + curClus = tempNextCluster; + disc_ReadSector( FAT_ClustToSect( curClus), writeBuffer); + } + } + + // Align to sector + tempVar = BYTE_PER_READ - curByte; + if (tempVar > remain) + tempVar = remain; + + if ((tempVar < BYTE_PER_READ) && flagNoError) + { + memcpy(&(writeBuffer[curByte]), data, tempVar); + remain -= tempVar; + data += tempVar; + curByte += tempVar; + + // Write buffer back to disk + disc_WriteSector (curSect + FAT_ClustToSect(curClus), writeBuffer); + + // Move onto next sector + if (curByte >= BYTE_PER_READ) + { + curByte = 0; + curSect++; + } + } + + // Align to cluster + // tempVar is number of sectors to write + if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ) + { + tempVar = filesysSecPerClus - curSect; + } else { + tempVar = remain / BYTE_PER_READ; + } + + if ((tempVar > 0) && flagNoError) + { + disc_WriteSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data); + data += tempVar * BYTE_PER_READ; + remain -= tempVar * BYTE_PER_READ; + curSect += tempVar; + } + + if (((curSect >= filesysSecPerClus) && flagNoError) && (remain > 0)) + { + curSect = 0; + tempNextCluster = FAT_NextCluster(curClus); + if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) + { + // Ran out of clusters so get a new one + curClus = FAT_LinkFreeCluster(curClus); + if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort + { + flagNoError = false; + } + } else { + curClus = tempNextCluster; + } + } + + // Write whole clusters + while ((remain >= filesysBytePerClus) && flagNoError) + { + disc_WriteSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data); + data += filesysBytePerClus; + remain -= filesysBytePerClus; + if (remain > 0) + { + tempNextCluster = FAT_NextCluster(curClus); + if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) + { + // Ran out of clusters so get a new one + curClus = FAT_LinkFreeCluster(curClus); + if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort + { + flagNoError = false; + break; + } + } else { + curClus = tempNextCluster; + } + } else { + // Allocate a new cluster when next writing the file + curSect = filesysSecPerClus; + } + } + + // Write remaining sectors + tempVar = remain / BYTE_PER_READ; // Number of sectors left + if ((tempVar > 0) && flagNoError) + { + disc_WriteSectors (FAT_ClustToSect(curClus), tempVar, data); + data += tempVar * BYTE_PER_READ; + remain -= tempVar * BYTE_PER_READ; + curSect += tempVar; + } + + // Last remaining sector + // Check if sector wanted is different to the one started with + if ( (( (file->append ? file->appByte : file->curByte) + length) >= BYTE_PER_READ) && flagNoError) + { + if (flagAppending) + { + // Zero sector before using it + memset (writeBuffer, 0, BYTE_PER_READ); + } else { + // Modify existing sector + disc_ReadSector( curSect + FAT_ClustToSect( curClus), writeBuffer); + } + if (remain > 0) { + memcpy(writeBuffer, data, remain); + curByte += remain; + remain = 0; + disc_WriteSector( curSect + FAT_ClustToSect( curClus), writeBuffer); + } + } + + // Amount read is the originally requested amount minus stuff remaining + length = length - remain; + + // Update file information + if (file->write) // Writing also shifts the read pointer + { + file->curByte = curByte; + file->curSect = curSect; + file->curClus = curClus; + file->curPos = file->curPos + length; + if (file->length < file->curPos) + { + file->length = file->curPos; + } + } + else if (file->append) // Appending doesn't affect the read pointer + { + file->appByte = curByte; + file->appSect = curSect; + file->appClus = curClus; + file->length = file->length + length; + } + + return length; +} +#endif + + +/*----------------------------------------------------------------- +FAT_feof(file) +Returns true if the end of file has been reached +FAT_FILE* file IN: Handle of an open file +bool return OUT: true if EOF, false if not +-----------------------------------------------------------------*/ +bool FAT_feof(FAT_FILE* file) +{ + if ((file == NULL) || (file->inUse == false)) + return true; // Return eof on invalid files + + return (file->length == file->curPos); +} + + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_remove (path) +Deletes the file or empty directory sepecified in path +const char* path IN: Path of item to delete +int return OUT: zero if successful, non-zero if not +-----------------------------------------------------------------*/ +int FAT_remove (const char* path) +{ + DIR_ENT dirEntry; + u32 oldWorkDirCluster; + char checkFilename[13]; + FILE_TYPE checkFiletype; + + dirEntry = FAT_DirEntFromPath (path); + + if (dirEntry.name[0] == FILE_FREE) + { + return -1; + } + + // Only delete directories if the directory is entry + if (dirEntry.attrib & ATTRIB_DIR) + { + // Change to the directory temporarily + oldWorkDirCluster = curWorkDirCluster; + FAT_chdir(path); + + // Search for files or directories, excluding the . and .. entries + checkFiletype = FAT_FindFirstFile (checkFilename); + while ((checkFilename[0] == '.') && (checkFiletype != FT_NONE)) + { + checkFiletype = FAT_FindNextFile (checkFilename); + } + + // Change back to working directory + curWorkDirCluster = oldWorkDirCluster; + + // Check that the directory is empty + if (checkFiletype != FT_NONE) + { + // Directory isn't empty + return -1; + } + } + + // Refresh directory information + dirEntry = FAT_DirEntFromPath (path); + + // Free any clusters used + FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16)); + + // Remove Directory entry + disc_ReadSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer); + ((DIR_ENT*)globalBuffer)[wrkDirOffset].name[0] = FILE_FREE; + disc_WriteSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer); + + // Flush any sectors in disc cache + disc_CacheFlush(); + + return 0; +} +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_mkdir (path) +Makes a new directory, so long as no other directory or file has + the same name. +const char* path IN: Path and filename of directory to make +int return OUT: zero if successful, non-zero if not +-----------------------------------------------------------------*/ +int FAT_mkdir (const char* path) +{ + u32 newDirCluster; + u32 parentDirCluster; + DIR_ENT dirEntry; + DIR_ENT* entries = (DIR_ENT*)globalBuffer; + int i; + + int pathPos, filePos; + char pathname[MAX_FILENAME_LENGTH]; + u32 oldDirCluster; + + if (FAT_FileExists(path) != FT_NONE) + { + return -1; // File or directory exists with that name + } + + // Find filename within path and change to that directory + oldDirCluster = curWorkDirCluster; + if (path[0] == '/') + { + curWorkDirCluster = filesysRootDirClus; + } + + pathPos = 0; + filePos = 0; + + while (path[pathPos + filePos] != '\0') + { + if (path[pathPos + filePos] == '/') + { + pathname[filePos] = '\0'; + if (FAT_chdir(pathname) == false) + { + curWorkDirCluster = oldDirCluster; + return -1; // Couldn't change directory + } + pathPos += filePos + 1; + filePos = 0; + } + pathname[filePos] = path[pathPos + filePos]; + filePos++; + } + + // Now grab the parent directory's cluster + parentDirCluster = curWorkDirCluster; + curWorkDirCluster = oldDirCluster; + + // Get a new cluster for the file + newDirCluster = FAT_LinkFreeCluster(CLUSTER_FREE); + + if (newDirCluster == CLUSTER_FREE) + { + return -1; // Couldn't get a new cluster for the directory + } + // Fill in directory entry's information + dirEntry.attrib = ATTRIB_DIR; + dirEntry.reserved = 0; + // Time and date set to system time and date + dirEntry.cTime_ms = 0; + dirEntry.cTime = getRTCtoFileTime(); + dirEntry.cDate = getRTCtoFileDate(); + dirEntry.aDate = getRTCtoFileDate(); + dirEntry.mTime = getRTCtoFileTime(); + dirEntry.mDate = getRTCtoFileDate(); + // Store cluster position into the directory entry + dirEntry.startCluster = (newDirCluster & 0xFFFF); + dirEntry.startClusterHigh = ((newDirCluster >> 16) & 0xFFFF); + // The file has no data in it - its over written so should be empty + dirEntry.fileSize = 0; + + if (FAT_AddDirEntry (path, dirEntry) == false) + { + return -1; // Couldn't add the directory entry + } + + // Create the new directory itself + memset(entries, FILE_LAST, BYTE_PER_READ); + + // Create . directory entry + dirEntry.name[0] = '.'; + // Fill name and extension with spaces + for (i = 1; i < 11; i++) + { + dirEntry.name[i] = ' '; + } + + memcpy(entries, &dirEntry, sizeof(dirEntry)); + + // Create .. directory entry + dirEntry.name[1] = '.'; + dirEntry.startCluster = (parentDirCluster & 0xFFFF); + dirEntry.startClusterHigh = ((parentDirCluster >> 16) & 0xFFFF); + + memcpy(&entries[1], &dirEntry, sizeof(dirEntry)); + + // Write entry to disc + disc_WriteSector(FAT_ClustToSect(newDirCluster), entries); + + // Flush any sectors in disc cache + disc_CacheFlush(); + return 0; +} +#endif + +/*----------------------------------------------------------------- +FAT_fgetc (handle) +Gets the next character in the file +FAT_FILE* file IN: Handle of open file +bool return OUT: character if successful, EOF if not +-----------------------------------------------------------------*/ +char FAT_fgetc (FAT_FILE* file) +{ + char c; + return (FAT_fread(&c, 1, 1, file) == 1) ? c : EOF; +} + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fputc (character, handle) +Writes the given character into the file +char c IN: Character to be written +FAT_FILE* file IN: Handle of open file +bool return OUT: character if successful, EOF if not +-----------------------------------------------------------------*/ +char FAT_fputc (char c, FAT_FILE* file) +{ + return (FAT_fwrite(&c, 1, 1, file) == 1) ? c : EOF; +} +#endif + +/*----------------------------------------------------------------- +FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file) +Gets a up to num bytes from file, stopping at the first + newline. + +CAUTION: does not do strictly streaming. I.e. it's + reading more then needed bytes and seeking back. + shouldn't matter for random access + +char *tgtBuffer OUT: buffer to write to +int num IN: size of target buffer +FAT_FILE* file IN: Handle of open file +bool return OUT: character if successful, EOF if not + + Written by MightyMax + Modified by Chishm - 2005-11-17 + * Added check for unix style text files + * Removed seek when no newline is found, since it isn't necessary +-------------------------------------------------------------------*/ +char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file) +{ + u32 curPos; + u32 readLength; + char *returnChar; + + // invalid filehandle + if (file == NULL) + { + return NULL ; + } + + // end of file + if (FAT_feof(file)==true) + { + return NULL ; + } + + // save current position + curPos = FAT_ftell(file); + + // read the full buffer (max string chars is num-1 and one end of string \0 + readLength = FAT_fread(tgtBuffer,1,num-1,file) ; + + // mark least possible end of string + tgtBuffer[readLength] = '\0' ; + + if (readLength==0) { + // return error + return NULL ; + } + + // get position of first return '\r' + returnChar = strchr(tgtBuffer,'\r'); + + // if no return is found, search for a newline + if (returnChar == NULL) + { + returnChar = strchr(tgtBuffer,'\n'); + } + + // Mark the return, if existant, as end of line/string + if (returnChar!=NULL) { + *returnChar++ = 0 ; + if (*returnChar=='\n') { // catch newline too when jumping over the end + // return to location after \r\n (strlen+2) + FAT_fseek(file,curPos+strlen(tgtBuffer)+2,SEEK_SET) ; + return tgtBuffer ; + } else { + // return to location after \r (strlen+1) + FAT_fseek(file,curPos+strlen(tgtBuffer)+1,SEEK_SET) ; + return tgtBuffer ; + } + } + + return tgtBuffer ; +} + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fputs (const char *string, FAT_FILE* file) +Writes string to file, excluding end of string character +const char *string IN: string to write +FAT_FILE* file IN: Handle of open file +bool return OUT: number of characters written if successful, + EOF if not + + Written by MightyMax + Modified by Chishm - 2005-11-17 + * Uses FAT_FILE instead of int + * writtenBytes is now u32 instead of int +-------------------------------------------------------------------*/ +int FAT_fputs (const char *string, FAT_FILE* file) +{ + u32 writtenBytes; + // save string except end of string '\0' + writtenBytes = FAT_fwrite((void *)string, 1, strlen(string), file); + + // check if we had an error + if (writtenBytes != strlen(string)) + { + // return EOF error + return EOF; + } + + // return the charcount written + return writtenBytes ; +} +#endif + + + +/* + gba_nds_fat.c + By chishm (Michael Chisholm) + + Routines for reading a compact flash card + using the GBA Movie Player or M3. + + Some FAT routines are based on those in fat.c, which + is part of avrlib by Pascal Stang. + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +//--------------------------------------------------------------- +// Includes + +#include "gba_nds_fat.h" +#include "disc_io.h" +#include +#ifdef NDS + #include // Time on the NDS +#endif +//---------------------------------------------------------------- +// Data types +#ifndef NULL + #define NULL 0 +#endif + +//---------------------------------------------------------------- +// NDS memory access control register +#ifdef NDS + #ifndef WAIT_CR + #define WAIT_CR (*(vu16*)0x04000204) + #endif +#endif + +//--------------------------------------------------------------- +// Appropriate placement of CF functions and data +#ifdef NDS + #define _VARS_IN_RAM +#else + #define _VARS_IN_RAM __attribute__ ((section (".sbss"))) +#endif + + +//----------------------------------------------------------------- +// FAT constants +#define CLUSTER_EOF_16 0xFFFF +#define CLUSTER_EOF 0x0FFFFFFF +#define CLUSTER_FREE 0x0000 +#define CLUSTER_FIRST 0x0002 + +#define FILE_LAST 0x00 +#define FILE_FREE 0xE5 + +#define FAT16_ROOT_DIR_CLUSTER 0x00 + + +//----------------------------------------------------------------- +// long file name constants +#define LFN_END 0x40 +#define LFN_DEL 0x80 + +//----------------------------------------------------------------- +// Data Structures + +// Take care of packing for GCC - it doesn't obey pragma pack() +// properly for ARM targets. +#ifdef __GNUC__ + #define __PACKED __attribute__ ((__packed__)) +#else + #define __PACKED + #pragma pack(1) +#endif + +// Boot Sector - must be packed +typedef struct +{ + u8 jmpBoot[3]; + u8 OEMName[8]; + // BIOS Parameter Block + u16 bytesPerSector; + u8 sectorsPerCluster; + u16 reservedSectors; + u8 numFATs; + u16 rootEntries; + u16 numSectorsSmall; + u8 mediaDesc; + u16 sectorsPerFAT; + u16 sectorsPerTrk; + u16 numHeads; + u32 numHiddenSectors; + u32 numSectors; + union // Different types of extended BIOS Parameter Block for FAT16 and FAT32 + { + struct + { + // Ext BIOS Parameter Block for FAT16 + u8 driveNumber; + u8 reserved1; + u8 extBootSig; + u32 volumeID; + u8 volumeLabel[11]; + u8 fileSysType[8]; + // Bootcode + u8 bootCode[448]; + } __PACKED fat16; + struct + { + // FAT32 extended block + u32 sectorsPerFAT32; + u16 extFlags; + u16 fsVer; + u32 rootClus; + u16 fsInfo; + u16 bkBootSec; + u8 reserved[12]; + // Ext BIOS Parameter Block for FAT16 + u8 driveNumber; + u8 reserved1; + u8 extBootSig; + u32 volumeID; + u8 volumeLabel[11]; + u8 fileSysType[8]; + // Bootcode + u8 bootCode[420]; + } __PACKED fat32; + } __PACKED extBlock; + + u16 bootSig; + +} __PACKED BOOT_SEC; + +// Directory entry - must be packed +typedef struct +{ + u8 name[8]; + u8 ext[3]; + u8 attrib; + u8 reserved; + u8 cTime_ms; + u16 cTime; + u16 cDate; + u16 aDate; + u16 startClusterHigh; + u16 mTime; + u16 mDate; + u16 startCluster; + u32 fileSize; +} __PACKED DIR_ENT; + +// Long file name directory entry - must be packed +typedef struct +{ + u8 ordinal; // Position within LFN + u16 char0; + u16 char1; + u16 char2; + u16 char3; + u16 char4; + u8 flag; // Should be equal to ATTRIB_LFN + u8 reserved1; // Always 0x00 + u8 checkSum; // Checksum of short file name (alias) + u16 char5; + u16 char6; + u16 char7; + u16 char8; + u16 char9; + u16 char10; + u16 reserved2; // Always 0x0000 + u16 char11; + u16 char12; +} __PACKED DIR_ENT_LFN; + +const char lfn_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E}; + +// End of packed structs +#ifdef __PACKED + #undef __PACKED +#endif +#ifndef __GNUC__ + #pragma pack() +#endif + +//----------------------------------------------------------------- +// Global Variables + +// _VARS_IN_RAM variables are stored in the largest section of WRAM +// available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA + +// Files +_VARS_IN_RAM FAT_FILE openFiles[MAX_FILES_OPEN]; + +// Long File names +_VARS_IN_RAM char lfnName[MAX_FILENAME_LENGTH]; +bool lfnExists; + +// Locations on card +int filesysRootDir; +int filesysRootDirClus; +int filesysFAT; +int filesysSecPerFAT; +int filesysNumSec; +int filesysData; +int filesysBytePerSec; +int filesysSecPerClus; +int filesysBytePerClus; + +FS_TYPE filesysType = FS_UNKNOWN; +u32 filesysTotalSize; + +// Info about FAT +u32 fatLastCluster; +u32 fatFirstFree; + +// fatBuffer used to reduce wear on the CF card from multiple writes +_VARS_IN_RAM char fatBuffer[BYTE_PER_READ]; +u32 fatBufferCurSector; + +// Current working directory +u32 curWorkDirCluster; + +// Position of the directory entry last retreived with FAT_GetDirEntry +u32 wrkDirCluster; +int wrkDirSector; +int wrkDirOffset; + +// Global sector buffer to save on stack space +_VARS_IN_RAM unsigned char globalBuffer[BYTE_PER_READ]; + +//----------------------------------------------------------------- +// Functions contained in this file - predeclarations +char ucase (char character); +u16 getRTCtoFileTime (void); +u16 getRTCtoFileDate (void); + +bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry); +bool FAT_ClearLinks (u32 cluster); +DIR_ENT FAT_DirEntFromPath (const char* path); +u32 FAT_FirstFreeCluster(void); +DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin); +u32 FAT_LinkFreeCluster(u32 cluster); +u32 FAT_NextCluster(u32 cluster); +bool FAT_WriteFatEntry (u32 cluster, u32 value); +bool FAT_GetFilename (DIR_ENT dirEntry, char* alias); + +bool FAT_InitFiles (void); +bool FAT_FreeFiles (void); +int FAT_remove (const char* path); +bool FAT_chdir (const char* path); +FILE_TYPE FAT_FindFirstFile (char* filename); +FILE_TYPE FAT_FindNextFile (char* filename); +FILE_TYPE FAT_FileExists (const char* filename); +bool FAT_GetAlias (char* alias); +bool FAT_GetLongFilename (char* filename); +u32 FAT_GetFileSize (void); +u32 FAT_GetFileCluster (void); + +FAT_FILE* FAT_fopen(const char* path, const char* mode); +bool FAT_fclose (FAT_FILE* file); +bool FAT_feof(FAT_FILE* file); +int FAT_fseek(FAT_FILE* file, s32 offset, int origin); +u32 FAT_ftell (FAT_FILE* file); +u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file); +u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file); +char FAT_fgetc (FAT_FILE* file); +char FAT_fputc (char c, FAT_FILE* file); + +/*----------------------------------------------------------------- +ucase +Returns the uppercase version of the given char +char IN: a character +char return OUT: uppercase version of character +-----------------------------------------------------------------*/ +char ucase (char character) +{ + if ((character > 0x60) && (character < 0x7B)) + character = character - 0x20; + return (character); +} + + +/*----------------------------------------------------------------- +getRTCtoFileTime and getRTCtoFileDate +Returns the time / date in Dir Entry styled format +u16 return OUT: time / date in Dir Entry styled format +-----------------------------------------------------------------*/ +u16 getRTCtoFileTime (void) +{ +#ifdef NDS + return ( + ( ( (IPC->rtc_hours > 11 ? IPC->rtc_hours - 40 : IPC->rtc_hours) & 0x1F) << 11) | + ( (IPC->rtc_minutes & 0x3F) << 5) | + ( (IPC->rtc_seconds >> 1) & 0x1F) ); +#else + return 0; +#endif +} + +u16 getRTCtoFileDate (void) +{ +#ifdef NDS + return ( + ( ((IPC->rtc_year + 20) & 0x7F) <<9) | + ( (IPC->rtc_month & 0xF) << 5) | + (IPC->rtc_day & 0x1F) ); +#else + return 0; +#endif +} + + +/*----------------------------------------------------------------- +Disc level FAT routines +-----------------------------------------------------------------*/ +#define FAT_ClustToSect(m) \ + (((m-2) * filesysSecPerClus) + filesysData) + +/*----------------------------------------------------------------- +FAT_NextCluster +Internal function - gets the cluster linked from input cluster +-----------------------------------------------------------------*/ +u32 FAT_NextCluster(u32 cluster) +{ + u32 nextCluster = CLUSTER_FREE; + u32 sector; + int offset; + + switch (filesysType) + { + case FS_UNKNOWN: + nextCluster = CLUSTER_FREE; + break; + + case FS_FAT12: + sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ); + offset = ((cluster * 3) / 2) % BYTE_PER_READ; + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + nextCluster = ((u8*)fatBuffer)[offset]; + offset++; + + if (offset >= BYTE_PER_READ) { + offset = 0; + fatBufferCurSector++; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + nextCluster |= (((u8*)fatBuffer)[offset]) << 8; + + if (cluster & 0x01) { + nextCluster = nextCluster >> 4; + } else { + nextCluster &= 0x0FFF; + } + + if (nextCluster >= 0x0FF7) + { + nextCluster = CLUSTER_EOF; + } + + break; + + case FS_FAT16: + sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 1); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // read the nextCluster value + nextCluster = ((u16*)fatBuffer)[offset]; + + if (nextCluster >= 0xFFF7) + { + nextCluster = CLUSTER_EOF; + } + break; + + case FS_FAT32: + sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 2); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // read the nextCluster value + nextCluster = (((u32*)fatBuffer)[offset]) & 0x0FFFFFFF; + + if (nextCluster >= 0x0FFFFFF7) + { + nextCluster = CLUSTER_EOF; + } + break; + + default: + nextCluster = CLUSTER_FREE; + break; + } + + return nextCluster; +} + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_WriteFatEntry +Internal function - writes FAT information about a cluster +-----------------------------------------------------------------*/ +bool FAT_WriteFatEntry (u32 cluster, u32 value) +{ + u32 sector; + int offset; + + if ((cluster < 0x0002) || (cluster > fatLastCluster)) + { + return false; + } + + switch (filesysType) + { + case FS_UNKNOWN: + return false; + break; + + case FS_FAT12: + sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ); + offset = ((cluster * 3) / 2) % BYTE_PER_READ; + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + if (cluster & 0x01) { + + ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4); + + offset++; + if (offset >= BYTE_PER_READ) { + offset = 0; + // write the buffer back to disc + disc_WriteSector(fatBufferCurSector, fatBuffer); + // read the next sector + fatBufferCurSector++; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4; + + } else { + + ((u8*)fatBuffer)[offset] = value & 0xFF; + + offset++; + if (offset >= BYTE_PER_READ) { + offset = 0; + // write the buffer back to disc + disc_WriteSector(fatBufferCurSector, fatBuffer); + // read the next sector + fatBufferCurSector++; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F); + } + + break; + + case FS_FAT16: + sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 1); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // write the value to the FAT buffer + ((u16*)fatBuffer)[offset] = (value & 0xFFFF); + + break; + + case FS_FAT32: + sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 2); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // write the value to the FAT buffer + (((u32*)fatBuffer)[offset]) = value; + + break; + + default: + return false; + break; + } + + // write the buffer back to disc + disc_WriteSector(fatBufferCurSector, fatBuffer); + + return true; +} +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_ReadWriteFatEntryBuffered +Internal function - writes FAT information about a cluster to a + buffer that should then be flushed to disc using + FAT_WriteFatEntryFlushBuffer() + Call FAT_WriteFatEntry first so as not to ruin the disc. + Also returns the entry being replaced +-----------------------------------------------------------------*/ +u32 FAT_ReadWriteFatEntryBuffered (u32 cluster, u32 value) +{ + u32 sector; + int offset; + u32 oldValue; + + if ((cluster < 0x0002) || (cluster > fatLastCluster)) + return CLUSTER_FREE; + + + switch (filesysType) + { + case FS_UNKNOWN: + oldValue = CLUSTER_FREE; + break; + + case FS_FAT12: + sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ); + offset = ((cluster * 3) / 2) % BYTE_PER_READ; + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // write the old buffer to disc + if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT))) + disc_WriteSector(fatBufferCurSector, fatBuffer); + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + if (cluster & 0x01) { + + oldValue = (((u8*)fatBuffer)[offset] & 0xF0) >> 4; + ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4); + + offset++; + if (offset >= BYTE_PER_READ) { + offset = 0; + // write the buffer back to disc + disc_WriteSector(fatBufferCurSector, fatBuffer); + // read the next sector + fatBufferCurSector++; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + oldValue |= ((((u8*)fatBuffer)[offset]) << 4) & 0x0FF0; + ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4; + + } else { + + oldValue = ((u8*)fatBuffer)[offset] & 0xFF; + ((u8*)fatBuffer)[offset] = value & 0xFF; + + offset++; + if (offset >= BYTE_PER_READ) { + offset = 0; + // write the buffer back to disc + disc_WriteSector(fatBufferCurSector, fatBuffer); + // read the next sector + fatBufferCurSector++; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + oldValue |= (((u8*)fatBuffer)[offset] & 0x0F) << 8; + ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F); + } + + if (oldValue >= 0x0FF7) + { + oldValue = CLUSTER_EOF; + } + + break; + + case FS_FAT16: + sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 1); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // write the old buffer to disc + if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT))) + disc_WriteSector(fatBufferCurSector, fatBuffer); + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // write the value to the FAT buffer + oldValue = ((u16*)fatBuffer)[offset]; + ((u16*)fatBuffer)[offset] = value; + + if (oldValue >= 0xFFF7) + { + oldValue = CLUSTER_EOF; + } + + break; + + case FS_FAT32: + sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ); + offset = cluster % (BYTE_PER_READ >> 2); + + // If FAT buffer contains wrong sector + if (sector != fatBufferCurSector) + { + // write the old buffer to disc + if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT))) + disc_WriteSector(fatBufferCurSector, fatBuffer); + // Load correct sector to buffer + fatBufferCurSector = sector; + disc_ReadSector(fatBufferCurSector, fatBuffer); + } + + // write the value to the FAT buffer + oldValue = ((u32*)fatBuffer)[offset]; + ((u32*)fatBuffer)[offset] = value; + + if (oldValue >= 0x0FFFFFF7) + { + oldValue = CLUSTER_EOF; + } + + break; + + default: + oldValue = CLUSTER_FREE; + break; + } + + return oldValue; +} +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_WriteFatEntryFlushBuffer +Flush the FAT buffer back to the disc +-----------------------------------------------------------------*/ +bool FAT_WriteFatEntryFlushBuffer (void) +{ + // write the buffer disc + if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT))) + { + disc_WriteSector(fatBufferCurSector, fatBuffer); + return true; + } else { + return false; + } +} +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_FirstFreeCluster +Internal function - gets the first available free cluster +-----------------------------------------------------------------*/ +u32 FAT_FirstFreeCluster(void) +{ + // Start at first valid cluster + if (fatFirstFree < CLUSTER_FIRST) + fatFirstFree = CLUSTER_FIRST; + + while ((FAT_NextCluster(fatFirstFree) != CLUSTER_FREE) && (fatFirstFree <= fatLastCluster)) + { + fatFirstFree++; + } + if (fatFirstFree > fatLastCluster) + { + return CLUSTER_EOF; + } + return fatFirstFree; +} +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_LinkFreeCluster +Internal function - gets the first available free cluster, sets it +to end of file, links the input cluster to it then returns the +cluster number +-----------------------------------------------------------------*/ +u32 FAT_LinkFreeCluster(u32 cluster) +{ + u32 firstFree; + u32 curLink; + + if (cluster > fatLastCluster) + { + return CLUSTER_FREE; + } + + // Check if the cluster already has a link, and return it if so + curLink = FAT_NextCluster (cluster); + if ((curLink >= CLUSTER_FIRST) && (curLink < fatLastCluster)) + { + return curLink; // Return the current link - don't allocate a new one + } + + // Get a free cluster + firstFree = FAT_FirstFreeCluster(); + + // If couldn't get a free cluster then return + if (firstFree == CLUSTER_EOF) + { + return CLUSTER_FREE; + } + + if ((cluster >= CLUSTER_FIRST) && (cluster < fatLastCluster)) + { + // Update the linked from FAT entry + FAT_WriteFatEntry (cluster, firstFree); + } + // Create the linked to FAT entry + FAT_WriteFatEntry (firstFree, CLUSTER_EOF); + + return firstFree; +} +#endif + + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_ClearLinks +Internal function - frees any cluster used by a file +-----------------------------------------------------------------*/ +bool FAT_ClearLinks (u32 cluster) +{ + u32 nextCluster; + + if ((cluster < 0x0002) || (cluster > fatLastCluster)) + return false; + + // Store next cluster before erasing the link + nextCluster = FAT_NextCluster (cluster); + + // Erase the link + FAT_WriteFatEntry (cluster, CLUSTER_FREE); + + // Move onto next cluster + cluster = nextCluster; + + while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE)) + { + cluster = FAT_ReadWriteFatEntryBuffered (cluster, CLUSTER_FREE); + } + + // Flush fat write buffer + FAT_WriteFatEntryFlushBuffer (); + + return true; +} +#endif + + +/*----------------------------------------------------------------- +FAT_InitFiles +Reads the FAT information from the CF card. +You need to call this before reading any files. +bool return OUT: true if successful. +-----------------------------------------------------------------*/ +bool FAT_InitFiles (void) +{ + int i; + int bootSector; + BOOT_SEC* bootSec; + + if (!disc_Init()) + { + return (false); + } + + // Read first sector of CF card + if ( !disc_ReadSector(0, globalBuffer)) { + return false; + } + + // Make sure it is a valid MBR or boot sector + if ( (globalBuffer[0x1FE] != 0x55) || (globalBuffer[0x1FF] != 0xAA)) { + return false; + } + + // Check if there is a FAT string, which indicates this is a boot sector + if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T')) + { + bootSector = 0; + } + // Check for FAT32 + else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T')) + { + bootSector = 0; + } + else // This is an MBR + { + // Find first valid partition from MBR + // First check for an active partition + for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i] != 0x80); i+= 0x10); + // If it didn't find an active partition, search for any valid partition + if (i == 0x1FE) + for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10); + + // Go to first valid partition + if ( i != 0x1FE) // Make sure it found a partition + { + bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F); + } else { + bootSector = 0; // No partition found, assume this is a MBR free disk + } + } + + // Read in boot sector + bootSec = (BOOT_SEC*) globalBuffer; + if (!disc_ReadSector (bootSector, bootSec)) { + return false; + } + + // Store required information about the file system + if (bootSec->sectorsPerFAT != 0) + { + filesysSecPerFAT = bootSec->sectorsPerFAT; + } + else + { + filesysSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32; + } + + if (bootSec->numSectorsSmall != 0) + { + filesysNumSec = bootSec->numSectorsSmall; + } + else + { + filesysNumSec = bootSec->numSectors; + } + + filesysBytePerSec = BYTE_PER_READ; // Sector size is redefined to be 512 bytes + filesysSecPerClus = bootSec->sectorsPerCluster * bootSec->bytesPerSector / BYTE_PER_READ; + filesysBytePerClus = filesysBytePerSec * filesysSecPerClus; + filesysFAT = bootSector + bootSec->reservedSectors; + + filesysRootDir = filesysFAT + (bootSec->numFATs * filesysSecPerFAT); + filesysData = filesysRootDir + ((bootSec->rootEntries * sizeof(DIR_ENT)) / filesysBytePerSec); + + filesysTotalSize = (filesysNumSec - filesysData) * filesysBytePerSec; + + // Store info about FAT + fatLastCluster = (filesysNumSec - filesysData) / bootSec->sectorsPerCluster; + fatFirstFree = CLUSTER_FIRST; + fatBufferCurSector = 0; + disc_ReadSector(fatBufferCurSector, fatBuffer); + + if (fatLastCluster < 4085) + { + filesysType = FS_FAT12; // FAT12 volume - unsupported + } + else if (fatLastCluster < 65525) + { + filesysType = FS_FAT16; // FAT16 volume + } + else + { + filesysType = FS_FAT32; // FAT32 volume + } + + if (filesysType != FS_FAT32) + { + filesysRootDirClus = FAT16_ROOT_DIR_CLUSTER; + } + else // Set up for the FAT32 way + { + filesysRootDirClus = bootSec->extBlock.fat32.rootClus; + // Check if FAT mirroring is enabled + if (!(bootSec->extBlock.fat32.extFlags & 0x80)) + { + // Use the active FAT + filesysFAT = filesysFAT + ( filesysSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F)); + } + } + + // Set current directory to the root + curWorkDirCluster = filesysRootDirClus; + wrkDirCluster = filesysRootDirClus; + wrkDirSector = 0; + wrkDirOffset = 0; + + // Set all files to free + for (i=0; i < MAX_FILES_OPEN; i++) + { + openFiles[i].inUse = false; + } + + // No long filenames so far + lfnExists = false; + for (i = 0; i < MAX_FILENAME_LENGTH; i++) + { + lfnName[i] = '\0'; + } + + return (true); +} + +/*----------------------------------------------------------------- +FAT_FreeFiles +Closes all open files then resets the CF card. +Call this before exiting back to the GBAMP +bool return OUT: true if successful. +-----------------------------------------------------------------*/ +bool FAT_FreeFiles (void) +{ + int i; + + // Close all open files + for (i=0; i < MAX_FILES_OPEN; i++) + { + if (openFiles[i].inUse == true) + { + FAT_fclose(&openFiles[i]); + } + } + + // Flush any sectors in disc cache + disc_CacheFlush(); + + // Clear card status + disc_ClearStatus(); + + // Return status of card + return disc_IsInserted(); +} + + +/*----------------------------------------------------------------- +FAT_GetDirEntry +Return the file info structure of the next valid file entry +u32 dirCluster: IN cluster of subdirectory table +int entry: IN the desired file entry +int origin IN: relative position of the entry +DIR_ENT return OUT: desired dirEntry. First char will be FILE_FREE if + the entry does not exist. +-----------------------------------------------------------------*/ +DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin) +{ + DIR_ENT dir; + DIR_ENT_LFN lfn; + int firstSector = 0; + bool notFound = false; + bool found = false; + int maxSectors; + int lfnPos, aliasPos; + u8 lfnChkSum, chkSum; + + int i; + + dir.name[0] = FILE_FREE; // default to no file found + dir.attrib = 0x00; + + // Check if fat has been initialised + if (filesysBytePerSec == 0) + { + return (dir); + } + + switch (origin) + { + case SEEK_SET: + wrkDirCluster = dirCluster; + wrkDirSector = 0; + wrkDirOffset = -1; + break; + case SEEK_CUR: // Don't change anything + break; + case SEEK_END: // Find entry signifying end of directory + // Subtraction will never reach 0, so it keeps going + // until reaches end of directory + wrkDirCluster = dirCluster; + wrkDirSector = 0; + wrkDirOffset = -1; + entry = -1; + break; + default: + return dir; + } + + lfnChkSum = 0; + maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus); + + // Scan Dir for correct entry + firstSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)); + disc_ReadSector (firstSector + wrkDirSector, globalBuffer); + found = false; + notFound = false; + do { + wrkDirOffset++; + if (wrkDirOffset == BYTE_PER_READ / sizeof (DIR_ENT)) + { + wrkDirOffset = 0; + wrkDirSector++; + if ((wrkDirSector == filesysSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER)) + { + wrkDirSector = 0; + wrkDirCluster = FAT_NextCluster(wrkDirCluster); + if (wrkDirCluster == CLUSTER_EOF) + { + notFound = true; + } + firstSector = FAT_ClustToSect(wrkDirCluster); + } + else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (filesysData - filesysRootDir))) + { + notFound = true; // Got to end of root dir + } + disc_ReadSector (firstSector + wrkDirSector, globalBuffer); + } + dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset]; + if ((dir.name[0] != FILE_FREE) && (dir.name[0] > 0x20) && ((dir.attrib & ATTRIB_VOL) != ATTRIB_VOL)) + { + entry--; + if (lfnExists) + { + // Calculate file checksum + chkSum = 0; + for (aliasPos=0; aliasPos < 11; aliasPos++) + { + // NOTE: The operation is an unsigned char rotate right + chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + (aliasPos < 8 ? dir.name[aliasPos] : dir.ext[aliasPos - 8]); + } + if (chkSum != lfnChkSum) + { + lfnExists = false; + lfnName[0] = '\0'; + } + } + if (entry == 0) + { + if (!lfnExists) + { + FAT_GetFilename (dir, lfnName); + } + found = true; + } + } + else if (dir.name[0] == FILE_LAST) + { + if (origin == SEEK_END) + { + found = true; + } + else + { + notFound = true; + } + } + else if (dir.attrib == ATTRIB_LFN) + { + lfn = ((DIR_ENT_LFN*) globalBuffer)[wrkDirOffset]; + if (lfn.ordinal & LFN_DEL) + { + lfnExists = false; + } + else if (lfn.ordinal & LFN_END) // Last part of LFN, make sure it isn't deleted (Thanks MoonLight) + { + lfnExists = true; + lfnName[(lfn.ordinal & ~LFN_END) * 13] = '\0'; // Set end of lfn to null character + lfnChkSum = lfn.checkSum; + } + if (lfnChkSum != lfn.checkSum) + { + lfnExists = false; + } + if (lfnExists) + { + lfnPos = ((lfn.ordinal & ~LFN_END) - 1) * 13; + for (i = 0; i < 13; i++) { + lfnName[lfnPos + i] = ((u8*)&lfn)[(int)(lfn_offset_table[i])] /* | ((u8*)&lfn)[(int)(lfn_offset_table[i]) + 1] include this for unicode support*/; + } + } + } + } while (!found && !notFound); + + // If no file is found, return FILE_FREE + if (notFound) + { + dir.name[0] = FILE_FREE; + } + + return (dir); +} + + +/*----------------------------------------------------------------- +FAT_GetLongFilename +Get the long name of the last file or directory retrived with + GetDirEntry. Also works for FindFirstFile and FindNextFile. + If a long name doesn't exist, it returns the short name + instead. +char* filename: OUT will be filled with the filename, should be at + least 256 bytes long +bool return OUT: return true if successful +-----------------------------------------------------------------*/ +bool FAT_GetLongFilename (char* filename) +{ + if (filename == NULL) + return false; + + strncpy (filename, lfnName, MAX_FILENAME_LENGTH - 1); + filename[MAX_FILENAME_LENGTH - 1] = '\0'; + + return true; +} + + +/*----------------------------------------------------------------- +FAT_GetFilename +Get the alias (short name) of the file or directory stored in + dirEntry +DIR_ENT dirEntry: IN a valid directory table entry +char* alias OUT: will be filled with the alias (short filename), + should be at least 13 bytes long +bool return OUT: return true if successful +-----------------------------------------------------------------*/ +bool FAT_GetFilename (DIR_ENT dirEntry, char* alias) +{ + int i=0; + int j=0; + + alias[0] = '\0'; + if (dirEntry.name[0] != FILE_FREE) + { + if (dirEntry.name[0] == '.') + { + alias[0] = '.'; + if (dirEntry.name[1] == '.') + { + alias[1] = '.'; + alias[2] = '\0'; + } + else + { + alias[1] = '\0'; + } + } + else + { + // Copy the filename from the dirEntry to the string + for (i = 0; (i < 8) && (dirEntry.name[i] != ' '); i++) + { + alias[i] = dirEntry.name[i]; + } + // Copy the extension from the dirEntry to the string + if (dirEntry.ext[0] != ' ') + { + alias[i++] = '.'; + for ( j = 0; (j < 3) && (dirEntry.ext[j] != ' '); j++) + { + alias[i++] = dirEntry.ext[j]; + } + } + alias[i] = '\0'; + } + } + + return (alias[0] != '\0'); +} + +/*----------------------------------------------------------------- +FAT_GetAlias +Get the alias (short name) of the last file or directory entry read + using GetDirEntry. Works for FindFirstFile and FindNextFile +char* alias OUT: will be filled with the alias (short filename), + should be at least 13 bytes long +bool return OUT: return true if successful +-----------------------------------------------------------------*/ +bool FAT_GetAlias (char* alias) +{ + if (alias == NULL) + { + return false; + } + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return FAT_GetFilename (((DIR_ENT*)globalBuffer)[wrkDirOffset], alias); +} + +/*----------------------------------------------------------------- +FAT_GetFileSize +Get the file size of the last file found or openned. +This idea is based on a modification by MoonLight +u32 return OUT: the file size +-----------------------------------------------------------------*/ +u32 FAT_GetFileSize (void) +{ + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return ((DIR_ENT*)globalBuffer)[wrkDirOffset].fileSize; +} + +/*----------------------------------------------------------------- +FAT_GetFileCluster +Get the first cluster of the last file found or openned. +u32 return OUT: the file start cluster +-----------------------------------------------------------------*/ +u32 FAT_GetFileCluster (void) +{ + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return (((DIR_ENT*)globalBuffer)[wrkDirOffset].startCluster) | (((DIR_ENT*)globalBuffer)[wrkDirOffset].startClusterHigh << 16); +} + +/*----------------------------------------------------------------- +FAT_GetFileAttributes +Get the attributes of the last file found or openned. +u8 return OUT: the file's attributes +-----------------------------------------------------------------*/ +u8 FAT_GetFileAttributes (void) +{ + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib; +} + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_SetFileAttributes +Set the attributes of a file. +const char* filename IN: The name and path of the file to modify +u8 attributes IN: The attribute values to assign +u8 mask IN: Detemines which attributes are changed +u8 return OUT: the file's new attributes +-----------------------------------------------------------------*/ +u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask) +{ + // Get the file + if (!FAT_FileExists(filename)) { + return 0xff; + } + + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib = (((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib & ~(mask & 0x27)) | (attributes & 0x27); // 0x27 is he settable attributes + + disc_WriteSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib; +} +#endif + +#ifdef FILE_TIME_SUPPORT +time_t FAT_FileTimeToCTime (u16 fileTime, u16 fileDate) +{ + struct tm timeInfo; + + timeInfo.tm_year = (fileDate >> 9) + 80; // years since midnight January 1970 + timeInfo.tm_mon = ((fileDate >> 5) & 0xf) - 1; // Months since january + timeInfo.tm_mday = fileDate & 0x1f; // Day of the month + + timeInfo.tm_hour = fileTime >> 11; // hours past midnight + timeInfo.tm_min = (fileTime >> 5) & 0x3f; // minutes past the hour + timeInfo.tm_sec = (fileTime & 0x1f) * 2; // seconds past the minute + + return mktime(&timeInfo); +} + +/*----------------------------------------------------------------- +FAT_GetFileCreationTime +Get the creation time of the last file found or openned. +time_t return OUT: the file's creation time +-----------------------------------------------------------------*/ +time_t FAT_GetFileCreationTime (void) +{ + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].cTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].cDate); +} + +/*----------------------------------------------------------------- +FAT_GetFileLastWriteTime +Get the creation time of the last file found or openned. +time_t return OUT: the file's creation time +-----------------------------------------------------------------*/ +time_t FAT_GetFileLastWriteTime (void) +{ + // Read in the last accessed directory entry + disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer); + + return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].mTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].mDate); +} +#endif + +/*----------------------------------------------------------------- +FAT_DirEntFromPath +Finds the directory entry for a file or directory from a path +Path separator is a forward slash / +const char* path: IN null terminated string of path. +DIR_ENT return OUT: dirEntry of found file. First char will be FILE_FREE + if the file was not found +-----------------------------------------------------------------*/ +DIR_ENT FAT_DirEntFromPath (const char* path) +{ + int pathPos; + char name[MAX_FILENAME_LENGTH]; + char alias[13]; + int namePos; + bool found, notFound; + DIR_ENT dirEntry; + u32 dirCluster; + bool flagLFN, dotSeen; + + // Start at beginning of path + pathPos = 0; + + if (path[pathPos] == '/') + { + dirCluster = filesysRootDirClus; // Start at root directory + } + else + { + dirCluster = curWorkDirCluster; // Start at current working dir + } + + // Eat any slash / + while ((path[pathPos] == '/') && (path[pathPos] != '\0')) + { + pathPos++; + } + + // Search until can't continue + found = false; + notFound = false; + while (!notFound && !found) + { + flagLFN = false; + // Copy name from path + namePos = 0; + if ((path[pathPos] == '.') && ((path[pathPos + 1] == '\0') || (path[pathPos + 1] == '/'))) { + // Dot entry + name[namePos++] = '.'; + pathPos++; + } else if ((path[pathPos] == '.') && (path[pathPos + 1] == '.') && ((path[pathPos + 2] == '\0') || (path[pathPos + 2] == '/'))){ + // Double dot entry + name[namePos++] = '.'; + pathPos++; + name[namePos++] = '.'; + pathPos++; + } else { + // Copy name from path + if (path[pathPos] == '.') { + flagLFN = true; + } + dotSeen = false; + while ((namePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0') && (path[pathPos] != '/')) + { + name[namePos] = ucase(path[pathPos]); + if ((name[namePos] <= ' ') || ((name[namePos] >= ':') && (name[namePos] <= '?'))) // Invalid character + { + flagLFN = true; + } + if (name[namePos] == '.') { + if (!dotSeen) { + dotSeen = true; + } else { + flagLFN = true; + } + } + namePos++; + pathPos++; + } + // Check if a long filename was specified + if (namePos > 12) + { + flagLFN = true; + } + } + + // Add end of string char + name[namePos] = '\0'; + + // Move through path to correct place + while ((path[pathPos] != '/') && (path[pathPos] != '\0')) + pathPos++; + // Eat any slash / + while ((path[pathPos] == '/') && (path[pathPos] != '\0')) + { + pathPos++; + } + + // Search current Dir for correct entry + dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_SET); + while ( !found && !notFound) + { + // Match filename + found = true; + for (namePos = 0; (namePos < MAX_FILENAME_LENGTH) && found && (name[namePos] != '\0') && (lfnName[namePos] != '\0'); namePos++) + { + if (name[namePos] != ucase(lfnName[namePos])) + { + found = false; + } + } + if ((name[namePos] == '\0') != (lfnName[namePos] == '\0')) + { + found = false; + } + + // Check against alias as well. + if (!found) + { + FAT_GetFilename(dirEntry, alias); + found = true; + for (namePos = 0; (namePos < 13) && found && (name[namePos] != '\0') && (alias[namePos] != '\0'); namePos++) + { + if (name[namePos] != ucase(alias[namePos])) + { + found = false; + } + } + if ((name[namePos] == '\0') != (alias[namePos] == '\0')) + { + found = false; + } + } + + if (dirEntry.name[0] == FILE_FREE) + // Couldn't find specified file + { + found = false; + notFound = true; + } + if (!found && !notFound) + { + dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_CUR); + } + } + + if (found && ((dirEntry.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (path[pathPos] != '\0')) + // It has found a directory from within the path that needs to be followed + { + found = false; + dirCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16); + } + } + + if (notFound) + { + dirEntry.name[0] = FILE_FREE; + dirEntry.attrib = 0x00; + } + + return (dirEntry); +} + + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_AddDirEntry +Creates a new dir entry for a file +Path separator is a forward slash / +const char* path: IN null terminated string of path to file. +DIR_ENT newDirEntry IN: The directory entry to use. +int file IN: The file being added (optional, use -1 if not used) +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry) +{ + char filename[MAX_FILENAME_LENGTH]; + int filePos, pathPos, aliasPos; + char tempChar; + bool flagLFN, dotSeen; + char fileAlias[13] = {0}; + int tailNum; + + unsigned char chkSum = 0; + + u32 oldWorkDirCluster; + + DIR_ENT* dirEntries = (DIR_ENT*)globalBuffer; + u32 dirCluster; + int secOffset; + int entryOffset; + int maxSectors; + u32 firstSector; + + DIR_ENT_LFN lfnEntry; + int lfnPos = 0; + + int dirEntryLength = 0; + int dirEntryRemain = 0; + u32 tempDirCluster; + int tempSecOffset; + int tempEntryOffset; + bool dirEndFlag = false; + + int i; + + // Store current working directory + oldWorkDirCluster = curWorkDirCluster; + + // Find filename within path and change to correct directory + if (path[0] == '/') + { + curWorkDirCluster = filesysRootDirClus; + } + + pathPos = 0; + filePos = 0; + flagLFN = false; + + while (path[pathPos + filePos] != '\0') + { + if (path[pathPos + filePos] == '/') + { + filename[filePos] = '\0'; + if (FAT_chdir(filename) == false) + { + curWorkDirCluster = oldWorkDirCluster; + return false; // Couldn't change directory + } + pathPos += filePos + 1; + filePos = 0; + } + filename[filePos] = path[pathPos + filePos]; + filePos++; + } + + // Skip over last slashes + while (path[pathPos] == '/') + pathPos++; + + // Check if the filename has a leading "." + // If so, it is an LFN + if (path[pathPos] == '.') { + flagLFN = true; + } + + // Copy name from path + filePos = 0; + dotSeen = false; + + while ((filePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0')) + { + filename[filePos] = path[pathPos]; + if ((filename[filePos] <= ' ') || ((filename[filePos] >= ':') && (filename[filePos] <= '?'))) // Invalid character + { + flagLFN = true; + } + if (filename[filePos] == '.') { + if (!dotSeen) { + dotSeen = true; + } else { + flagLFN = true; + } + } + filePos++; + pathPos++; + if ((filePos > 8) && !dotSeen) { + flagLFN = true; + } + } + + if (filePos == 0) // No filename + { + return false; + } + + // Check if a long filename was specified + if (filePos > 12) + { + flagLFN = true; + } + + // Check if extension is > 3 characters long + if (!flagLFN && (strrchr (filename, '.') != NULL) && (strlen(strrchr(filename, '.')) > 4)) { + flagLFN = true; + } + + lfnPos = (filePos - 1) / 13; + + // Add end of string char + filename[filePos++] = '\0'; + // Clear remaining chars + while (filePos < MAX_FILENAME_LENGTH) + filename[filePos++] = 0x01; // Set for LFN compatibility + + + if (flagLFN) + { + // Generate short filename - always a 2 digit number for tail + // Get first 5 chars of alias from LFN + aliasPos = 0; + filePos = 0; + if (filename[filePos] == '.') { + filePos++; + } + for ( ; (aliasPos < 5) && (filename[filePos] != '\0') && (filename[filePos] != '.') ; filePos++) + { + tempChar = ucase(filename[filePos]); + if (((tempChar > ' ' && tempChar < ':') || tempChar > '?') && tempChar != '.') + fileAlias[aliasPos++] = tempChar; + } + // Pad Alias with underscores + while (aliasPos < 5) + fileAlias[aliasPos++] = '_'; + + fileAlias[5] = '~'; + fileAlias[8] = '.'; + fileAlias[9] = ' '; + fileAlias[10] = ' '; + fileAlias[11] = ' '; + if (strchr (filename, '.') != NULL) { + while(filename[filePos] != '\0') + { + filePos++; + if (filename[filePos] == '.') + { + pathPos = filePos; + } + } + filePos = pathPos + 1; //pathPos is used as a temporary variable + // Copy first 3 characters of extension + for (aliasPos = 9; (aliasPos < 12) && (filename[filePos] != '\0'); filePos++) + { + tempChar = ucase(filename[filePos]); + if ((tempChar > ' ' && tempChar < ':') || tempChar > '?') + fileAlias[aliasPos++] = tempChar; + } + } else { + aliasPos = 9; + } + + // Pad Alias extension with spaces + while (aliasPos < 12) + fileAlias[aliasPos++] = ' '; + + fileAlias[12] = '\0'; + + + // Get a valid tail number + tailNum = 0; + do { + tailNum++; + fileAlias[6] = 0x30 + ((tailNum / 10) % 10); // 10's digit + fileAlias[7] = 0x30 + (tailNum % 10); // 1's digit + } while ((FAT_DirEntFromPath(fileAlias).name[0] != FILE_FREE) && (tailNum < 100)); + + if (tailNum < 100) // Found an alias not being used + { + // Calculate file checksum + chkSum = 0; + for (aliasPos=0; aliasPos < 12; aliasPos++) + { + // Skip '.' + if (fileAlias[aliasPos] == '.') + aliasPos++; + // NOTE: The operation is an unsigned char rotate right + chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + fileAlias[aliasPos]; + } + } + else // Couldn't find a valid alias + { + return false; + } + + dirEntryLength = lfnPos + 2; + } + else // Its not a long file name + { + // Just copy alias straight from filename + for (aliasPos = 0; aliasPos < 13; aliasPos++) + { + tempChar = ucase(filename[aliasPos]); + if ((tempChar > ' ' && tempChar < ':') || tempChar > '?') + fileAlias[aliasPos] = tempChar; + } + fileAlias[12] = '\0'; + + lfnPos = -1; + + dirEntryLength = 1; + } + + // Change dirEntry name to match alias + for (aliasPos = 0; ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0') && (aliasPos < 8)); aliasPos++) + { + newDirEntry.name[aliasPos] = fileAlias[aliasPos]; + } + while (aliasPos < 8) + { + newDirEntry.name[aliasPos++] = ' '; + } + aliasPos = 0; + while ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0')) + aliasPos++; + filePos = 0; + while (( filePos < 3 ) && (fileAlias[aliasPos] != '\0')) + { + tempChar = fileAlias[aliasPos++]; + if ((tempChar > ' ' && tempChar < ':' && tempChar!='.') || tempChar > '?') + newDirEntry.ext[filePos++] = tempChar; + } + while (filePos < 3) + { + newDirEntry.ext[filePos++] = ' '; + } + + // Scan Dir for free entry + dirCluster = curWorkDirCluster; + secOffset = 0; + entryOffset = 0; + maxSectors = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus); + firstSector = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(dirCluster)); + disc_ReadSector (firstSector + secOffset, dirEntries); + + dirEntryRemain = dirEntryLength; + tempDirCluster = dirCluster; + tempSecOffset = secOffset; + tempEntryOffset = entryOffset; + + // Search for a large enough space to fit in new directory entry + while ((dirEntries[entryOffset].name[0] != FILE_LAST) && (dirEntryRemain > 0)) + { + + entryOffset++; + + if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT)) + { + entryOffset = 0; + secOffset++; + if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER)) + { + secOffset = 0; + if (FAT_NextCluster(dirCluster) == CLUSTER_EOF) + { + dirCluster = FAT_LinkFreeCluster(dirCluster); + dirEntries[0].name[0] = FILE_LAST; + } + else + { + dirCluster = FAT_NextCluster(dirCluster); + } + firstSector = FAT_ClustToSect(dirCluster); + } + else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir))) + { + return false; // Got to end of root dir - can't fit in more files + } + disc_ReadSector (firstSector + secOffset, dirEntries); + } + + if ((dirEntries[entryOffset].name[0] == FILE_FREE) || (dirEntries[entryOffset].name[0] == FILE_LAST) ) + { + dirEntryRemain--; + } else { + dirEntryRemain = dirEntryLength; + tempDirCluster = dirCluster; + tempSecOffset = secOffset; + tempEntryOffset = entryOffset; + } + } + + // Modifying the last directory is a special case - have to erase following entries + if (dirEntries[entryOffset].name[0] == FILE_LAST) + { + dirEndFlag = true; + } + + // Recall last used entry + dirCluster = tempDirCluster; + secOffset = tempSecOffset; + entryOffset = tempEntryOffset; + dirEntryRemain = dirEntryLength; + + // Re-read in first sector that will be written to + if (dirEndFlag && (entryOffset == 0)) { + memset (dirEntries, FILE_LAST, BYTE_PER_READ); + } else { + disc_ReadSector (firstSector + secOffset, dirEntries); + } + + // Add new directory entry + while (dirEntryRemain > 0) + { + // Move to next entry, first pass advances from last used entry + entryOffset++; + if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT)) + { + // Write out the current sector if we need to + entryOffset = 0; + if (dirEntryRemain < dirEntryLength) // Don't write out sector on first pass + { + disc_WriteSector (firstSector + secOffset, dirEntries); + } + secOffset++; + if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER)) + { + secOffset = 0; + if (FAT_NextCluster(dirCluster) == CLUSTER_EOF) + { + dirCluster = FAT_LinkFreeCluster(dirCluster); + dirEntries[0].name[0] = FILE_LAST; + } + else + { + dirCluster = FAT_NextCluster(dirCluster); + } + firstSector = FAT_ClustToSect(dirCluster); + } + else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir))) + { + return false; // Got to end of root dir - can't fit in more files + } + if (dirEndFlag) + { + memset (dirEntries, FILE_LAST, BYTE_PER_READ); + } else { + disc_ReadSector (firstSector + secOffset, dirEntries); + } + } + + // Generate LFN entries + if (lfnPos >= 0) + { + lfnEntry.ordinal = (lfnPos + 1) | (dirEntryRemain == dirEntryLength ? LFN_END : 0); + for (i = 0; i < 13; i++) { + if (filename [lfnPos * 13 + i] == 0x01) { + ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = 0xff; + ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0xff; + } else { + ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = filename [lfnPos * 13 + i]; + ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0x00; + } + } + + lfnEntry.checkSum = chkSum; + lfnEntry.flag = ATTRIB_LFN; + lfnEntry.reserved1 = 0; + lfnEntry.reserved2 = 0; + + *((DIR_ENT_LFN*)&dirEntries[entryOffset]) = lfnEntry; + lfnPos --; + lfnEntry.ordinal = 0; + } // end writing long filename entries + else + { + dirEntries[entryOffset] = newDirEntry; + if (dirEndFlag && (entryOffset < (BYTE_PER_READ / sizeof (DIR_ENT))) ) + dirEntries[entryOffset+1].name[0] = FILE_LAST; + } + + dirEntryRemain--; + } + + // Write directory back to disk + disc_WriteSector (firstSector + secOffset, dirEntries); + + // Change back to Working DIR + curWorkDirCluster = oldWorkDirCluster; + + return true; +} +#endif + +/*----------------------------------------------------------------- +FAT_FindNextFile +Gets the name of the next directory entry + (can be a file or subdirectory) +char* filename: OUT filename, must be at least 13 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindNextFile(char* filename) +{ + // Get the next directory entry + DIR_ENT file; + file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_CUR); + + if (file.name[0] == FILE_FREE) + { + return FT_NONE; // Did not find a file + } + + // Get the filename + if (filename != NULL) + FAT_GetFilename (file, filename); + + if ((file.attrib & ATTRIB_DIR) != 0) + { + return FT_DIR; // Found a directory + } + else + { + return FT_FILE; // Found a file + } +} + +/*----------------------------------------------------------------- +FAT_FindFirstFile +Gets the name of the first directory entry and resets the count + (can be a file or subdirectory) +char* filename: OUT filename, must be at least 13 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindFirstFile(char* filename) +{ + // Get the first directory entry + DIR_ENT file; + file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_SET); + + if (file.name[0] == FILE_FREE) + { + return FT_NONE; // Did not find a file + } + + // Get the filename + if (filename != NULL) + FAT_GetFilename (file, filename); + + if ((file.attrib & ATTRIB_DIR) != 0) + { + return FT_DIR; // Found a directory + } + else + { + return FT_FILE; // Found a file + } +} + +/*----------------------------------------------------------------- +FAT_FindFirstFileLFN +Gets the long file name of the first directory entry and resets + the count (can be a file or subdirectory) +char* lfn: OUT long file name, must be at least 256 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindFirstFileLFN(char* lfn) +{ + FILE_TYPE type; + type = FAT_FindFirstFile(NULL); + FAT_GetLongFilename (lfn); + return type; +} + +/*----------------------------------------------------------------- +FAT_FindNextFileLFN +Gets the long file name of the next directory entry + (can be a file or subdirectory) +char* lfn: OUT long file name, must be at least 256 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindNextFileLFN(char* lfn) +{ + FILE_TYPE type; + type = FAT_FindNextFile(NULL); + FAT_GetLongFilename (lfn); + return type; +} + + +/*----------------------------------------------------------------- +FAT_FileExists +Returns the type of file +char* filename: IN filename of the file to look for +FILE_TYPE return: OUT returns FT_NONE if there is now file with + that name, FT_FILE if it is a file and FT_DIR if it is a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FileExists(const char* filename) +{ + DIR_ENT dirEntry; + // Get the dirEntry for the path specified + dirEntry = FAT_DirEntFromPath (filename); + + if (dirEntry.name[0] == FILE_FREE) + { + return FT_NONE; + } + else if (dirEntry.attrib & ATTRIB_DIR) + { + return FT_DIR; + } + else + { + return FT_FILE; + } +} + +/*----------------------------------------------------------------- +FAT_GetFileSystemType +FS_TYPE return: OUT returns the current file system type +-----------------------------------------------------------------*/ +FS_TYPE FAT_GetFileSystemType (void) +{ + return filesysType; +} + +/*----------------------------------------------------------------- +FAT_GetFileSystemTotalSize +u32 return: OUT returns the total disk space (used + free) +-----------------------------------------------------------------*/ +u32 FAT_GetFileSystemTotalSize (void) +{ + return filesysTotalSize; +} + + + +/*----------------------------------------------------------------- +FAT_chdir +Changes the current working directory +const char* path: IN null terminated string of directory separated by + forward slashes, / is root +bool return: OUT returns true if successful +-----------------------------------------------------------------*/ +bool FAT_chdir (const char* path) +{ + DIR_ENT dir; + if (path[0] == '/' && path[1] == '\0') + { + curWorkDirCluster = filesysRootDirClus; + return true; + } + if (path[0] == '\0') // Return true if changing relative to nothing + { + return true; + } + + dir = FAT_DirEntFromPath (path); + + if (((dir.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (dir.name[0] != FILE_FREE)) + { + // Change directory + curWorkDirCluster = dir.startCluster | (dir.startClusterHigh << 16); + + // Move to correct cluster for root directory + if (curWorkDirCluster == FAT16_ROOT_DIR_CLUSTER) + { + curWorkDirCluster = filesysRootDirClus; + } + + // Reset file position in directory + wrkDirCluster = curWorkDirCluster; + wrkDirSector = 0; + wrkDirOffset = -1; + return true; + } + else + { + // Couldn't change directory - wrong path specified + return false; + } +} + +/*----------------------------------------------------------------- +FAT_fopen(filename, mode) +Opens a file +const char* path: IN null terminated string of filename and path + separated by forward slashes, / is root +const char* mode: IN mode to open file in + Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use + "b" or "t" in any mode, as all files are openned in binary mode +FAT_FILE* return: OUT handle to open file, returns NULL if the file + couldn't be openned +-----------------------------------------------------------------*/ +FAT_FILE* FAT_fopen(const char* path, const char* mode) +{ + int fileNum; + FAT_FILE* file; + DIR_ENT dirEntry; +#ifdef CAN_WRITE_TO_DISC + u32 startCluster; + int clusCount; +#endif + + char* pchTemp; + // Check that a valid mode was specified + pchTemp = strpbrk ( mode, "rRwWaA" ); + if (pchTemp == NULL) + { + return NULL; + } + if (strpbrk ( pchTemp+1, "rRwWaA" ) != NULL) + { + return NULL; + } + + // Get the dirEntry for the path specified + dirEntry = FAT_DirEntFromPath (path); + + // Check that it is not a directory + if (dirEntry.attrib & ATTRIB_DIR) + { + return NULL; + } + +#ifdef CAN_WRITE_TO_DISC + // Check that it is not a read only file being openned in a writing mode + if ( (strpbrk(mode, "wWaA+") != NULL) && (dirEntry.attrib & ATTRIB_RO)) + { + return NULL; + } +#else + if ( (strpbrk(mode, "wWaA+") != NULL)) + { + return NULL; + } +#endif + + // Find a free file buffer + for (fileNum = 0; (fileNum < MAX_FILES_OPEN) && (openFiles[fileNum].inUse == true); fileNum++); + + if (fileNum == MAX_FILES_OPEN) // No free files + { + return NULL; + } + + file = &openFiles[fileNum]; + // Remember where directory entry was + file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector; + file->dirEntOffset = wrkDirOffset; + + if ( strpbrk(mode, "rR") != NULL ) //(ucase(mode[0]) == 'R') + { + if (dirEntry.name[0] == FILE_FREE) // File must exist + { + return NULL; + } + + file->read = true; +#ifdef CAN_WRITE_TO_DISC + file->write = ( strchr(mode, '+') != NULL ); //(mode[1] == '+'); +#else + file->write = false; +#endif + file->append = false; + + // Store information about position within the file, for use + // by FAT_fread, FAT_fseek, etc. + file->firstCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16); + +#ifdef CAN_WRITE_TO_DISC + // Check if file is openned for random. If it is, and currently has no cluster, one must be + // assigned to it. + if (file->write && file->firstCluster == CLUSTER_FREE) + { + file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE); + if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster + { + return NULL; + } + + // Store cluster position into the directory entry + dirEntry.startCluster = (file->firstCluster & 0xFFFF); + dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF); + disc_ReadSector (file->dirEntSector, globalBuffer); + ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry; + disc_WriteSector (file->dirEntSector, globalBuffer); + } +#endif + + file->length = dirEntry.fileSize; + file->curPos = 0; + file->curClus = dirEntry.startCluster | (dirEntry.startClusterHigh << 16); + file->curSect = 0; + file->curByte = 0; + + // Not appending + file->appByte = 0; + file->appClus = 0; + file->appSect = 0; + + disc_ReadSector( FAT_ClustToSect( file->curClus), file->readBuffer); + file->inUse = true; // We're using this file now + + return file; + } // mode "r" + +#ifdef CAN_WRITE_TO_DISC + if ( strpbrk(mode, "wW") != NULL ) // (ucase(mode[0]) == 'W') + { + if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist + { + dirEntry.attrib = ATTRIB_ARCH; + dirEntry.reserved = 0; + + // Time and date set to system time and date + dirEntry.cTime_ms = 0; + dirEntry.cTime = getRTCtoFileTime(); + dirEntry.cDate = getRTCtoFileDate(); + dirEntry.aDate = getRTCtoFileDate(); + dirEntry.mTime = getRTCtoFileTime(); + dirEntry.mDate = getRTCtoFileDate(); + } + else // Already a file entry + { + // Free any clusters used + FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16)); + } + + // Get a cluster to use + startCluster = FAT_LinkFreeCluster (CLUSTER_FREE); + if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster + { + return NULL; + } + + // Store cluster position into the directory entry + dirEntry.startCluster = (startCluster & 0xFFFF); + dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF); + + // The file has no data in it - its over written so should be empty + dirEntry.fileSize = 0; + + if (dirEntry.name[0] == FILE_FREE) // No file + { + // Have to create a new entry + if(!FAT_AddDirEntry (path, dirEntry)) + { + return NULL; + } + // Get the newly created dirEntry + dirEntry = FAT_DirEntFromPath (path); + + // Remember where directory entry was + file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector; + file->dirEntOffset = wrkDirOffset; + } + else // Already a file + { + // Just modify the old entry + disc_ReadSector (file->dirEntSector, globalBuffer); + ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry; + disc_WriteSector (file->dirEntSector, globalBuffer); + } + + + // Now that file is created, open it + file->read = ( strchr(mode, '+') != NULL ); //(mode[1] == '+'); + file->write = true; + file->append = false; + + // Store information about position within the file, for use + // by FAT_fread, FAT_fseek, etc. + file->firstCluster = startCluster; + file->length = 0; // Should always have 0 bytes if openning in "w" mode + file->curPos = 0; + file->curClus = startCluster; + file->curSect = 0; + file->curByte = 0; + + // Not appending + file->appByte = 0; + file->appClus = 0; + file->appSect = 0; + + // Empty file, so empty read buffer + memset (file->readBuffer, 0, BYTE_PER_READ); + file->inUse = true; // We're using this file now + + return file; + } + + if ( strpbrk(mode, "aA") != NULL ) // (ucase(mode[0]) == 'A') + { + if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist + { + dirEntry.attrib = ATTRIB_ARCH; + dirEntry.reserved = 0; + + // Time and date set to system time and date + dirEntry.cTime_ms = 0; + dirEntry.cTime = getRTCtoFileTime(); + dirEntry.cDate = getRTCtoFileDate(); + dirEntry.aDate = getRTCtoFileDate(); + dirEntry.mTime = getRTCtoFileTime(); + dirEntry.mDate = getRTCtoFileDate(); + + // The file has no data in it + dirEntry.fileSize = 0; + + // Get a cluster to use + startCluster = FAT_LinkFreeCluster (CLUSTER_FREE); + if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster + { + return NULL; + } + dirEntry.startCluster = (startCluster & 0xFFFF); + dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF); + + if(!FAT_AddDirEntry (path, dirEntry)) + return NULL; + + // Get the newly created dirEntry + dirEntry = FAT_DirEntFromPath (path); + + // Store append cluster + file->appClus = startCluster; + + // Remember where directory entry was + file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector; + file->dirEntOffset = wrkDirOffset; + } + else // File already exists - reuse the old directory entry + { + startCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16); + // If it currently has no cluster, one must be assigned to it. + if (startCluster == CLUSTER_FREE) + { + file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE); + if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster + { + return NULL; + } + + // Store cluster position into the directory entry + dirEntry.startCluster = (file->firstCluster & 0xFFFF); + dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF); + disc_ReadSector (file->dirEntSector, globalBuffer); + ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry; + disc_WriteSector (file->dirEntSector, globalBuffer); + + // Store append cluster + file->appClus = startCluster; + + } else { + + // Follow cluster list until last one is found + clusCount = dirEntry.fileSize / filesysBytePerClus; + file->appClus = startCluster; + while ((clusCount--) && (FAT_NextCluster (file->appClus) != CLUSTER_FREE) && (FAT_NextCluster (file->appClus) != CLUSTER_EOF)) + { + file->appClus = FAT_NextCluster (file->appClus); + } + if (clusCount >= 0) // Check if ran out of clusters + { + // Set flag to allocate new cluster when needed + file->appSect = filesysSecPerClus; + file->appByte = 0; + } + } + } + + // Now that file is created, open it + file->read = ( strchr(mode, '+') != NULL ); + file->write = false; + file->append = true; + + // Calculate the sector and byte of the current position, + // and store them + file->appSect = (dirEntry.fileSize % filesysBytePerClus) / BYTE_PER_READ; + file->appByte = dirEntry.fileSize % BYTE_PER_READ; + + // Store information about position within the file, for use + // by FAT_fread, FAT_fseek, etc. + file->firstCluster = startCluster; + file->length = dirEntry.fileSize; + file->curPos = dirEntry.fileSize; + file->curClus = file->appClus; + file->curSect = file->appSect; + file->curByte = file->appByte; + + // Read into buffer + disc_ReadSector( FAT_ClustToSect(file->curClus) + file->curSect, file->readBuffer); + file->inUse = true; // We're using this file now + return file; + } +#endif + + // Can only reach here if a bad mode was specified + return NULL; +} + +/*----------------------------------------------------------------- +FAT_fclose(file) +Closes a file +FAT_FILE* file: IN handle of the file to close +bool return OUT: true if successful, false if not +-----------------------------------------------------------------*/ +bool FAT_fclose (FAT_FILE* file) +{ + // Clear memory used by file information + if ((file != NULL) && (file->inUse == true)) + { +#ifdef CAN_WRITE_TO_DISC + if (file->write || file->append) + { + // Write new length, time and date back to directory entry + disc_ReadSector (file->dirEntSector, globalBuffer); + + ((DIR_ENT*)globalBuffer)[file->dirEntOffset].fileSize = file->length; + ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mTime = getRTCtoFileTime(); + ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mDate = getRTCtoFileDate(); + ((DIR_ENT*)globalBuffer)[file->dirEntOffset].aDate = getRTCtoFileDate(); + + disc_WriteSector (file->dirEntSector, globalBuffer); + + // Flush any sectors in disc cache + disc_CacheFlush(); + } +#endif + file->inUse = false; + return true; + } + else + { + return false; + } +} + +/*----------------------------------------------------------------- +FAT_ftell(file) +Returns the current position in a file +FAT_FILE* file: IN handle of an open file +u32 OUT: Current position +-----------------------------------------------------------------*/ +u32 FAT_ftell (FAT_FILE* file) +{ + // Return the position as specified in the FAT_FILE structure + if ((file != NULL) && (file->inUse == true)) + { + return file->curPos; + } + else + { + // Return -1 if no file was given + return -1; + } +} + +/*----------------------------------------------------------------- +FAT_fseek(file, offset, origin) +Seeks to specified byte position in file +FAT_FILE* file: IN handle of an open file +s32 offset IN: position to seek to, relative to origin +int origin IN: origin to seek from +int OUT: Returns 0 if successful, -1 if not +-----------------------------------------------------------------*/ +int FAT_fseek(FAT_FILE* file, s32 offset, int origin) +{ + u32 cluster, nextCluster; + int clusCount; + u32 position; + u32 curPos; + + if ((file == NULL) || (file->inUse == false)) // invalid file + { + return -1; + } + + // Can't seek in append only mode + if (!file->read && !file->write) + { + return -1; + } + + curPos = file->curPos; + + switch (origin) + { + case SEEK_SET: + if (offset >= 0) + { + position = offset; + } else { + // Tried to seek before start of file + position = 0; + } + break; + case SEEK_CUR: + if (offset >= 0) + { + position = curPos + offset; + } + else if ( (u32)(offset * -1) >= curPos ) + { + // Tried to seek before start of file + position = 0; + } + else + { + // Using u32 to maintain 32 bits of accuracy + position = curPos - (u32)(offset * -1); + } + break; + case SEEK_END: + if (offset >= 0) + { + // Seeking to end of file + position = file->length; // Fixed thanks to MoonLight + } + else if ( (u32)(offset * -1) >= file->length ) + { + // Tried to seek before start of file + position = 0; + } + else + { + // Using u32 to maintain 32 bits of accuracy + position = file->length - (u32)(offset * -1); + } + break; + default: + return -1; + } + + if (position > file->length) + { + // Tried to go past end of file + position = file->length; + } + + // Save position + file->curPos = position; + + + // Calculate where the correct cluster is + if (position > curPos) + { + clusCount = (position - curPos + (file->curSect * filesysBytePerSec) + file->curByte) / filesysBytePerClus; // Fixed thanks to AgentQ + cluster = file->curClus; + } else { + clusCount = position / filesysBytePerClus; + cluster = file->firstCluster; + } + + // Calculate the sector and byte of the current position, + // and store them + file->curSect = (position % filesysBytePerClus) / BYTE_PER_READ; + file->curByte = position % BYTE_PER_READ; + + // Follow cluster list until desired one is found + if (clusCount > 0) // Only look at next cluster if need to + { + nextCluster = FAT_NextCluster (cluster); + } else { + nextCluster = cluster; + } + while ((clusCount--) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) + { + cluster = nextCluster; + nextCluster = FAT_NextCluster (cluster); + } + // Check if ran out of clusters, and the file is being written to + if ((clusCount >= 0) && (file->write || file->append)) + { + // Set flag to allocate a new cluster + file->curSect = filesysSecPerClus; + file->curByte = 0; + } + file->curClus = cluster; + + // Reload sector buffer for new position in file, if it is a different sector + if ((curPos ^ position) >= BYTE_PER_READ) + { + disc_ReadSector( file->curSect + FAT_ClustToSect(file->curClus), file->readBuffer); + } + + return 0; +} + +/*----------------------------------------------------------------- +FAT_fread(buffer, size, count, file) +Reads in size * count bytes into buffer from file, starting + from current position. It then sets the current position to the + byte after the last byte read. If it reaches the end of file + before filling the buffer then it stops reading. +void* buffer OUT: Pointer to buffer to fill. Should be at least as + big as the number of bytes required +u32 size IN: size of each item to read +u32 count IN: number of items to read +FAT_FILE* file IN: Handle of an open file +u32 OUT: returns the actual number of bytes read +-----------------------------------------------------------------*/ +u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file) +{ + int curByte; + int curSect; + u32 curClus; + u32 tempNextCluster; + + int tempVar; + + char* data = (char*)buffer; + + u32 length = size * count; + u32 remain; + + bool flagNoError = true; + + // Can't read non-existant files + if ((file == NULL) || (file->inUse == false) || size == 0 || count == 0 || buffer == NULL) + return 0; + + // Can only read files openned for reading + if (!file->read) + return 0; + + // Don't read past end of file + if (length + file->curPos > file->length) + length = file->length - file->curPos; + + remain = length; + + curByte = file->curByte; + curSect = file->curSect; + curClus = file->curClus; + + // Align to sector + tempVar = BYTE_PER_READ - curByte; + if (tempVar > remain) + tempVar = remain; + + if ((tempVar < BYTE_PER_READ) && flagNoError) + { + memcpy(data, &(file->readBuffer[curByte]), tempVar); + remain -= tempVar; + data += tempVar; + + curByte += tempVar; + if (curByte >= BYTE_PER_READ) + { + curByte = 0; + curSect++; + } + } + + // align to cluster + // tempVar is number of sectors to read + if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ) + { + tempVar = filesysSecPerClus - curSect; + } else { + tempVar = remain / BYTE_PER_READ; + } + + if ((tempVar > 0) && flagNoError) + { + disc_ReadSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data); + data += tempVar * BYTE_PER_READ; + remain -= tempVar * BYTE_PER_READ; + + curSect += tempVar; + } + + // Move onto next cluster + // It should get to here without reading anything if a cluster is due to be allocated + if (curSect >= filesysSecPerClus) + { + tempNextCluster = FAT_NextCluster(curClus); + if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) + { + curSect = filesysSecPerClus; + } else { + curSect = 0; + curClus = tempNextCluster; + if (curClus == CLUSTER_FREE) + { + flagNoError = false; + } + } + } + + // Read in whole clusters + while ((remain >= filesysBytePerClus) && flagNoError) + { + disc_ReadSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data); + data += filesysBytePerClus; + remain -= filesysBytePerClus; + + // Advance to next cluster + tempNextCluster = FAT_NextCluster(curClus); + if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) + { + curSect = filesysSecPerClus; + } else { + curSect = 0; + curClus = tempNextCluster; + if (curClus == CLUSTER_FREE) + { + flagNoError = false; + } + } + } + + // Read remaining sectors + tempVar = remain / BYTE_PER_READ; // Number of sectors left + if ((tempVar > 0) && flagNoError) + { + disc_ReadSectors (FAT_ClustToSect(curClus), tempVar, data); + data += tempVar * BYTE_PER_READ; + remain -= tempVar * BYTE_PER_READ; + curSect += tempVar; + } + + // Last remaining sector + // Check if sector wanted is different to the one started with + if ( ((file->curByte + length) >= BYTE_PER_READ) && flagNoError) + { + disc_ReadSector( curSect + FAT_ClustToSect( curClus), file->readBuffer); + if (remain > 0) + { + memcpy(data, file->readBuffer, remain); + curByte += remain; + remain = 0; + } + } + + // Length read is the wanted length minus the stuff not read + length = length - remain; + + // Update file information + file->curByte = curByte; + file->curSect = curSect; + file->curClus = curClus; + file->curPos = file->curPos + length; + return length; +} + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fwrite(buffer, size, count, file) +Writes size * count bytes into file from buffer, starting + from current position. It then sets the current position to the + byte after the last byte written. If the file was openned in + append mode it always writes to the end of the file. +const void* buffer IN: Pointer to buffer containing data. Should be + at least as big as the number of bytes to be written. +u32 size IN: size of each item to write +u32 count IN: number of items to write +FAT_FILE* file IN: Handle of an open file +u32 OUT: returns the actual number of bytes written +-----------------------------------------------------------------*/ +u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file) +{ + int curByte; + int curSect; + u32 curClus; + + u32 tempNextCluster; + int tempVar; + u32 length = size * count; + u32 remain = length; + char* data = (char*)buffer; + + char* writeBuffer; + + bool flagNoError = true; + bool flagAppending = false; + + if ((file == NULL) || (file->inUse == false) || length == 0 || buffer == NULL) + return 0; + + if (file->write) + { + // Write at current read pointer + curByte = file->curByte; + curSect = file->curSect; + curClus = file->curClus; + + // Use read buffer as write buffer + writeBuffer = file->readBuffer; + + // If it is writing past the current end of file, set appending flag + if (length + file->curPos > file->length) + { + flagAppending = true; + } + } + else if (file->append) + { + // Write at end of file + curByte = file->appByte; + curSect = file->appSect; + curClus = file->appClus; + flagAppending = true; + + // Use global buffer as write buffer, don't touch read buffer + writeBuffer = (char*)globalBuffer; + disc_ReadSector(curSect + FAT_ClustToSect(curClus), writeBuffer); + } + else + { + return 0; + } + + // Move onto next cluster if needed + if (curSect >= filesysSecPerClus) + { + curSect = 0; + tempNextCluster = FAT_NextCluster(curClus); + if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) + { + // Ran out of clusters so get a new one + curClus = FAT_LinkFreeCluster(curClus); + if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort + { + flagNoError = false; + } + memset(writeBuffer, 0, BYTE_PER_READ); + } else { + curClus = tempNextCluster; + disc_ReadSector( FAT_ClustToSect( curClus), writeBuffer); + } + } + + // Align to sector + tempVar = BYTE_PER_READ - curByte; + if (tempVar > remain) + tempVar = remain; + + if ((tempVar < BYTE_PER_READ) && flagNoError) + { + memcpy(&(writeBuffer[curByte]), data, tempVar); + remain -= tempVar; + data += tempVar; + curByte += tempVar; + + // Write buffer back to disk + disc_WriteSector (curSect + FAT_ClustToSect(curClus), writeBuffer); + + // Move onto next sector + if (curByte >= BYTE_PER_READ) + { + curByte = 0; + curSect++; + } + } + + // Align to cluster + // tempVar is number of sectors to write + if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ) + { + tempVar = filesysSecPerClus - curSect; + } else { + tempVar = remain / BYTE_PER_READ; + } + + if ((tempVar > 0) && flagNoError) + { + disc_WriteSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data); + data += tempVar * BYTE_PER_READ; + remain -= tempVar * BYTE_PER_READ; + curSect += tempVar; + } + + if (((curSect >= filesysSecPerClus) && flagNoError) && (remain > 0)) + { + curSect = 0; + tempNextCluster = FAT_NextCluster(curClus); + if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) + { + // Ran out of clusters so get a new one + curClus = FAT_LinkFreeCluster(curClus); + if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort + { + flagNoError = false; + } + } else { + curClus = tempNextCluster; + } + } + + // Write whole clusters + while ((remain >= filesysBytePerClus) && flagNoError) + { + disc_WriteSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data); + data += filesysBytePerClus; + remain -= filesysBytePerClus; + if (remain > 0) + { + tempNextCluster = FAT_NextCluster(curClus); + if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) + { + // Ran out of clusters so get a new one + curClus = FAT_LinkFreeCluster(curClus); + if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort + { + flagNoError = false; + break; + } + } else { + curClus = tempNextCluster; + } + } else { + // Allocate a new cluster when next writing the file + curSect = filesysSecPerClus; + } + } + + // Write remaining sectors + tempVar = remain / BYTE_PER_READ; // Number of sectors left + if ((tempVar > 0) && flagNoError) + { + disc_WriteSectors (FAT_ClustToSect(curClus), tempVar, data); + data += tempVar * BYTE_PER_READ; + remain -= tempVar * BYTE_PER_READ; + curSect += tempVar; + } + + // Last remaining sector + // Check if sector wanted is different to the one started with + if ( (( (file->append ? file->appByte : file->curByte) + length) >= BYTE_PER_READ) && flagNoError) + { + if (flagAppending) + { + // Zero sector before using it + memset (writeBuffer, 0, BYTE_PER_READ); + } else { + // Modify existing sector + disc_ReadSector( curSect + FAT_ClustToSect( curClus), writeBuffer); + } + if (remain > 0) { + memcpy(writeBuffer, data, remain); + curByte += remain; + remain = 0; + disc_WriteSector( curSect + FAT_ClustToSect( curClus), writeBuffer); + } + } + + // Amount read is the originally requested amount minus stuff remaining + length = length - remain; + + // Update file information + if (file->write) // Writing also shifts the read pointer + { + file->curByte = curByte; + file->curSect = curSect; + file->curClus = curClus; + file->curPos = file->curPos + length; + if (file->length < file->curPos) + { + file->length = file->curPos; + } + } + else if (file->append) // Appending doesn't affect the read pointer + { + file->appByte = curByte; + file->appSect = curSect; + file->appClus = curClus; + file->length = file->length + length; + } + + return length; +} +#endif + + +/*----------------------------------------------------------------- +FAT_feof(file) +Returns true if the end of file has been reached +FAT_FILE* file IN: Handle of an open file +bool return OUT: true if EOF, false if not +-----------------------------------------------------------------*/ +bool FAT_feof(FAT_FILE* file) +{ + if ((file == NULL) || (file->inUse == false)) + return true; // Return eof on invalid files + + return (file->length == file->curPos); +} + + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_remove (path) +Deletes the file or empty directory sepecified in path +const char* path IN: Path of item to delete +int return OUT: zero if successful, non-zero if not +-----------------------------------------------------------------*/ +int FAT_remove (const char* path) +{ + DIR_ENT dirEntry; + u32 oldWorkDirCluster; + char checkFilename[13]; + FILE_TYPE checkFiletype; + + dirEntry = FAT_DirEntFromPath (path); + + if (dirEntry.name[0] == FILE_FREE) + { + return -1; + } + + // Only delete directories if the directory is entry + if (dirEntry.attrib & ATTRIB_DIR) + { + // Change to the directory temporarily + oldWorkDirCluster = curWorkDirCluster; + FAT_chdir(path); + + // Search for files or directories, excluding the . and .. entries + checkFiletype = FAT_FindFirstFile (checkFilename); + while ((checkFilename[0] == '.') && (checkFiletype != FT_NONE)) + { + checkFiletype = FAT_FindNextFile (checkFilename); + } + + // Change back to working directory + curWorkDirCluster = oldWorkDirCluster; + + // Check that the directory is empty + if (checkFiletype != FT_NONE) + { + // Directory isn't empty + return -1; + } + } + + // Refresh directory information + dirEntry = FAT_DirEntFromPath (path); + + // Free any clusters used + FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16)); + + // Remove Directory entry + disc_ReadSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer); + ((DIR_ENT*)globalBuffer)[wrkDirOffset].name[0] = FILE_FREE; + disc_WriteSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer); + + // Flush any sectors in disc cache + disc_CacheFlush(); + + return 0; +} +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_mkdir (path) +Makes a new directory, so long as no other directory or file has + the same name. +const char* path IN: Path and filename of directory to make +int return OUT: zero if successful, non-zero if not +-----------------------------------------------------------------*/ +int FAT_mkdir (const char* path) +{ + u32 newDirCluster; + u32 parentDirCluster; + DIR_ENT dirEntry; + DIR_ENT* entries = (DIR_ENT*)globalBuffer; + int i; + + int pathPos, filePos; + char pathname[MAX_FILENAME_LENGTH]; + u32 oldDirCluster; + + if (FAT_FileExists(path) != FT_NONE) + { + return -1; // File or directory exists with that name + } + + // Find filename within path and change to that directory + oldDirCluster = curWorkDirCluster; + if (path[0] == '/') + { + curWorkDirCluster = filesysRootDirClus; + } + + pathPos = 0; + filePos = 0; + + while (path[pathPos + filePos] != '\0') + { + if (path[pathPos + filePos] == '/') + { + pathname[filePos] = '\0'; + if (FAT_chdir(pathname) == false) + { + curWorkDirCluster = oldDirCluster; + return -1; // Couldn't change directory + } + pathPos += filePos + 1; + filePos = 0; + } + pathname[filePos] = path[pathPos + filePos]; + filePos++; + } + + // Now grab the parent directory's cluster + parentDirCluster = curWorkDirCluster; + curWorkDirCluster = oldDirCluster; + + // Get a new cluster for the file + newDirCluster = FAT_LinkFreeCluster(CLUSTER_FREE); + + if (newDirCluster == CLUSTER_FREE) + { + return -1; // Couldn't get a new cluster for the directory + } + // Fill in directory entry's information + dirEntry.attrib = ATTRIB_DIR; + dirEntry.reserved = 0; + // Time and date set to system time and date + dirEntry.cTime_ms = 0; + dirEntry.cTime = getRTCtoFileTime(); + dirEntry.cDate = getRTCtoFileDate(); + dirEntry.aDate = getRTCtoFileDate(); + dirEntry.mTime = getRTCtoFileTime(); + dirEntry.mDate = getRTCtoFileDate(); + // Store cluster position into the directory entry + dirEntry.startCluster = (newDirCluster & 0xFFFF); + dirEntry.startClusterHigh = ((newDirCluster >> 16) & 0xFFFF); + // The file has no data in it - its over written so should be empty + dirEntry.fileSize = 0; + + if (FAT_AddDirEntry (path, dirEntry) == false) + { + return -1; // Couldn't add the directory entry + } + + // Create the new directory itself + memset(entries, FILE_LAST, BYTE_PER_READ); + + // Create . directory entry + dirEntry.name[0] = '.'; + // Fill name and extension with spaces + for (i = 1; i < 11; i++) + { + dirEntry.name[i] = ' '; + } + + memcpy(entries, &dirEntry, sizeof(dirEntry)); + + // Create .. directory entry + dirEntry.name[1] = '.'; + dirEntry.startCluster = (parentDirCluster & 0xFFFF); + dirEntry.startClusterHigh = ((parentDirCluster >> 16) & 0xFFFF); + + memcpy(&entries[1], &dirEntry, sizeof(dirEntry)); + + // Write entry to disc + disc_WriteSector(FAT_ClustToSect(newDirCluster), entries); + + // Flush any sectors in disc cache + disc_CacheFlush(); + return 0; +} +#endif + +/*----------------------------------------------------------------- +FAT_fgetc (handle) +Gets the next character in the file +FAT_FILE* file IN: Handle of open file +bool return OUT: character if successful, EOF if not +-----------------------------------------------------------------*/ +char FAT_fgetc (FAT_FILE* file) +{ + char c; + return (FAT_fread(&c, 1, 1, file) == 1) ? c : EOF; +} + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fputc (character, handle) +Writes the given character into the file +char c IN: Character to be written +FAT_FILE* file IN: Handle of open file +bool return OUT: character if successful, EOF if not +-----------------------------------------------------------------*/ +char FAT_fputc (char c, FAT_FILE* file) +{ + return (FAT_fwrite(&c, 1, 1, file) == 1) ? c : EOF; +} +#endif + +/*----------------------------------------------------------------- +FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file) +Gets a up to num bytes from file, stopping at the first + newline. + +CAUTION: does not do strictly streaming. I.e. it's + reading more then needed bytes and seeking back. + shouldn't matter for random access + +char *tgtBuffer OUT: buffer to write to +int num IN: size of target buffer +FAT_FILE* file IN: Handle of open file +bool return OUT: character if successful, EOF if not + + Written by MightyMax + Modified by Chishm - 2005-11-17 + * Added check for unix style text files + * Removed seek when no newline is found, since it isn't necessary +-------------------------------------------------------------------*/ +char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file) +{ + u32 curPos; + u32 readLength; + char *returnChar; + + // invalid filehandle + if (file == NULL) + { + return NULL ; + } + + // end of file + if (FAT_feof(file)==true) + { + return NULL ; + } + + // save current position + curPos = FAT_ftell(file); + + // read the full buffer (max string chars is num-1 and one end of string \0 + readLength = FAT_fread(tgtBuffer,1,num-1,file) ; + + // mark least possible end of string + tgtBuffer[readLength] = '\0' ; + + if (readLength==0) { + // return error + return NULL ; + } + + // get position of first return '\r' + returnChar = strchr(tgtBuffer,'\r'); + + // if no return is found, search for a newline + if (returnChar == NULL) + { + returnChar = strchr(tgtBuffer,'\n'); + } + + // Mark the return, if existant, as end of line/string + if (returnChar!=NULL) { + *returnChar++ = 0 ; + if (*returnChar=='\n') { // catch newline too when jumping over the end + // return to location after \r\n (strlen+2) + FAT_fseek(file,curPos+strlen(tgtBuffer)+2,SEEK_SET) ; + return tgtBuffer ; + } else { + // return to location after \r (strlen+1) + FAT_fseek(file,curPos+strlen(tgtBuffer)+1,SEEK_SET) ; + return tgtBuffer ; + } + } + + return tgtBuffer ; +} + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fputs (const char *string, FAT_FILE* file) +Writes string to file, excluding end of string character +const char *string IN: string to write +FAT_FILE* file IN: Handle of open file +bool return OUT: number of characters written if successful, + EOF if not + + Written by MightyMax + Modified by Chishm - 2005-11-17 + * Uses FAT_FILE instead of int + * writtenBytes is now u32 instead of int +-------------------------------------------------------------------*/ +int FAT_fputs (const char *string, FAT_FILE* file) +{ + u32 writtenBytes; + // save string except end of string '\0' + writtenBytes = FAT_fwrite((void *)string, 1, strlen(string), file); + + // check if we had an error + if (writtenBytes != strlen(string)) + { + // return EOF error + return EOF; + } + + // return the charcount written + return writtenBytes ; +} +#endif + + + diff --git a/backends/platform/ds/arm9/source/fat/gba_nds_fat.h b/backends/platform/ds/arm9/source/fat/gba_nds_fat.h new file mode 100644 index 0000000000..8c44fafafb --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/gba_nds_fat.h @@ -0,0 +1,888 @@ +/* + gba_nds_fat.h + By chishm (Michael Chisholm) + + Routines for reading a compact flash card + using the GBA Movie Player or M3. + + Some FAT routines are based on those in fat.c, which + is part of avrlib by Pascal Stang. + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +//--------------------------------------------------------------- + +#ifndef _GBA_NDS_FAT_INCLUDED +#define _GBA_NDS_FAT_INCLUDED + +//--------------------------------------------------------------- +// Customisable features + +// Maximum number of files open at once +// Increase this to open more files, decrease to save memory +#define MAX_FILES_OPEN 4 + +// Allow file writing +// Disable this to remove file writing support +#define CAN_WRITE_TO_DISC + +// Allow file time functions +// This adds ~ 14KB to the compiled size +// Uncomment to enable +// #define FILE_TIME_SUPPORT + +//--------------------------------------------------------------- +// Platform specific includes + +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + +#ifdef NDS + #include +#else + #include "gba_types.h" +#endif + +#ifdef FILE_TIME_SUPPORT + #include +#endif + +//--------------------------------------------------------------- +#ifdef __cplusplus +extern "C" { +#endif +//--------------------------------------------------------------- + +//--------------------------------------------------------------- +// Important constants + + +#define MAX_FILENAME_LENGTH 256 // Maximum LFN length. Don't change this one + +// File Constants +#ifndef EOF +#define EOF -1 +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +// File attributes +#define ATTRIB_ARCH 0x20 // Archive +#define ATTRIB_DIR 0x10 // Directory +#define ATTRIB_LFN 0x0F // Long file name +#define ATTRIB_VOL 0x08 // Volume +#define ATTRIB_SYS 0x04 // System +#define ATTRIB_HID 0x02 // Hidden +#define ATTRIB_RO 0x01 // Read only + + +// Directory Constants +typedef enum {FT_NONE, FT_FILE, FT_DIR} FILE_TYPE; + +// Filesystem type +typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE; + +// Open file information structure +typedef struct +{ + u32 firstCluster; + u32 length; + u32 curPos; + u32 curClus; // Current cluster to read from + int curSect; // Current sector within cluster + int curByte; // Current byte within sector + char readBuffer[512]; // Buffer used for unaligned reads + u32 appClus; // Cluster to append to + int appSect; // Sector within cluster for appending + int appByte; // Byte within sector for appending + bool read; // Can read from file + bool write; // Can write to file + bool append;// Can append to file + bool inUse; // This file is open + u32 dirEntSector; // The sector where the directory entry is stored + int dirEntOffset; // The offset within the directory sector +} FAT_FILE; + + +//----------------------------------------------------------------- +// CF Card functions + +/*----------------------------------------------------------------- +FAT_InitFiles +Reads the FAT information from the CF card. +You need to call this before reading any files. +bool return OUT: true if successful. +-----------------------------------------------------------------*/ +bool FAT_InitFiles (void); + +/*----------------------------------------------------------------- +FAT_FreeFiles +Closes all open files then resets the CF card. +Call this before exiting back to the GBAMP +bool return OUT: true if successful. +-----------------------------------------------------------------*/ +bool FAT_FreeFiles (void); + +/*----------------------------------------------------------------- +FAT_GetAlias +Get the alias (short name) of the last file or directory entry read + using GetDirEntry. Works for FindFirstFile and FindNextFile +char* alias OUT: will be filled with the alias (short filename), + should be at least 13 bytes long +bool return OUT: return true if successful +-----------------------------------------------------------------*/ +bool FAT_GetAlias (char* alias); + +/*----------------------------------------------------------------- +FAT_GetLongFilename +Get the long name of the last file or directory retrived with + GetDirEntry. Also works for FindFirstFile and FindNextFile +char* filename: OUT will be filled with the filename, should be at + least 256 bytes long +bool return OUT: return true if successful +-----------------------------------------------------------------*/ +bool FAT_GetLongFilename (char* filename); + +/*----------------------------------------------------------------- +FAT_GetFileSize +Get the file size of the last file found or openned. +This idea is based on a modification by MoonShine +u32 return OUT: the file size +-----------------------------------------------------------------*/ +u32 FAT_GetFileSize (void); + +/*----------------------------------------------------------------- +FAT_GetFileCluster +Get the first cluster of the last file found or openned. +u32 return OUT: the file start cluster +-----------------------------------------------------------------*/ +u32 FAT_GetFileCluster (void); + +/*----------------------------------------------------------------- +FAT_GetFileAttributes +Get the attributes of the last file found or openned. +u8 return OUT: the file's attributes +-----------------------------------------------------------------*/ +u8 FAT_GetFileAttributes (void); + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_FAT_SetFileAttributes +Set the attributes of a file. +const char* filename IN: The name and path of the file to modify +u8 attributes IN: The attribute values to assign +u8 mask IN: Detemines which attributes are changed +u8 return OUT: the file's new attributes +-----------------------------------------------------------------*/ +u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask); +#endif + +#ifdef FILE_TIME_SUPPORT +/*----------------------------------------------------------------- +FAT_GetFileCreationTime +Get the creation time of the last file found or openned. +time_t return OUT: the file's creation time +-----------------------------------------------------------------*/ +time_t FAT_GetFileCreationTime (void); + +/*----------------------------------------------------------------- +FAT_GetFileLastWriteTime +Get the creation time of the last file found or openned. +time_t return OUT: the file's creation time +-----------------------------------------------------------------*/ +time_t FAT_GetFileLastWriteTime (void); +#endif + +/*----------------------------------------------------------------- +FAT_FindNextFile +Gets the name of the next directory entry + (can be a file or subdirectory) +char* filename: OUT filename, must be at least 13 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindNextFile (char* filename); + +/*----------------------------------------------------------------- +FAT_FindFirstFile +Gets the name of the first directory entry and resets the count + (can be a file or subdirectory) +char* filename: OUT filename, must be at least 13 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindFirstFile (char* filename); + +/*----------------------------------------------------------------- +FAT_FindFirstFileLFN +Gets the long file name of the first directory entry and resets + the count (can be a file or subdirectory) +char* lfn: OUT long file name, must be at least 256 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindFirstFileLFN(char* lfn); + +/*----------------------------------------------------------------- +FAT_FindNextFileLFN +Gets the long file name of the next directory entry + (can be a file or subdirectory) +char* lfn: OUT long file name, must be at least 256 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindNextFileLFN(char* lfn); + +/*----------------------------------------------------------------- +FAT_FileExists +Returns the type of file +char* filename: IN filename of the file to look for +FILE_TYPE return: OUT returns FT_NONE if there is now file with + that name, FT_FILE if it is a file and FT_DIR if it is a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FileExists (const char* filename); + +/*----------------------------------------------------------------- +FAT_GetFileSystemType +FS_TYPE return: OUT returns the current file system type +-----------------------------------------------------------------*/ +FS_TYPE FAT_GetFileSystemType (void); + +/*----------------------------------------------------------------- +FAT_GetFileSystemTotalSize +u32 return: OUT returns the total disk space (used + free) +-----------------------------------------------------------------*/ +u32 FAT_GetFileSystemTotalSize (void); + +/*----------------------------------------------------------------- +FAT_chdir +Changes the current working directory +const char* path: IN null terminated string of directory separated by + forward slashes, / is root +bool return: OUT returns true if successful +-----------------------------------------------------------------*/ +bool FAT_chdir (const char* path); + + +//----------------------------------------------------------------- +// File functions + +/*----------------------------------------------------------------- +FAT_fopen(filename, mode) +Opens a file +const char* path: IN null terminated string of filename and path + separated by forward slashes, / is root +const char* mode: IN mode to open file in + Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use + "b" or "t" in any mode, as all files are openned in binary mode +FAT_FILE* return: OUT handle to open file, returns -1 if the file + couldn't be openned +-----------------------------------------------------------------*/ +FAT_FILE* FAT_fopen(const char* path, const char* mode); + +/*----------------------------------------------------------------- +FAT_fclose(file) +Closes a file +FAT_FILE* file: IN handle of the file to close +bool return OUT: true if successful, false if not +-----------------------------------------------------------------*/ +bool FAT_fclose (FAT_FILE* file); + +/*----------------------------------------------------------------- +FAT_ftell(file) +Returns the current position in a file +FAT_FILE* file: IN handle of an open file +u32 OUT: Current position +-----------------------------------------------------------------*/ +u32 FAT_ftell (FAT_FILE* file); + +/*----------------------------------------------------------------- +FAT_fseek(file, offset, origin) +Seeks to specified byte position in file +int file: IN handle of an open file +u32 offset IN: position to seek to, relative to origin +int origin IN: origin to seek from +int OUT: Returns 0 if successful, -1 if not +-----------------------------------------------------------------*/ +int FAT_fseek(FAT_FILE* file, s32 offset, int origin); + +/*----------------------------------------------------------------- +FAT_fread(buffer, size, count, file) +Reads in length number of bytes into buffer from file, starting + from current position. It then sets the current position to the + byte after the last byte read. If it reaches the end of file + before filling the buffer then it stops reading. +void* buffer OUT: Pointer to buffer to fill. Should be at least as + big as the number of bytes required +u32 size IN: size of each item to read +u32 count IN: number of items to read +FAT_FILE* file IN: Handle of an open file +u32 OUT: returns the actual number of bytes read +-----------------------------------------------------------------*/ +u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file); + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fwrite(buffer, size, count, file) +Writes size * count bytes into file from buffer, starting + from current position. It then sets the current position to the + byte after the last byte written. If the file was openned in + append mode it always writes to the end of the file. +const void* buffer IN: Pointer to buffer containing data. Should be + at least as big as the number of bytes to be written. +u32 size IN: size of each item to write +u32 count IN: number of items to write +FAT_FILE* file IN: Handle of an open file +u32 OUT: returns the actual number of bytes written +-----------------------------------------------------------------*/ +u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file); +#endif + +/*----------------------------------------------------------------- +FAT_feof(file) +Returns true if the end of file has been reached +FAT_FILE* file IN: Handle of an open file +bool return OUT: true if EOF, false if not +-----------------------------------------------------------------*/ +bool FAT_feof(FAT_FILE* file); + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_remove (path) +Deletes the file or empty directory sepecified in path +const char* path IN: Path of item to delete +int return OUT: zero if successful, non-zero if not +-----------------------------------------------------------------*/ +int FAT_remove (const char* path); +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_mkdir (path) +Makes a new directory, so long as no other directory or file has + the same name. +const char* path IN: Path and filename of directory to make +int return OUT: zero if successful, non-zero if not +-----------------------------------------------------------------*/ +int FAT_mkdir (const char* path); +#endif + +/*----------------------------------------------------------------- +FAT_fgetc (handle) +Gets the next character in the file +FAT_FILE* file IN: Handle of open file +bool return OUT: character if successful, EOF if not +-----------------------------------------------------------------*/ +char FAT_fgetc (FAT_FILE* file); + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fputc (character, handle) +Writes the given character into the file +char c IN: Character to be written +FAT_FILE* handle IN: Handle of open file +bool return OUT: character if successful, EOF if not +-----------------------------------------------------------------*/ +char FAT_fputc (char c, FAT_FILE* file); +#endif + +/*----------------------------------------------------------------- +FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file) +Gets a up to num bytes from file, stopping at the first + newline. + +CAUTION: does not do strictly streaming. I.e. it's + reading more then needed bytes and seeking back. + shouldn't matter for random access + +char *tgtBuffer OUT: buffer to write to +int num IN: size of target buffer +FAT_FILE* file IN: Handle of open file +bool return OUT: character if successful, EOF if not + + Written by MightyMax + Modified by Chishm - 2005-11-17 + * Added check for unix style text files + * Removed seek when no newline is found, since it isn't necessary +-------------------------------------------------------------------*/ +char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file) ; + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fputs (const char *string, FAT_FILE* file) +Writes string to file, excluding end of string character +const char *string IN: string to write +FAT_FILE* file IN: Handle of open file +bool return OUT: number of characters written if successful, + EOF if not + + Written by MightyMax + Modified by Chishm - 2005-11-17 + * Uses FAT_FILE instead of int + * writtenBytes is now u32 instead of int +-------------------------------------------------------------------*/ +int FAT_fputs (const char *string, FAT_FILE* file); +#endif + +//------------------------------------------------------------------ +#ifdef __cplusplus +} // extern "C" +#endif +//------------------------------------------------------------------ + +#endif // ifndef _GBA_NDS_FAT + +/* + gba_nds_fat.h + By chishm (Michael Chisholm) + + Routines for reading a compact flash card + using the GBA Movie Player or M3. + + Some FAT routines are based on those in fat.c, which + is part of avrlib by Pascal Stang. + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +//--------------------------------------------------------------- + +#ifndef _GBA_NDS_FAT_INCLUDED +#define _GBA_NDS_FAT_INCLUDED + +//--------------------------------------------------------------- +// Customisable features + +// Maximum number of files open at once +// Increase this to open more files, decrease to save memory +#define MAX_FILES_OPEN 4 + +// Allow file writing +// Disable this to remove file writing support +#define CAN_WRITE_TO_DISC + +// Allow file time functions +// This adds ~ 14KB to the compiled size +// Uncomment to enable +// #define FILE_TIME_SUPPORT + +//--------------------------------------------------------------- +// Platform specific includes + +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + +#ifdef NDS + #include +#else + #include "gba_types.h" +#endif + +#ifdef FILE_TIME_SUPPORT + #include +#endif + +//--------------------------------------------------------------- +#ifdef __cplusplus +extern "C" { +#endif +//--------------------------------------------------------------- + +//--------------------------------------------------------------- +// Important constants + + +#define MAX_FILENAME_LENGTH 256 // Maximum LFN length. Don't change this one + +// File Constants +#ifndef EOF +#define EOF -1 +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +// File attributes +#define ATTRIB_ARCH 0x20 // Archive +#define ATTRIB_DIR 0x10 // Directory +#define ATTRIB_LFN 0x0F // Long file name +#define ATTRIB_VOL 0x08 // Volume +#define ATTRIB_SYS 0x04 // System +#define ATTRIB_HID 0x02 // Hidden +#define ATTRIB_RO 0x01 // Read only + + +// Directory Constants +typedef enum {FT_NONE, FT_FILE, FT_DIR} FILE_TYPE; + +// Filesystem type +typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE; + +// Open file information structure +typedef struct +{ + u32 firstCluster; + u32 length; + u32 curPos; + u32 curClus; // Current cluster to read from + int curSect; // Current sector within cluster + int curByte; // Current byte within sector + char readBuffer[512]; // Buffer used for unaligned reads + u32 appClus; // Cluster to append to + int appSect; // Sector within cluster for appending + int appByte; // Byte within sector for appending + bool read; // Can read from file + bool write; // Can write to file + bool append;// Can append to file + bool inUse; // This file is open + u32 dirEntSector; // The sector where the directory entry is stored + int dirEntOffset; // The offset within the directory sector +} FAT_FILE; + + +//----------------------------------------------------------------- +// CF Card functions + +/*----------------------------------------------------------------- +FAT_InitFiles +Reads the FAT information from the CF card. +You need to call this before reading any files. +bool return OUT: true if successful. +-----------------------------------------------------------------*/ +bool FAT_InitFiles (void); + +/*----------------------------------------------------------------- +FAT_FreeFiles +Closes all open files then resets the CF card. +Call this before exiting back to the GBAMP +bool return OUT: true if successful. +-----------------------------------------------------------------*/ +bool FAT_FreeFiles (void); + +/*----------------------------------------------------------------- +FAT_GetAlias +Get the alias (short name) of the last file or directory entry read + using GetDirEntry. Works for FindFirstFile and FindNextFile +char* alias OUT: will be filled with the alias (short filename), + should be at least 13 bytes long +bool return OUT: return true if successful +-----------------------------------------------------------------*/ +bool FAT_GetAlias (char* alias); + +/*----------------------------------------------------------------- +FAT_GetLongFilename +Get the long name of the last file or directory retrived with + GetDirEntry. Also works for FindFirstFile and FindNextFile +char* filename: OUT will be filled with the filename, should be at + least 256 bytes long +bool return OUT: return true if successful +-----------------------------------------------------------------*/ +bool FAT_GetLongFilename (char* filename); + +/*----------------------------------------------------------------- +FAT_GetFileSize +Get the file size of the last file found or openned. +This idea is based on a modification by MoonShine +u32 return OUT: the file size +-----------------------------------------------------------------*/ +u32 FAT_GetFileSize (void); + +/*----------------------------------------------------------------- +FAT_GetFileCluster +Get the first cluster of the last file found or openned. +u32 return OUT: the file start cluster +-----------------------------------------------------------------*/ +u32 FAT_GetFileCluster (void); + +/*----------------------------------------------------------------- +FAT_GetFileAttributes +Get the attributes of the last file found or openned. +u8 return OUT: the file's attributes +-----------------------------------------------------------------*/ +u8 FAT_GetFileAttributes (void); + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_FAT_SetFileAttributes +Set the attributes of a file. +const char* filename IN: The name and path of the file to modify +u8 attributes IN: The attribute values to assign +u8 mask IN: Detemines which attributes are changed +u8 return OUT: the file's new attributes +-----------------------------------------------------------------*/ +u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask); +#endif + +#ifdef FILE_TIME_SUPPORT +/*----------------------------------------------------------------- +FAT_GetFileCreationTime +Get the creation time of the last file found or openned. +time_t return OUT: the file's creation time +-----------------------------------------------------------------*/ +time_t FAT_GetFileCreationTime (void); + +/*----------------------------------------------------------------- +FAT_GetFileLastWriteTime +Get the creation time of the last file found or openned. +time_t return OUT: the file's creation time +-----------------------------------------------------------------*/ +time_t FAT_GetFileLastWriteTime (void); +#endif + +/*----------------------------------------------------------------- +FAT_FindNextFile +Gets the name of the next directory entry + (can be a file or subdirectory) +char* filename: OUT filename, must be at least 13 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindNextFile (char* filename); + +/*----------------------------------------------------------------- +FAT_FindFirstFile +Gets the name of the first directory entry and resets the count + (can be a file or subdirectory) +char* filename: OUT filename, must be at least 13 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindFirstFile (char* filename); + +/*----------------------------------------------------------------- +FAT_FindFirstFileLFN +Gets the long file name of the first directory entry and resets + the count (can be a file or subdirectory) +char* lfn: OUT long file name, must be at least 256 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindFirstFileLFN(char* lfn); + +/*----------------------------------------------------------------- +FAT_FindNextFileLFN +Gets the long file name of the next directory entry + (can be a file or subdirectory) +char* lfn: OUT long file name, must be at least 256 chars long +FILE_TYPE return: OUT returns FT_NONE if failed, + FT_FILE if it found a file and FT_DIR if it found a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FindNextFileLFN(char* lfn); + +/*----------------------------------------------------------------- +FAT_FileExists +Returns the type of file +char* filename: IN filename of the file to look for +FILE_TYPE return: OUT returns FT_NONE if there is now file with + that name, FT_FILE if it is a file and FT_DIR if it is a directory +-----------------------------------------------------------------*/ +FILE_TYPE FAT_FileExists (const char* filename); + +/*----------------------------------------------------------------- +FAT_GetFileSystemType +FS_TYPE return: OUT returns the current file system type +-----------------------------------------------------------------*/ +FS_TYPE FAT_GetFileSystemType (void); + +/*----------------------------------------------------------------- +FAT_GetFileSystemTotalSize +u32 return: OUT returns the total disk space (used + free) +-----------------------------------------------------------------*/ +u32 FAT_GetFileSystemTotalSize (void); + +/*----------------------------------------------------------------- +FAT_chdir +Changes the current working directory +const char* path: IN null terminated string of directory separated by + forward slashes, / is root +bool return: OUT returns true if successful +-----------------------------------------------------------------*/ +bool FAT_chdir (const char* path); + + +//----------------------------------------------------------------- +// File functions + +/*----------------------------------------------------------------- +FAT_fopen(filename, mode) +Opens a file +const char* path: IN null terminated string of filename and path + separated by forward slashes, / is root +const char* mode: IN mode to open file in + Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use + "b" or "t" in any mode, as all files are openned in binary mode +FAT_FILE* return: OUT handle to open file, returns -1 if the file + couldn't be openned +-----------------------------------------------------------------*/ +FAT_FILE* FAT_fopen(const char* path, const char* mode); + +/*----------------------------------------------------------------- +FAT_fclose(file) +Closes a file +FAT_FILE* file: IN handle of the file to close +bool return OUT: true if successful, false if not +-----------------------------------------------------------------*/ +bool FAT_fclose (FAT_FILE* file); + +/*----------------------------------------------------------------- +FAT_ftell(file) +Returns the current position in a file +FAT_FILE* file: IN handle of an open file +u32 OUT: Current position +-----------------------------------------------------------------*/ +u32 FAT_ftell (FAT_FILE* file); + +/*----------------------------------------------------------------- +FAT_fseek(file, offset, origin) +Seeks to specified byte position in file +int file: IN handle of an open file +u32 offset IN: position to seek to, relative to origin +int origin IN: origin to seek from +int OUT: Returns 0 if successful, -1 if not +-----------------------------------------------------------------*/ +int FAT_fseek(FAT_FILE* file, s32 offset, int origin); + +/*----------------------------------------------------------------- +FAT_fread(buffer, size, count, file) +Reads in length number of bytes into buffer from file, starting + from current position. It then sets the current position to the + byte after the last byte read. If it reaches the end of file + before filling the buffer then it stops reading. +void* buffer OUT: Pointer to buffer to fill. Should be at least as + big as the number of bytes required +u32 size IN: size of each item to read +u32 count IN: number of items to read +FAT_FILE* file IN: Handle of an open file +u32 OUT: returns the actual number of bytes read +-----------------------------------------------------------------*/ +u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file); + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fwrite(buffer, size, count, file) +Writes size * count bytes into file from buffer, starting + from current position. It then sets the current position to the + byte after the last byte written. If the file was openned in + append mode it always writes to the end of the file. +const void* buffer IN: Pointer to buffer containing data. Should be + at least as big as the number of bytes to be written. +u32 size IN: size of each item to write +u32 count IN: number of items to write +FAT_FILE* file IN: Handle of an open file +u32 OUT: returns the actual number of bytes written +-----------------------------------------------------------------*/ +u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file); +#endif + +/*----------------------------------------------------------------- +FAT_feof(file) +Returns true if the end of file has been reached +FAT_FILE* file IN: Handle of an open file +bool return OUT: true if EOF, false if not +-----------------------------------------------------------------*/ +bool FAT_feof(FAT_FILE* file); + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_remove (path) +Deletes the file or empty directory sepecified in path +const char* path IN: Path of item to delete +int return OUT: zero if successful, non-zero if not +-----------------------------------------------------------------*/ +int FAT_remove (const char* path); +#endif + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_mkdir (path) +Makes a new directory, so long as no other directory or file has + the same name. +const char* path IN: Path and filename of directory to make +int return OUT: zero if successful, non-zero if not +-----------------------------------------------------------------*/ +int FAT_mkdir (const char* path); +#endif + +/*----------------------------------------------------------------- +FAT_fgetc (handle) +Gets the next character in the file +FAT_FILE* file IN: Handle of open file +bool return OUT: character if successful, EOF if not +-----------------------------------------------------------------*/ +char FAT_fgetc (FAT_FILE* file); + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fputc (character, handle) +Writes the given character into the file +char c IN: Character to be written +FAT_FILE* handle IN: Handle of open file +bool return OUT: character if successful, EOF if not +-----------------------------------------------------------------*/ +char FAT_fputc (char c, FAT_FILE* file); +#endif + +/*----------------------------------------------------------------- +FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file) +Gets a up to num bytes from file, stopping at the first + newline. + +CAUTION: does not do strictly streaming. I.e. it's + reading more then needed bytes and seeking back. + shouldn't matter for random access + +char *tgtBuffer OUT: buffer to write to +int num IN: size of target buffer +FAT_FILE* file IN: Handle of open file +bool return OUT: character if successful, EOF if not + + Written by MightyMax + Modified by Chishm - 2005-11-17 + * Added check for unix style text files + * Removed seek when no newline is found, since it isn't necessary +-------------------------------------------------------------------*/ +char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file) ; + +#ifdef CAN_WRITE_TO_DISC +/*----------------------------------------------------------------- +FAT_fputs (const char *string, FAT_FILE* file) +Writes string to file, excluding end of string character +const char *string IN: string to write +FAT_FILE* file IN: Handle of open file +bool return OUT: number of characters written if successful, + EOF if not + + Written by MightyMax + Modified by Chishm - 2005-11-17 + * Uses FAT_FILE instead of int + * writtenBytes is now u32 instead of int +-------------------------------------------------------------------*/ +int FAT_fputs (const char *string, FAT_FILE* file); +#endif + +//------------------------------------------------------------------ +#ifdef __cplusplus +} // extern "C" +#endif +//------------------------------------------------------------------ + +#endif // ifndef _GBA_NDS_FAT + diff --git a/backends/platform/ds/arm9/source/fat/io_efa2.c b/backends/platform/ds/arm9/source/fat/io_efa2.c new file mode 100644 index 0000000000..f3aa65cfcb --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_efa2.c @@ -0,0 +1,642 @@ +/* +io_efa2.c by CyteX + +Based on io_mpfc.c by chishm (Michael Chisholm) + +Hardware Routines for reading the NAND flash located on +EFA2 flash carts + +This software is completely free. No warranty is provided. +If you use it, please give me credit and email me about your +project at cytex gmx de and do not forget to also +drop chishm hotmail com a line + +See gba_nds_fat.txt for help and license details. +*/ + +#include "io_efa2.h" + +#ifdef SUPPORT_EFA2 + +// +// EFA2 register addresses +// + +// RTC registers +#define REG_RTC_CLK *(vu16*)0x080000c4 +#define REG_RTC_EN *(vu16*)0x080000c8 + +// "Magic" registers used for unlock/lock sequences +#define REG_EFA2_MAGIC_A *(vu16*)0x09fe0000 +#define REG_EFA2_MAGIC_B *(vu16*)0x08000000 +#define REG_EFA2_MAGIC_C *(vu16*)0x08020000 +#define REG_EFA2_MAGIC_D *(vu16*)0x08040000 +#define REG_EFA2_MAGIC_E *(vu16*)0x09fc0000 + +// NAND flash lock/unlock register +#define REG_EFA2_NAND_LOCK *(vu16*)0x09c40000 +// NAND flash enable register +#define REG_EFA2_NAND_EN *(vu16*)0x09400000 +// NAND flash command write register +#define REG_EFA2_NAND_CMD *(vu8*)0x09ffffe2 +// NAND flash address/data write register +#define REG_EFA2_NAND_WR *(vu8*)0x09ffffe0 +// NAND flash data read register +#define REG_EFA2_NAND_RD *(vu8*)0x09ffc000 + +// ID of Samsung K9K1G NAND flash chip +#define EFA2_NAND_ID 0xEC79A5C0 + +// first sector of udisk +#define EFA2_UDSK_START 0x40 + +// +// EFA2 access functions +// + +// deactivate RTC ports +inline void efa2_rtc_deactivate(void) { + REG_RTC_EN = 0; +} + +// unlock register access +void efa2_reg_unlock(void) { + REG_EFA2_MAGIC_A = 0x0d200; + REG_EFA2_MAGIC_B = 0x01500; + REG_EFA2_MAGIC_C = 0x0d200; + REG_EFA2_MAGIC_D = 0x01500; +} + +// finish/lock register access +inline void efa2_reg_lock(void) { + REG_EFA2_MAGIC_E = 0x1500; +} + +// global reset/init/enable/unlock ? +void efa2_global_unlock(void) { + efa2_reg_unlock(); + *(vu16*)0x09880000 = 0x08000; + efa2_reg_lock(); +} + +// global lock, stealth mode +void efa2_global_lock(void) { + // quite sure there is such a sequence, but haven't had + // a look for it upto now +} + +// unlock NAND Flash +void efa2_nand_unlock(void) { + efa2_reg_unlock(); + REG_EFA2_NAND_LOCK = 0x01500; + efa2_reg_lock(); +} + +// lock NAND Flash +void efa2_nand_lock(void) { + efa2_reg_unlock(); + REG_EFA2_NAND_LOCK = 0x0d200; + efa2_reg_lock(); +} + +// +// Set NAND Flash chip enable and write protection bits ? +// +// val | ~CE | ~WP | +// -----+-----+-----+ +// 0 | 0 | 0 | +// 1 | 1 | 0 | +// 3 | 1 | 1 | +// -----+-----+-----+ +// +void efa2_nand_enable(u16 val) { + efa2_reg_unlock(); + REG_EFA2_NAND_EN = val; + efa2_reg_lock(); +} + +// +// Perform NAND reset +// NAND has to be unlocked and enabled when called +// +inline void efa2_nand_reset(void) { + REG_EFA2_NAND_CMD = 0xff; // write reset command +} + +// +// Read out NAND ID information, could be used for card detection +// +// | EFA2 1GBit | +// ------------------+------------+ +// maker code | 0xEC | +// device code | 0x79 | +// don't care | 0xA5 | +// multi plane code | 0xC0 | +// ------------------+------------+ +// +u32 efa2_nand_id(void) { + u8 byte; + u32 id; + + efa2_nand_unlock(); + efa2_nand_enable(1); + + REG_EFA2_NAND_CMD = 0x90; // write id command + REG_EFA2_NAND_WR = 0x00; // (dummy) address cycle + byte = REG_EFA2_NAND_RD; // read maker code + id = byte; + byte = REG_EFA2_NAND_RD; // read device code + id = (id << 8) | byte; + byte = REG_EFA2_NAND_RD; // read don't care + id = (id << 8) | byte; + byte = REG_EFA2_NAND_RD; // read multi plane code + id = (id << 8) | byte; + + efa2_nand_enable(0); + efa2_nand_lock(); + return (id); +} + +// +// Start of gba_nds_fat block device description +// + +/*----------------------------------------------------------------- +EFA2_ClearStatus +Reads and checks NAND status information +bool return OUT: true if NAND is idle +-----------------------------------------------------------------*/ +bool EFA2_ClearStatus (void) +{ + // tbd: currently there is no write support, so always return + // true, there is no possibility for pending operations + return true; +} + +/*----------------------------------------------------------------- +EFA2_IsInserted +Checks to see if the NAND chip used by the EFA2 is present +bool return OUT: true if the correct NAND chip is found +-----------------------------------------------------------------*/ +bool EFA2_IsInserted (void) +{ + EFA2_ClearStatus(); + return (efa2_nand_id() == EFA2_NAND_ID); +} + +/*----------------------------------------------------------------- +EFA2_ReadSectors +Read "numSecs" 512 byte sectors starting from "sector" into "buffer" +No error correction, no use of spare cells, no use of R/~B signal +u32 sector IN: number of first 512 byte sector to be read +u8 numSecs IN: number of 512 byte sectors to read, +1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool EFA2_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + int j = (numSecs > 0 ? numSecs : 256); + +#ifndef _CF_ALLOW_UNALIGNED + u8 byte; + u16 word; +#endif + + // NAND page 0x40 (EFA2_UDSK_START) contains the MBR of the + // udisk and thus is sector 0. The original EFA2 firmware + // does never look at this, it only watches page 0x60, which + // contains the boot block of the FAT16 partition. That is + // fixed, so the EFA2 udisk must not be reformated, else + // the ARK Octopus and also the original Firmware won't be + // able to access the udisk anymore and I have to write a + // recovery tool. + u32 page = EFA2_UDSK_START + sector; + + // future enhancement: wait for possible write operations to + // be finisched + if (!EFA2_ClearStatus()) return false; + + efa2_nand_unlock(); + efa2_nand_enable(1); + efa2_nand_reset(); + + // set NAND to READ1 operation mode and transfer page address + REG_EFA2_NAND_CMD = 0x00; // write READ1 command + REG_EFA2_NAND_WR = 0x00; // write address [7:0] + REG_EFA2_NAND_WR = (page ) & 0xff; // write address [15:8] + REG_EFA2_NAND_WR = (page >> 8 ) & 0xff; // write address[23:16] + REG_EFA2_NAND_WR = (page >> 16) & 0xff; // write address[26:24] + + // Due to a bug in EFA2 design there is need to waste some cycles + // "by hand" instead the possibility to check the R/~B port of + // the NAND flash via a register. The RTC deactivation is only + // there to make sure the loop won't be optimized by the compiler + for (i=0 ; i < 3 ; i++) efa2_rtc_deactivate(); + + while (j--) + { + // read page data +#ifdef _CF_ALLOW_UNALIGNED + // slow byte access to RAM, but works in principle + for (i=0 ; i < 512 ; i++) + ((u8*)buffer)[i] = REG_EFA2_NAND_RD; +#else + // a bit faster, but DMA is not possible + for (i=0 ; i < 256 ; i++) { + byte = REG_EFA2_NAND_RD; // read lo-byte + word = byte; + byte = REG_EFA2_NAND_RD; // read hi-byte + word = word | (byte << 8); + ((u16*)buffer)[i] = word; + } +#endif + } + + efa2_nand_enable(0); + efa2_nand_lock(); + return true; +} + + +/*----------------------------------------------------------------- +EFA2_WriteSectors +Write "numSecs" 512 byte sectors starting at "sector" from "buffer" +u32 sector IN: address of 512 byte sector on card to write +u8 numSecs IN: number of 512 byte sectors to write +1 to 256 sectors can be written, 0 = 256 +void* buffer IN: pointer to 512 byte buffer to read data from +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool EFA2_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + // Upto now I focused on reading NAND, write operations + // will follow + return false; +} + +/*----------------------------------------------------------------- +EFA2_Shutdown +unload the EFA2 interface +-----------------------------------------------------------------*/ +bool EFA2_Shutdown(void) +{ + return EFA2_ClearStatus(); +} + +/*----------------------------------------------------------------- +EFA2_StartUp +initializes the EFA2 card, returns true if successful, +otherwise returns false +-----------------------------------------------------------------*/ +bool EFA2_StartUp(void) +{ + efa2_global_unlock(); + return (efa2_nand_id() == EFA2_NAND_ID); +} + +/*----------------------------------------------------------------- +the actual interface structure +-----------------------------------------------------------------*/ +IO_INTERFACE io_efa2 = { + DEVICE_TYPE_EFA2, + FEATURE_MEDIUM_CANREAD | FEATURE_SLOT_GBA, + (FN_MEDIUM_STARTUP)&EFA2_StartUp, + (FN_MEDIUM_ISINSERTED)&EFA2_IsInserted, + (FN_MEDIUM_READSECTORS)&EFA2_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&EFA2_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&EFA2_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&EFA2_Shutdown +}; + +/*----------------------------------------------------------------- +EFA2_GetInterface +returns the interface structure to host +-----------------------------------------------------------------*/ +LPIO_INTERFACE EFA2_GetInterface(void) { + return &io_efa2; +} + +#endif // SUPPORT_EFA2 +/* +io_efa2.c by CyteX + +Based on io_mpfc.c by chishm (Michael Chisholm) + +Hardware Routines for reading the NAND flash located on +EFA2 flash carts + +This software is completely free. No warranty is provided. +If you use it, please give me credit and email me about your +project at cytex gmx de and do not forget to also +drop chishm hotmail com a line + +See gba_nds_fat.txt for help and license details. +*/ + +#include "io_efa2.h" + +#ifdef SUPPORT_EFA2 + +// +// EFA2 register addresses +// + +// RTC registers +#define REG_RTC_CLK *(vu16*)0x080000c4 +#define REG_RTC_EN *(vu16*)0x080000c8 + +// "Magic" registers used for unlock/lock sequences +#define REG_EFA2_MAGIC_A *(vu16*)0x09fe0000 +#define REG_EFA2_MAGIC_B *(vu16*)0x08000000 +#define REG_EFA2_MAGIC_C *(vu16*)0x08020000 +#define REG_EFA2_MAGIC_D *(vu16*)0x08040000 +#define REG_EFA2_MAGIC_E *(vu16*)0x09fc0000 + +// NAND flash lock/unlock register +#define REG_EFA2_NAND_LOCK *(vu16*)0x09c40000 +// NAND flash enable register +#define REG_EFA2_NAND_EN *(vu16*)0x09400000 +// NAND flash command write register +#define REG_EFA2_NAND_CMD *(vu8*)0x09ffffe2 +// NAND flash address/data write register +#define REG_EFA2_NAND_WR *(vu8*)0x09ffffe0 +// NAND flash data read register +#define REG_EFA2_NAND_RD *(vu8*)0x09ffc000 + +// ID of Samsung K9K1G NAND flash chip +#define EFA2_NAND_ID 0xEC79A5C0 + +// first sector of udisk +#define EFA2_UDSK_START 0x40 + +// +// EFA2 access functions +// + +// deactivate RTC ports +inline void efa2_rtc_deactivate(void) { + REG_RTC_EN = 0; +} + +// unlock register access +void efa2_reg_unlock(void) { + REG_EFA2_MAGIC_A = 0x0d200; + REG_EFA2_MAGIC_B = 0x01500; + REG_EFA2_MAGIC_C = 0x0d200; + REG_EFA2_MAGIC_D = 0x01500; +} + +// finish/lock register access +inline void efa2_reg_lock(void) { + REG_EFA2_MAGIC_E = 0x1500; +} + +// global reset/init/enable/unlock ? +void efa2_global_unlock(void) { + efa2_reg_unlock(); + *(vu16*)0x09880000 = 0x08000; + efa2_reg_lock(); +} + +// global lock, stealth mode +void efa2_global_lock(void) { + // quite sure there is such a sequence, but haven't had + // a look for it upto now +} + +// unlock NAND Flash +void efa2_nand_unlock(void) { + efa2_reg_unlock(); + REG_EFA2_NAND_LOCK = 0x01500; + efa2_reg_lock(); +} + +// lock NAND Flash +void efa2_nand_lock(void) { + efa2_reg_unlock(); + REG_EFA2_NAND_LOCK = 0x0d200; + efa2_reg_lock(); +} + +// +// Set NAND Flash chip enable and write protection bits ? +// +// val | ~CE | ~WP | +// -----+-----+-----+ +// 0 | 0 | 0 | +// 1 | 1 | 0 | +// 3 | 1 | 1 | +// -----+-----+-----+ +// +void efa2_nand_enable(u16 val) { + efa2_reg_unlock(); + REG_EFA2_NAND_EN = val; + efa2_reg_lock(); +} + +// +// Perform NAND reset +// NAND has to be unlocked and enabled when called +// +inline void efa2_nand_reset(void) { + REG_EFA2_NAND_CMD = 0xff; // write reset command +} + +// +// Read out NAND ID information, could be used for card detection +// +// | EFA2 1GBit | +// ------------------+------------+ +// maker code | 0xEC | +// device code | 0x79 | +// don't care | 0xA5 | +// multi plane code | 0xC0 | +// ------------------+------------+ +// +u32 efa2_nand_id(void) { + u8 byte; + u32 id; + + efa2_nand_unlock(); + efa2_nand_enable(1); + + REG_EFA2_NAND_CMD = 0x90; // write id command + REG_EFA2_NAND_WR = 0x00; // (dummy) address cycle + byte = REG_EFA2_NAND_RD; // read maker code + id = byte; + byte = REG_EFA2_NAND_RD; // read device code + id = (id << 8) | byte; + byte = REG_EFA2_NAND_RD; // read don't care + id = (id << 8) | byte; + byte = REG_EFA2_NAND_RD; // read multi plane code + id = (id << 8) | byte; + + efa2_nand_enable(0); + efa2_nand_lock(); + return (id); +} + +// +// Start of gba_nds_fat block device description +// + +/*----------------------------------------------------------------- +EFA2_ClearStatus +Reads and checks NAND status information +bool return OUT: true if NAND is idle +-----------------------------------------------------------------*/ +bool EFA2_ClearStatus (void) +{ + // tbd: currently there is no write support, so always return + // true, there is no possibility for pending operations + return true; +} + +/*----------------------------------------------------------------- +EFA2_IsInserted +Checks to see if the NAND chip used by the EFA2 is present +bool return OUT: true if the correct NAND chip is found +-----------------------------------------------------------------*/ +bool EFA2_IsInserted (void) +{ + EFA2_ClearStatus(); + return (efa2_nand_id() == EFA2_NAND_ID); +} + +/*----------------------------------------------------------------- +EFA2_ReadSectors +Read "numSecs" 512 byte sectors starting from "sector" into "buffer" +No error correction, no use of spare cells, no use of R/~B signal +u32 sector IN: number of first 512 byte sector to be read +u8 numSecs IN: number of 512 byte sectors to read, +1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool EFA2_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + int j = (numSecs > 0 ? numSecs : 256); + +#ifndef _CF_ALLOW_UNALIGNED + u8 byte; + u16 word; +#endif + + // NAND page 0x40 (EFA2_UDSK_START) contains the MBR of the + // udisk and thus is sector 0. The original EFA2 firmware + // does never look at this, it only watches page 0x60, which + // contains the boot block of the FAT16 partition. That is + // fixed, so the EFA2 udisk must not be reformated, else + // the ARK Octopus and also the original Firmware won't be + // able to access the udisk anymore and I have to write a + // recovery tool. + u32 page = EFA2_UDSK_START + sector; + + // future enhancement: wait for possible write operations to + // be finisched + if (!EFA2_ClearStatus()) return false; + + efa2_nand_unlock(); + efa2_nand_enable(1); + efa2_nand_reset(); + + // set NAND to READ1 operation mode and transfer page address + REG_EFA2_NAND_CMD = 0x00; // write READ1 command + REG_EFA2_NAND_WR = 0x00; // write address [7:0] + REG_EFA2_NAND_WR = (page ) & 0xff; // write address [15:8] + REG_EFA2_NAND_WR = (page >> 8 ) & 0xff; // write address[23:16] + REG_EFA2_NAND_WR = (page >> 16) & 0xff; // write address[26:24] + + // Due to a bug in EFA2 design there is need to waste some cycles + // "by hand" instead the possibility to check the R/~B port of + // the NAND flash via a register. The RTC deactivation is only + // there to make sure the loop won't be optimized by the compiler + for (i=0 ; i < 3 ; i++) efa2_rtc_deactivate(); + + while (j--) + { + // read page data +#ifdef _CF_ALLOW_UNALIGNED + // slow byte access to RAM, but works in principle + for (i=0 ; i < 512 ; i++) + ((u8*)buffer)[i] = REG_EFA2_NAND_RD; +#else + // a bit faster, but DMA is not possible + for (i=0 ; i < 256 ; i++) { + byte = REG_EFA2_NAND_RD; // read lo-byte + word = byte; + byte = REG_EFA2_NAND_RD; // read hi-byte + word = word | (byte << 8); + ((u16*)buffer)[i] = word; + } +#endif + } + + efa2_nand_enable(0); + efa2_nand_lock(); + return true; +} + + +/*----------------------------------------------------------------- +EFA2_WriteSectors +Write "numSecs" 512 byte sectors starting at "sector" from "buffer" +u32 sector IN: address of 512 byte sector on card to write +u8 numSecs IN: number of 512 byte sectors to write +1 to 256 sectors can be written, 0 = 256 +void* buffer IN: pointer to 512 byte buffer to read data from +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool EFA2_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + // Upto now I focused on reading NAND, write operations + // will follow + return false; +} + +/*----------------------------------------------------------------- +EFA2_Shutdown +unload the EFA2 interface +-----------------------------------------------------------------*/ +bool EFA2_Shutdown(void) +{ + return EFA2_ClearStatus(); +} + +/*----------------------------------------------------------------- +EFA2_StartUp +initializes the EFA2 card, returns true if successful, +otherwise returns false +-----------------------------------------------------------------*/ +bool EFA2_StartUp(void) +{ + efa2_global_unlock(); + return (efa2_nand_id() == EFA2_NAND_ID); +} + +/*----------------------------------------------------------------- +the actual interface structure +-----------------------------------------------------------------*/ +IO_INTERFACE io_efa2 = { + DEVICE_TYPE_EFA2, + FEATURE_MEDIUM_CANREAD | FEATURE_SLOT_GBA, + (FN_MEDIUM_STARTUP)&EFA2_StartUp, + (FN_MEDIUM_ISINSERTED)&EFA2_IsInserted, + (FN_MEDIUM_READSECTORS)&EFA2_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&EFA2_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&EFA2_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&EFA2_Shutdown +}; + +/*----------------------------------------------------------------- +EFA2_GetInterface +returns the interface structure to host +-----------------------------------------------------------------*/ +LPIO_INTERFACE EFA2_GetInterface(void) { + return &io_efa2; +} + +#endif // SUPPORT_EFA2 diff --git a/backends/platform/ds/arm9/source/fat/io_efa2.h b/backends/platform/ds/arm9/source/fat/io_efa2.h new file mode 100644 index 0000000000..27c4e9beb8 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_efa2.h @@ -0,0 +1,56 @@ +/* + io_efa2.h by CyteX + + Based on io_mpfc.h by chishm (Michael Chisholm) + + Hardware Routines for reading the NAND flash located on + EFA2 flash carts + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at cytex gmx de and do not forget to also + drop chishm hotmail com a line + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_EFA2_H +#define IO_EFA2_H + +// 'EFA2' +#define DEVICE_TYPE_EFA2 0x32414645 + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE EFA2_GetInterface(void); + +#endif // define IO_EFA2_H +/* + io_efa2.h by CyteX + + Based on io_mpfc.h by chishm (Michael Chisholm) + + Hardware Routines for reading the NAND flash located on + EFA2 flash carts + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at cytex gmx de and do not forget to also + drop chishm hotmail com a line + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_EFA2_H +#define IO_EFA2_H + +// 'EFA2' +#define DEVICE_TYPE_EFA2 0x32414645 + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE EFA2_GetInterface(void); + +#endif // define IO_EFA2_H diff --git a/backends/platform/ds/arm9/source/fat/io_fcsr.c b/backends/platform/ds/arm9/source/fat/io_fcsr.c new file mode 100644 index 0000000000..8ca311ac92 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_fcsr.c @@ -0,0 +1,658 @@ +/* + io_fcsr.c based on + + compact_flash.c + By chishm (Michael Chisholm) + + Hardware Routines for using a GBA Flash Cart and SRAM as a + block device. + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. + + The file system must be 512 byte aligned, in cart address space. + SRAM is supported. +*/ + + +#include "io_fcsr.h" + +#ifdef SUPPORT_FCSR +#include + +//--------------------------------------------------------------- +// DMA +#ifdef _CF_USE_DMA + #ifndef NDS + #include "gba_dma.h" + #else + #include + #ifdef ARM9 + #include + #endif + #endif +#endif + +#ifdef NDS + #define SRAM_START 0x0A000000 +#else + #define SRAM_START 0x0E000000 +#endif + +#define NO_SRAM 0xFFFFFFFF + +#define FCSR 0x52534346 +const char FCSR_LabelString[] = " Chishm FAT"; + +u8* FCSR_FileSysPointer = 0; +u8* FCSR_SramSectorPointer[4] = {0,0,0,0}; +u32 FCSR_SramSectorStart[4] = {0,0,0,0}; +u32 FCSR_SramSectorEnd[4] = {0,0,0,0}; + +/*----------------------------------------------------------------- +FCSR_IsInserted +Is a GBA Flash Cart with a valid file system inserted? +bool return OUT: true if a GBA FC card is inserted +-----------------------------------------------------------------*/ +bool FCSR_IsInserted (void) +{ + bool flagFoundFileSys = false; + + u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string + + // Search for file system + while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space + { + while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000)) + fileSysPointer += 0x40; + if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000)) + { + flagFoundFileSys = true; + } else { + fileSysPointer += 0x80; + } + } + + return flagFoundFileSys; +} + + +/*----------------------------------------------------------------- +FCSR_ClearStatus +Finish any pending operations +bool return OUT: always true for GBA FC +-----------------------------------------------------------------*/ +bool FCSR_ClearStatus (void) +{ + return true; +} + + +/*----------------------------------------------------------------- +FCSR_ReadSectors +Read 512 byte sector numbered "sector" into "buffer" +u32 sector IN: address of first 512 byte sector on Flash Cart to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool FCSR_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + bool flagSramSector = false; + int numSectors = (numSecs > 0 ? numSecs : 256); + int readLength = numSectors * BYTE_PER_READ; + u8* src;; + u8* dst; + + // Find which region this read is in + for (i = 0; (i < 4) && !flagSramSector; i++) + { + if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i])) + { + flagSramSector = true; + break; + } + } + + // Make sure read will be completely in SRAM range if it is partially there + if ( flagSramSector && ((sector + numSectors) > FCSR_SramSectorEnd[i])) + return false; + + // Copy data to buffer + if (flagSramSector) + { + src = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ; + } else { + src = FCSR_FileSysPointer + sector * BYTE_PER_READ; + } + dst = (u8*)buffer; + + if (flagSramSector) + { + while (readLength--) + { + *dst++ = *src++; + } + } else { // Reading from Cart ROM + +#ifdef _CF_USE_DMA + #ifdef NDS + #ifdef ARM9 + DC_FlushRange( buffer, readLength); + #endif // ARM9 + DMA3_SRC = (u32)src; + DMA3_DEST = (u32)buffer; + DMA3_CR = (readLength >> 1) | DMA_COPY_HALFWORDS; + #else // ! NDS + DMA3COPY ( src, buffer, (readLength >> 1) | DMA16 | DMA_ENABLE); + #endif // NDS +#else // !_CF_USE_DMA + memcpy (buffer, src, readLength); +#endif // _CF_USE_DMA + + } // if (flagSramSector) + + return true; +} + +/*----------------------------------------------------------------- +FCSR_WriteSectors +Write 512 byte sector numbered "sector" from "buffer" +u32 sector IN: address of 512 byte sector on Flash Cart to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer IN: pointer to 512 byte buffer to read data from +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool FCSR_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + bool flagSramSector = false; + int writeLength = (numSecs > 0 ? numSecs : 256) * BYTE_PER_READ; + u8* src = (u8*) buffer; + u8* dst; + + // Find which region this sector belongs in + for (i = 0; (i < 4) && !flagSramSector; i++) + { + if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i])) + { + flagSramSector = true; + break; + } + } + + if (!flagSramSector) + return false; + + // Entire write must be within an SRAM region + if ((sector + (numSecs > 0 ? numSecs : 256)) > FCSR_SramSectorEnd[i]) + return false; + + // Copy data to SRAM + dst = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ; + while (writeLength--) + { + *dst++ = *src++; + } + + return true; +} + +/*----------------------------------------------------------------- +FCSR_Shutdown +unload the Flash Cart interface +-----------------------------------------------------------------*/ +bool FCSR_Shutdown(void) +{ + int i; + if (FCSR_ClearStatus() == false) + return false; + + FCSR_FileSysPointer = 0; + + for (i=0; i < 4; i++) + { + FCSR_SramSectorPointer[i] = 0; + FCSR_SramSectorStart[i] = 0; + FCSR_SramSectorEnd[i] = 0; + } + return true; +} + +/*----------------------------------------------------------------- +FCSR_StartUp +initializes the Flash Cart interface, returns true if successful, +otherwise returns false +-----------------------------------------------------------------*/ +bool FCSR_StartUp(void) +{ + bool flagFoundFileSys = false; + int i; + int SramRegionSize[4]; + u8* srcByte; + u8* destByte; + + u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string + + // Search for file system + while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space + { + while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000)) + fileSysPointer += 0x40; + if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000)) + { + flagFoundFileSys = true; + } else { + fileSysPointer += 0x80; + } + } + + if (!flagFoundFileSys) + return false; + + // Flash cart file system pointer has been found + FCSR_FileSysPointer = (u8*)(fileSysPointer - 0x40); + + // Get SRAM sector regions from header block + for (i = 0; i < 4; i++) + { + FCSR_SramSectorStart[i] = fileSysPointer[i+4]; + SramRegionSize[i] = fileSysPointer[i+8]; + FCSR_SramSectorEnd[i] = FCSR_SramSectorStart[i] + SramRegionSize[i]; + } + + // Calculate SRAM region pointers + FCSR_SramSectorPointer[0] = (u8*)(SRAM_START + 4); + for (i = 1; i < 4; i++) + { + FCSR_SramSectorPointer[i] = FCSR_SramSectorPointer[i-1] + (SramRegionSize[i-1] * BYTE_PER_READ); + } + + // Initialise SRAM with overlay if it hasn't been done so + if ( (*((u8*)SRAM_START) != 'F') || (*((u8*)(SRAM_START+1)) != 'C') || (*((u8*)(SRAM_START+2)) != 'S') || (*((u8*)(SRAM_START+3)) != 'R') ) + { + *((u8*)SRAM_START) = 'F'; + *((u8*)(SRAM_START+1)) = 'C'; + *((u8*)(SRAM_START+2)) = 'S'; + *((u8*)(SRAM_START+3)) = 'R'; + + for (i = 0; i < 4; i++) + { + srcByte = FCSR_FileSysPointer + (FCSR_SramSectorStart[i] * BYTE_PER_READ); + destByte = FCSR_SramSectorPointer[i]; + while (srcByte < FCSR_FileSysPointer + (FCSR_SramSectorEnd[i] * BYTE_PER_READ) ) + *destByte++ = *srcByte++; + } + } + + // Get SRAM sector regions from header block + for (i = 0; i < 4; i++) + { + if (SramRegionSize[i] == 0) + { + FCSR_SramSectorStart[i] = NO_SRAM; + FCSR_SramSectorEnd[i] = NO_SRAM; + } + } + + return true; +} + +/*----------------------------------------------------------------- +the actual interface structure +-----------------------------------------------------------------*/ +IO_INTERFACE io_fcsr = { + DEVICE_TYPE_FCSR, // 'FCSR' + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA, + (FN_MEDIUM_STARTUP)&FCSR_StartUp, + (FN_MEDIUM_ISINSERTED)&FCSR_IsInserted, + (FN_MEDIUM_READSECTORS)&FCSR_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&FCSR_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&FCSR_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&FCSR_Shutdown +} ; + +/*----------------------------------------------------------------- +FCSR_GetInterface +returns the interface structure to host +-----------------------------------------------------------------*/ +LPIO_INTERFACE FCSR_GetInterface(void) { + return &io_fcsr ; +} ; + +#endif // SUPPORT_FCSR +/* + io_fcsr.c based on + + compact_flash.c + By chishm (Michael Chisholm) + + Hardware Routines for using a GBA Flash Cart and SRAM as a + block device. + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. + + The file system must be 512 byte aligned, in cart address space. + SRAM is supported. +*/ + + +#include "io_fcsr.h" + +#ifdef SUPPORT_FCSR +#include + +//--------------------------------------------------------------- +// DMA +#ifdef _CF_USE_DMA + #ifndef NDS + #include "gba_dma.h" + #else + #include + #ifdef ARM9 + #include + #endif + #endif +#endif + +#ifdef NDS + #define SRAM_START 0x0A000000 +#else + #define SRAM_START 0x0E000000 +#endif + +#define NO_SRAM 0xFFFFFFFF + +#define FCSR 0x52534346 +const char FCSR_LabelString[] = " Chishm FAT"; + +u8* FCSR_FileSysPointer = 0; +u8* FCSR_SramSectorPointer[4] = {0,0,0,0}; +u32 FCSR_SramSectorStart[4] = {0,0,0,0}; +u32 FCSR_SramSectorEnd[4] = {0,0,0,0}; + +/*----------------------------------------------------------------- +FCSR_IsInserted +Is a GBA Flash Cart with a valid file system inserted? +bool return OUT: true if a GBA FC card is inserted +-----------------------------------------------------------------*/ +bool FCSR_IsInserted (void) +{ + bool flagFoundFileSys = false; + + u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string + + // Search for file system + while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space + { + while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000)) + fileSysPointer += 0x40; + if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000)) + { + flagFoundFileSys = true; + } else { + fileSysPointer += 0x80; + } + } + + return flagFoundFileSys; +} + + +/*----------------------------------------------------------------- +FCSR_ClearStatus +Finish any pending operations +bool return OUT: always true for GBA FC +-----------------------------------------------------------------*/ +bool FCSR_ClearStatus (void) +{ + return true; +} + + +/*----------------------------------------------------------------- +FCSR_ReadSectors +Read 512 byte sector numbered "sector" into "buffer" +u32 sector IN: address of first 512 byte sector on Flash Cart to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool FCSR_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + bool flagSramSector = false; + int numSectors = (numSecs > 0 ? numSecs : 256); + int readLength = numSectors * BYTE_PER_READ; + u8* src;; + u8* dst; + + // Find which region this read is in + for (i = 0; (i < 4) && !flagSramSector; i++) + { + if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i])) + { + flagSramSector = true; + break; + } + } + + // Make sure read will be completely in SRAM range if it is partially there + if ( flagSramSector && ((sector + numSectors) > FCSR_SramSectorEnd[i])) + return false; + + // Copy data to buffer + if (flagSramSector) + { + src = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ; + } else { + src = FCSR_FileSysPointer + sector * BYTE_PER_READ; + } + dst = (u8*)buffer; + + if (flagSramSector) + { + while (readLength--) + { + *dst++ = *src++; + } + } else { // Reading from Cart ROM + +#ifdef _CF_USE_DMA + #ifdef NDS + #ifdef ARM9 + DC_FlushRange( buffer, readLength); + #endif // ARM9 + DMA3_SRC = (u32)src; + DMA3_DEST = (u32)buffer; + DMA3_CR = (readLength >> 1) | DMA_COPY_HALFWORDS; + #else // ! NDS + DMA3COPY ( src, buffer, (readLength >> 1) | DMA16 | DMA_ENABLE); + #endif // NDS +#else // !_CF_USE_DMA + memcpy (buffer, src, readLength); +#endif // _CF_USE_DMA + + } // if (flagSramSector) + + return true; +} + +/*----------------------------------------------------------------- +FCSR_WriteSectors +Write 512 byte sector numbered "sector" from "buffer" +u32 sector IN: address of 512 byte sector on Flash Cart to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer IN: pointer to 512 byte buffer to read data from +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool FCSR_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + bool flagSramSector = false; + int writeLength = (numSecs > 0 ? numSecs : 256) * BYTE_PER_READ; + u8* src = (u8*) buffer; + u8* dst; + + // Find which region this sector belongs in + for (i = 0; (i < 4) && !flagSramSector; i++) + { + if ((sector >= FCSR_SramSectorStart[i]) && (sector < FCSR_SramSectorEnd[i])) + { + flagSramSector = true; + break; + } + } + + if (!flagSramSector) + return false; + + // Entire write must be within an SRAM region + if ((sector + (numSecs > 0 ? numSecs : 256)) > FCSR_SramSectorEnd[i]) + return false; + + // Copy data to SRAM + dst = FCSR_SramSectorPointer[i] + (sector - FCSR_SramSectorStart[i]) * BYTE_PER_READ; + while (writeLength--) + { + *dst++ = *src++; + } + + return true; +} + +/*----------------------------------------------------------------- +FCSR_Shutdown +unload the Flash Cart interface +-----------------------------------------------------------------*/ +bool FCSR_Shutdown(void) +{ + int i; + if (FCSR_ClearStatus() == false) + return false; + + FCSR_FileSysPointer = 0; + + for (i=0; i < 4; i++) + { + FCSR_SramSectorPointer[i] = 0; + FCSR_SramSectorStart[i] = 0; + FCSR_SramSectorEnd[i] = 0; + } + return true; +} + +/*----------------------------------------------------------------- +FCSR_StartUp +initializes the Flash Cart interface, returns true if successful, +otherwise returns false +-----------------------------------------------------------------*/ +bool FCSR_StartUp(void) +{ + bool flagFoundFileSys = false; + int i; + int SramRegionSize[4]; + u8* srcByte; + u8* destByte; + + u32* fileSysPointer = (u32*)0x08000100; // Start at beginning of cart address space, offset by expected location of string + + // Search for file system + while ((fileSysPointer < (u32*)0x0A000000) && !flagFoundFileSys) // Only search while not at end of cart address space + { + while ((*fileSysPointer != FCSR) && (fileSysPointer < (u32*)0x0A000000)) + fileSysPointer += 0x40; + if ((strncmp(FCSR_LabelString, (char*)(fileSysPointer + 1), 12) == 0) && (fileSysPointer < (u32*)0x0A000000)) + { + flagFoundFileSys = true; + } else { + fileSysPointer += 0x80; + } + } + + if (!flagFoundFileSys) + return false; + + // Flash cart file system pointer has been found + FCSR_FileSysPointer = (u8*)(fileSysPointer - 0x40); + + // Get SRAM sector regions from header block + for (i = 0; i < 4; i++) + { + FCSR_SramSectorStart[i] = fileSysPointer[i+4]; + SramRegionSize[i] = fileSysPointer[i+8]; + FCSR_SramSectorEnd[i] = FCSR_SramSectorStart[i] + SramRegionSize[i]; + } + + // Calculate SRAM region pointers + FCSR_SramSectorPointer[0] = (u8*)(SRAM_START + 4); + for (i = 1; i < 4; i++) + { + FCSR_SramSectorPointer[i] = FCSR_SramSectorPointer[i-1] + (SramRegionSize[i-1] * BYTE_PER_READ); + } + + // Initialise SRAM with overlay if it hasn't been done so + if ( (*((u8*)SRAM_START) != 'F') || (*((u8*)(SRAM_START+1)) != 'C') || (*((u8*)(SRAM_START+2)) != 'S') || (*((u8*)(SRAM_START+3)) != 'R') ) + { + *((u8*)SRAM_START) = 'F'; + *((u8*)(SRAM_START+1)) = 'C'; + *((u8*)(SRAM_START+2)) = 'S'; + *((u8*)(SRAM_START+3)) = 'R'; + + for (i = 0; i < 4; i++) + { + srcByte = FCSR_FileSysPointer + (FCSR_SramSectorStart[i] * BYTE_PER_READ); + destByte = FCSR_SramSectorPointer[i]; + while (srcByte < FCSR_FileSysPointer + (FCSR_SramSectorEnd[i] * BYTE_PER_READ) ) + *destByte++ = *srcByte++; + } + } + + // Get SRAM sector regions from header block + for (i = 0; i < 4; i++) + { + if (SramRegionSize[i] == 0) + { + FCSR_SramSectorStart[i] = NO_SRAM; + FCSR_SramSectorEnd[i] = NO_SRAM; + } + } + + return true; +} + +/*----------------------------------------------------------------- +the actual interface structure +-----------------------------------------------------------------*/ +IO_INTERFACE io_fcsr = { + DEVICE_TYPE_FCSR, // 'FCSR' + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA, + (FN_MEDIUM_STARTUP)&FCSR_StartUp, + (FN_MEDIUM_ISINSERTED)&FCSR_IsInserted, + (FN_MEDIUM_READSECTORS)&FCSR_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&FCSR_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&FCSR_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&FCSR_Shutdown +} ; + +/*----------------------------------------------------------------- +FCSR_GetInterface +returns the interface structure to host +-----------------------------------------------------------------*/ +LPIO_INTERFACE FCSR_GetInterface(void) { + return &io_fcsr ; +} ; + +#endif // SUPPORT_FCSR diff --git a/backends/platform/ds/arm9/source/fat/io_fcsr.h b/backends/platform/ds/arm9/source/fat/io_fcsr.h new file mode 100644 index 0000000000..2f87c1c8aa --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_fcsr.h @@ -0,0 +1,48 @@ +/* + io_fcsr.h + + Hardware Routines for using a GBA Flash Cart with SRAM + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_FCSR_H +#define IO_FCSR_H + +// 'FCSR' +#define DEVICE_TYPE_FCSR 0x52534346 + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE FCSR_GetInterface(void) ; + +#endif // define IO_FCSR_H +/* + io_fcsr.h + + Hardware Routines for using a GBA Flash Cart with SRAM + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_FCSR_H +#define IO_FCSR_H + +// 'FCSR' +#define DEVICE_TYPE_FCSR 0x52534346 + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE FCSR_GetInterface(void) ; + +#endif // define IO_FCSR_H diff --git a/backends/platform/ds/arm9/source/fat/io_m3cf.c b/backends/platform/ds/arm9/source/fat/io_m3cf.c new file mode 100644 index 0000000000..238be7e311 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_m3cf.c @@ -0,0 +1,734 @@ +/* + io_m3cf.c based on + + compact_flash.c + By chishm (Michael Chisholm) + + Hardware Routines for reading a compact flash card + using the M3 Perfect CF Adapter + + CF routines modified with help from Darkfader + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + + +#include "io_m3cf.h" + +#ifdef SUPPORT_M3CF + +//--------------------------------------------------------------- +// DMA +#ifdef _CF_USE_DMA + #ifndef NDS + #include "gba_dma.h" + #else + #include + #ifdef ARM9 + #include + #endif + #endif +#endif + +//--------------------------------------------------------------- +// CF Addresses & Commands + +#define GAME_PAK 0x08000000 // Game pack start address + +// GBAMP CF Addresses +#define M3_REG_STS *(vu16*)(GAME_PAK + 0x000C0000) // Status of the CF Card / Device control +#define M3_REG_CMD *(vu16*)(GAME_PAK + 0x008E0000) // Commands sent to control chip and status return +#define M3_REG_ERR *(vu16*)(GAME_PAK + 0x00820000) // Errors / Features + +#define M3_REG_SEC *(vu16*)(GAME_PAK + 0x00840000) // Number of sector to transfer +#define M3_REG_LBA1 *(vu16*)(GAME_PAK + 0x00860000) // 1st byte of sector address +#define M3_REG_LBA2 *(vu16*)(GAME_PAK + 0x00880000) // 2nd byte of sector address +#define M3_REG_LBA3 *(vu16*)(GAME_PAK + 0x008A0000) // 3rd byte of sector address +#define M3_REG_LBA4 *(vu16*)(GAME_PAK + 0x008C0000) // last nibble of sector address | 0xE0 + +#define M3_DATA (vu16*)(GAME_PAK + 0x00800000) // Pointer to buffer of CF data transered from card + +// CF Card status +#define CF_STS_INSERTED 0x50 +#define CF_STS_REMOVED 0x00 +#define CF_STS_READY 0x58 + +#define CF_STS_DRQ 0x08 +#define CF_STS_BUSY 0x80 + +// CF Card commands +#define CF_CMD_LBA 0xE0 +#define CF_CMD_READ 0x20 +#define CF_CMD_WRITE 0x30 + +#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write + + +/*----------------------------------------------------------------- +M3CF_IsInserted +Is a compact flash card inserted? +bool return OUT: true if a CF card is inserted +-----------------------------------------------------------------*/ +bool M3CF_IsInserted (void) +{ + // Change register, then check if value did change + M3_REG_STS = CF_STS_INSERTED; + return ((M3_REG_STS & 0xff) == CF_STS_INSERTED); +} + + +/*----------------------------------------------------------------- +M3CF_ClearStatus +Tries to make the CF card go back to idle mode +bool return OUT: true if a CF card is idle +-----------------------------------------------------------------*/ +bool M3CF_ClearStatus (void) +{ + int i; + + // Wait until CF card is finished previous commands + i=0; + while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + return true; +} + + +/*----------------------------------------------------------------- +M3CF_ReadSectors +Read 512 byte sector numbered "sector" into "buffer" +u32 sector IN: address of first 512 byte sector on CF card to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool M3CF_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + int j = (numSecs > 0 ? numSecs : 256); + u16 *buff = (u16*)buffer; +#ifdef _CF_ALLOW_UNALIGNED + u8 *buff_u8 = (u8*)buffer; + int temp; +#endif + +#if defined _CF_USE_DMA && defined NDS && defined ARM9 + DC_FlushRange( buffer, j * BYTE_PER_READ); +#endif + + // Wait until CF card is finished previous commands + i=0; + while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Set number of sectors to read + M3_REG_SEC = numSecs; + + // Set read sector + M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number + M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number + M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number + M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number + + // Set command to read + M3_REG_CMD = CF_CMD_READ; + + + while (j--) + { + // Wait until card is ready for reading + i = 0; + while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Read data +#ifdef _CF_USE_DMA + #ifdef NDS + DMA3_SRC = (u32)M3_DATA; + DMA3_DEST = (u32)buff; + DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX; + #else + DMA3COPY ( M3_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED); + #endif + buff += BYTE_PER_READ / 2; +#elif defined _CF_ALLOW_UNALIGNED + i=256; + if ((u32)buff_u8 & 0x01) { + while(i--) + { + temp = *M3_DATA; + *buff_u8++ = temp & 0xFF; + *buff_u8++ = temp >> 8; + } + } else { + while(i--) + *buff++ = *M3_DATA; + } +#else + i=256; + while(i--) + *buff++ = *M3_DATA; +#endif + } +#if defined _CF_USE_DMA && defined NDS + // Wait for end of transfer before returning + while(DMA3_CR & DMA_BUSY); +#endif + + return true; +} + + + +/*----------------------------------------------------------------- +M3CF_WriteSectors +Write 512 byte sector numbered "sector" from "buffer" +u32 sector IN: address of 512 byte sector on CF card to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer IN: pointer to 512 byte buffer to read data from +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool M3CF_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + int j = (numSecs > 0 ? numSecs : 256); + u16 *buff = (u16*)buffer; +#ifdef _CF_ALLOW_UNALIGNED + u8 *buff_u8 = (u8*)buffer; + int temp; +#endif + +#if defined _CF_USE_DMA && defined NDS && defined ARM9 + DC_FlushRange( buffer, j * BYTE_PER_READ); +#endif + + // Wait until CF card is finished previous commands + i=0; + while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Set number of sectors to write + M3_REG_SEC = numSecs; + + // Set write sector + M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number + M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number + M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number + M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number + + // Set command to write + M3_REG_CMD = CF_CMD_WRITE; + + while (j--) + { + // Wait until card is ready for writing + i = 0; + while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Write data +#ifdef _CF_USE_DMA + #ifdef NDS + DMA3_SRC = (u32)buff; + DMA3_DEST = (u32)M3_DATA; + DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX; + #else + DMA3COPY( buff, M3_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED); + #endif + buff += BYTE_PER_READ / 2; +#elif defined _CF_ALLOW_UNALIGNED + i=256; + if ((u32)buff_u8 & 0x01) { + while(i--) + { + temp = *buff_u8++; + temp |= *buff_u8++ << 8; + *M3_DATA = temp; + } + } else { + while(i--) + *M3_DATA = *buff++; + } +#else + i=256; + while(i--) + *M3_DATA = *buff++; +#endif + } +#if defined _CF_USE_DMA && defined NDS + // Wait for end of transfer before returning + while(DMA3_CR & DMA_BUSY); +#endif + + return true; +} + + +/*----------------------------------------------------------------- +M3_Unlock +Returns true if M3 was unlocked, false if failed +Added by MightyMax +-----------------------------------------------------------------*/ +bool M3_Unlock(void) +{ + // run unlock sequence + volatile unsigned short tmp ; + tmp = *(volatile unsigned short *)0x08000000 ; + tmp = *(volatile unsigned short *)0x08E00002 ; + tmp = *(volatile unsigned short *)0x0800000E ; + tmp = *(volatile unsigned short *)0x08801FFC ; + tmp = *(volatile unsigned short *)0x0800104A ; + tmp = *(volatile unsigned short *)0x08800612 ; + tmp = *(volatile unsigned short *)0x08000000 ; + tmp = *(volatile unsigned short *)0x08801B66 ; + tmp = *(volatile unsigned short *)0x08800006 ; + tmp = *(volatile unsigned short *)0x08000000 ; + // test that we have register access + tmp = M3_REG_LBA1; + M3_REG_LBA1 = (~tmp & 0xFF); + tmp = (~tmp & 0xFF); + // did it change? + return (M3_REG_LBA1 == tmp) ; +} + +bool M3CF_Shutdown(void) { + return M3CF_ClearStatus() ; +} ; + +bool M3CF_StartUp(void) { + return M3_Unlock() ; +} ; + + +IO_INTERFACE io_m3cf = { + DEVICE_TYPE_M3CF, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA, + (FN_MEDIUM_STARTUP)&M3CF_StartUp, + (FN_MEDIUM_ISINSERTED)&M3CF_IsInserted, + (FN_MEDIUM_READSECTORS)&M3CF_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&M3CF_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&M3CF_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&M3CF_Shutdown +} ; + + +LPIO_INTERFACE M3CF_GetInterface(void) { + return &io_m3cf ; +} ; + +#endif // SUPPORT_M3CF +/* + io_m3cf.c based on + + compact_flash.c + By chishm (Michael Chisholm) + + Hardware Routines for reading a compact flash card + using the M3 Perfect CF Adapter + + CF routines modified with help from Darkfader + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + + +#include "io_m3cf.h" + +#ifdef SUPPORT_M3CF + +//--------------------------------------------------------------- +// DMA +#ifdef _CF_USE_DMA + #ifndef NDS + #include "gba_dma.h" + #else + #include + #ifdef ARM9 + #include + #endif + #endif +#endif + +//--------------------------------------------------------------- +// CF Addresses & Commands + +#define GAME_PAK 0x08000000 // Game pack start address + +// GBAMP CF Addresses +#define M3_REG_STS *(vu16*)(GAME_PAK + 0x000C0000) // Status of the CF Card / Device control +#define M3_REG_CMD *(vu16*)(GAME_PAK + 0x008E0000) // Commands sent to control chip and status return +#define M3_REG_ERR *(vu16*)(GAME_PAK + 0x00820000) // Errors / Features + +#define M3_REG_SEC *(vu16*)(GAME_PAK + 0x00840000) // Number of sector to transfer +#define M3_REG_LBA1 *(vu16*)(GAME_PAK + 0x00860000) // 1st byte of sector address +#define M3_REG_LBA2 *(vu16*)(GAME_PAK + 0x00880000) // 2nd byte of sector address +#define M3_REG_LBA3 *(vu16*)(GAME_PAK + 0x008A0000) // 3rd byte of sector address +#define M3_REG_LBA4 *(vu16*)(GAME_PAK + 0x008C0000) // last nibble of sector address | 0xE0 + +#define M3_DATA (vu16*)(GAME_PAK + 0x00800000) // Pointer to buffer of CF data transered from card + +// CF Card status +#define CF_STS_INSERTED 0x50 +#define CF_STS_REMOVED 0x00 +#define CF_STS_READY 0x58 + +#define CF_STS_DRQ 0x08 +#define CF_STS_BUSY 0x80 + +// CF Card commands +#define CF_CMD_LBA 0xE0 +#define CF_CMD_READ 0x20 +#define CF_CMD_WRITE 0x30 + +#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write + + +/*----------------------------------------------------------------- +M3CF_IsInserted +Is a compact flash card inserted? +bool return OUT: true if a CF card is inserted +-----------------------------------------------------------------*/ +bool M3CF_IsInserted (void) +{ + // Change register, then check if value did change + M3_REG_STS = CF_STS_INSERTED; + return ((M3_REG_STS & 0xff) == CF_STS_INSERTED); +} + + +/*----------------------------------------------------------------- +M3CF_ClearStatus +Tries to make the CF card go back to idle mode +bool return OUT: true if a CF card is idle +-----------------------------------------------------------------*/ +bool M3CF_ClearStatus (void) +{ + int i; + + // Wait until CF card is finished previous commands + i=0; + while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + return true; +} + + +/*----------------------------------------------------------------- +M3CF_ReadSectors +Read 512 byte sector numbered "sector" into "buffer" +u32 sector IN: address of first 512 byte sector on CF card to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool M3CF_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + int j = (numSecs > 0 ? numSecs : 256); + u16 *buff = (u16*)buffer; +#ifdef _CF_ALLOW_UNALIGNED + u8 *buff_u8 = (u8*)buffer; + int temp; +#endif + +#if defined _CF_USE_DMA && defined NDS && defined ARM9 + DC_FlushRange( buffer, j * BYTE_PER_READ); +#endif + + // Wait until CF card is finished previous commands + i=0; + while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Set number of sectors to read + M3_REG_SEC = numSecs; + + // Set read sector + M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number + M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number + M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number + M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number + + // Set command to read + M3_REG_CMD = CF_CMD_READ; + + + while (j--) + { + // Wait until card is ready for reading + i = 0; + while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Read data +#ifdef _CF_USE_DMA + #ifdef NDS + DMA3_SRC = (u32)M3_DATA; + DMA3_DEST = (u32)buff; + DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX; + #else + DMA3COPY ( M3_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED); + #endif + buff += BYTE_PER_READ / 2; +#elif defined _CF_ALLOW_UNALIGNED + i=256; + if ((u32)buff_u8 & 0x01) { + while(i--) + { + temp = *M3_DATA; + *buff_u8++ = temp & 0xFF; + *buff_u8++ = temp >> 8; + } + } else { + while(i--) + *buff++ = *M3_DATA; + } +#else + i=256; + while(i--) + *buff++ = *M3_DATA; +#endif + } +#if defined _CF_USE_DMA && defined NDS + // Wait for end of transfer before returning + while(DMA3_CR & DMA_BUSY); +#endif + + return true; +} + + + +/*----------------------------------------------------------------- +M3CF_WriteSectors +Write 512 byte sector numbered "sector" from "buffer" +u32 sector IN: address of 512 byte sector on CF card to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer IN: pointer to 512 byte buffer to read data from +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool M3CF_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + int j = (numSecs > 0 ? numSecs : 256); + u16 *buff = (u16*)buffer; +#ifdef _CF_ALLOW_UNALIGNED + u8 *buff_u8 = (u8*)buffer; + int temp; +#endif + +#if defined _CF_USE_DMA && defined NDS && defined ARM9 + DC_FlushRange( buffer, j * BYTE_PER_READ); +#endif + + // Wait until CF card is finished previous commands + i=0; + while ((M3_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(M3_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Set number of sectors to write + M3_REG_SEC = numSecs; + + // Set write sector + M3_REG_LBA1 = sector & 0xFF; // 1st byte of sector number + M3_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number + M3_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number + M3_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number + + // Set command to write + M3_REG_CMD = CF_CMD_WRITE; + + while (j--) + { + // Wait until card is ready for writing + i = 0; + while (((M3_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Write data +#ifdef _CF_USE_DMA + #ifdef NDS + DMA3_SRC = (u32)buff; + DMA3_DEST = (u32)M3_DATA; + DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX; + #else + DMA3COPY( buff, M3_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED); + #endif + buff += BYTE_PER_READ / 2; +#elif defined _CF_ALLOW_UNALIGNED + i=256; + if ((u32)buff_u8 & 0x01) { + while(i--) + { + temp = *buff_u8++; + temp |= *buff_u8++ << 8; + *M3_DATA = temp; + } + } else { + while(i--) + *M3_DATA = *buff++; + } +#else + i=256; + while(i--) + *M3_DATA = *buff++; +#endif + } +#if defined _CF_USE_DMA && defined NDS + // Wait for end of transfer before returning + while(DMA3_CR & DMA_BUSY); +#endif + + return true; +} + + +/*----------------------------------------------------------------- +M3_Unlock +Returns true if M3 was unlocked, false if failed +Added by MightyMax +-----------------------------------------------------------------*/ +bool M3_Unlock(void) +{ + // run unlock sequence + volatile unsigned short tmp ; + tmp = *(volatile unsigned short *)0x08000000 ; + tmp = *(volatile unsigned short *)0x08E00002 ; + tmp = *(volatile unsigned short *)0x0800000E ; + tmp = *(volatile unsigned short *)0x08801FFC ; + tmp = *(volatile unsigned short *)0x0800104A ; + tmp = *(volatile unsigned short *)0x08800612 ; + tmp = *(volatile unsigned short *)0x08000000 ; + tmp = *(volatile unsigned short *)0x08801B66 ; + tmp = *(volatile unsigned short *)0x08800006 ; + tmp = *(volatile unsigned short *)0x08000000 ; + // test that we have register access + tmp = M3_REG_LBA1; + M3_REG_LBA1 = (~tmp & 0xFF); + tmp = (~tmp & 0xFF); + // did it change? + return (M3_REG_LBA1 == tmp) ; +} + +bool M3CF_Shutdown(void) { + return M3CF_ClearStatus() ; +} ; + +bool M3CF_StartUp(void) { + return M3_Unlock() ; +} ; + + +IO_INTERFACE io_m3cf = { + DEVICE_TYPE_M3CF, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA, + (FN_MEDIUM_STARTUP)&M3CF_StartUp, + (FN_MEDIUM_ISINSERTED)&M3CF_IsInserted, + (FN_MEDIUM_READSECTORS)&M3CF_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&M3CF_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&M3CF_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&M3CF_Shutdown +} ; + + +LPIO_INTERFACE M3CF_GetInterface(void) { + return &io_m3cf ; +} ; + +#endif // SUPPORT_M3CF diff --git a/backends/platform/ds/arm9/source/fat/io_m3cf.h b/backends/platform/ds/arm9/source/fat/io_m3cf.h new file mode 100644 index 0000000000..bade53f511 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_m3cf.h @@ -0,0 +1,50 @@ +/* + io_m3cf.h + + Hardware Routines for reading a compact flash card + using the M3 CF + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_M3CF_H +#define IO_M3CF_H + +// 'M3CF' +#define DEVICE_TYPE_M3CF 0x4643334D + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE M3CF_GetInterface(void) ; + +#endif // define IO_M3CF_H +/* + io_m3cf.h + + Hardware Routines for reading a compact flash card + using the M3 CF + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_M3CF_H +#define IO_M3CF_H + +// 'M3CF' +#define DEVICE_TYPE_M3CF 0x4643334D + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE M3CF_GetInterface(void) ; + +#endif // define IO_M3CF_H diff --git a/backends/platform/ds/arm9/source/fat/io_m3sd.c b/backends/platform/ds/arm9/source/fat/io_m3sd.c new file mode 100644 index 0000000000..897f65bd6e --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_m3sd.c @@ -0,0 +1,750 @@ +/* + io_m3sd.c based on io_m3cf.c by SaTa. + + io_m3cf.c based on + + compact_flash.c + By chishm (Michael Chisholm) + + Hardware Routines for reading a compact flash card + using the M3 Perfect CF Adapter + + CF routines modified with help from Darkfader + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + + +#include "io_m3sd.h" + +#ifdef SUPPORT_M3SD + +//SD dir control bit cmddir=bit0 clken=bit1 +//output +#define SDDIR (*(volatile u16*)0x8800000) + +//SD send get control bit send=bit0 get=bit1 +//output +#define SDCON (*(volatile u16*)0x9800000) + +//SD output data obyte[7:0]=AD[7:0] +//output +#define SDODA (*(volatile u16*)0x9000000) + +//SD input data AD[7:0]=ibyte[7:0] +//input +#define SDIDA (*(volatile u16*)0x9000000) + +//readsector data1 +#define SDIDA1 (*(volatile u16*)0x9200000) + +//readsector data2 +#define SDIDA2 (*(volatile u16*)0x9400000) + +//readsector data3 +#define SDIDA3 (*(volatile u16*)0x9600000) + +//SD stutas cmdneg=bit0 cmdpos=bit1 issend=bit2 isget=bit3 +//input +#define SDSTA (*(volatile u16*)0x9800000) + +//#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write +#define CARD_TIMEOUT (500*100) // M3SD timeout nomal:500 + +//====================================================== +bool M3SD_read1sector(u32 sectorn,u32 TAddr) +{ + u32 i; + int w; + + SDCON=0x8; // bit3:ƒRƒ}ƒ“ƒhƒ‚[ƒhH + SDIDA1=0x40+17; // ƒRƒ}ƒ“ƒh CMD17 + SDIDA2=(sectorn>>7);// ƒZƒNƒ^H 9ƒrƒbƒg=ƒAƒhƒŒƒXH ‚P‚Uƒrƒbƒg + SDIDA3=(sectorn<<9);// ƒZƒNƒ^L 7ƒrƒbƒg=ƒAƒhƒŒƒXL ‚P‚Uƒrƒbƒg + SDDIR=0x29; // ƒRƒ}ƒ“ƒh‘—MH + i=0; + + while ( ((SDSTA&0x01) != 0x01)&&(i < CARD_TIMEOUT) ) + { + i++; + } + SDDIR=0x09; + i=0; + SDDIR=0x49; + while ( ((SDSTA&0x40) != 0x40)&&(i < CARD_TIMEOUT) ) + { + i++; + } + SDDIR=0x09; + + SDDIR=0x8;//cmd input clken=0 datadir input clock=0 + SDCON=0x4;//send=0 get=0 en25=1 cmd1=0 + + w = SDDIR; + for(w=0;w<0x100;w++) + { + u16 d16; + u8 *d8=(u8 *)&d16; +// *(u16*)(TAddr+w*2) = SDDIR; // 16bit + d16 = SDDIR; // 16bit + *(u8 *)(TAddr+w*2) =d8[0]; + *(u8 *)(TAddr+w*2+1) =d8[1]; + + } + w = SDDIR; + w = SDDIR; + + if (i >= CARD_TIMEOUT) + return false; + + return true; + +} +//================================================== + + +//====================================================== +void SD_crc16(u16* buff,u16 num,u16* crc16buff); +void SD_data_write(u16 *buff,u16* crc16buff); + +u16 Hal4ATA_StatusByte; + +void Hal4ATA_GetStatus(void) +{ + Hal4ATA_StatusByte = SDSTA; +} + +bool Hal4ATA_WaitOnBusy(void) +{ + Hal4ATA_GetStatus(); + while ( (Hal4ATA_StatusByte & 0x01) != 0x1) + { + Hal4ATA_GetStatus(); + } + return TRUE; +} + +bool Hal4ATA_WaitOnBusyNDrdy(void) +{ + Hal4ATA_GetStatus(); + while ( (Hal4ATA_StatusByte&0x40) !=0x40) + { + Hal4ATA_GetStatus(); + } + return TRUE; +} + + +void SendCommand(u16 command, u32 sectorn) +{ + SDCON=0x8; + SDIDA1=0x40+command; + SDIDA2=(sectorn>>7); + SDIDA3=(sectorn<<9); + + SDDIR=0x29; + Hal4ATA_WaitOnBusy(); + SDDIR=0x09; +} + + +#define DMA3SAD *(u32*)0x040000D4 +#define DMA3DAD *(u32*)0x040000D8 +#define DMA3CNT *(u32*)0x040000DC + +void DMA3(u32 src, u32 dst, u32 cnt) +{ + DMA3SAD=src; + DMA3DAD=dst; + DMA3CNT=cnt; +} + + + +void PassRespond(u32 num) +{ + u32 i,dmanum; + + dmanum=(64+(num<<3))>>2; + SDDIR=0x8; + SDCON=0x4; + DMA3(0x8800000,(u32)&i,0x80400000+dmanum); +} + +//bool M3SD_write1sector(u32 sectorn,u16 * p) +bool M3SD_write1sector(u32 sectorn,u32 p) +{ + u16 crc[4]; + + SendCommand(24,sectorn); + PassRespond(6); + + SDDIR=0x4; + SDCON=0x0; + + SD_crc16((u16 *)p,512,crc); + SD_data_write((u16 *)p,crc); + return true; +} +//================================================== + + +// GBAMP CF Addresses + +#define M3_REG_STS *(vu16*)(0x09800000) // Status of the CF Card / Device control + +#define M3_DATA (vu16*)(0x08800000) // Pointer to buffer of CF data transered from card + +// CF Card status +#define CF_STS_INSERTED1 0x20 +#define CF_STS_INSERTED2 0x30 + +/*----------------------------------------------------------------- +M3SD_IsInserted +Is a compact flash card inserted? +bool return OUT: true if a CF card is inserted +-----------------------------------------------------------------*/ +bool M3SD_IsInserted (void) +{ + int i; + u16 sta; + // Change register, then check if value did change + M3_REG_STS = CF_STS_INSERTED1; + + for(i=0;i= CARD_TIMEOUT) + return false; + + return true; +} + + +/*----------------------------------------------------------------- +M3SD_ReadSectors +Read 512 byte sector numbered "sector" into "buffer" +u32 sector IN: address of first 512 byte sector on CF card to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool M3SD_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + + + //void M3SD_read1sector(u32 sectorn,u32 TAddr) + bool r=true; + int i; + for(i=0;i>7);// ƒZƒNƒ^H 9ƒrƒbƒg=ƒAƒhƒŒƒXH ‚P‚Uƒrƒbƒg + SDIDA3=(sectorn<<9);// ƒZƒNƒ^L 7ƒrƒbƒg=ƒAƒhƒŒƒXL ‚P‚Uƒrƒbƒg + SDDIR=0x29; // ƒRƒ}ƒ“ƒh‘—MH + i=0; + + while ( ((SDSTA&0x01) != 0x01)&&(i < CARD_TIMEOUT) ) + { + i++; + } + SDDIR=0x09; + i=0; + SDDIR=0x49; + while ( ((SDSTA&0x40) != 0x40)&&(i < CARD_TIMEOUT) ) + { + i++; + } + SDDIR=0x09; + + SDDIR=0x8;//cmd input clken=0 datadir input clock=0 + SDCON=0x4;//send=0 get=0 en25=1 cmd1=0 + + w = SDDIR; + for(w=0;w<0x100;w++) + { + u16 d16; + u8 *d8=(u8 *)&d16; +// *(u16*)(TAddr+w*2) = SDDIR; // 16bit + d16 = SDDIR; // 16bit + *(u8 *)(TAddr+w*2) =d8[0]; + *(u8 *)(TAddr+w*2+1) =d8[1]; + + } + w = SDDIR; + w = SDDIR; + + if (i >= CARD_TIMEOUT) + return false; + + return true; + +} +//================================================== + + +//====================================================== +void SD_crc16(u16* buff,u16 num,u16* crc16buff); +void SD_data_write(u16 *buff,u16* crc16buff); + +u16 Hal4ATA_StatusByte; + +void Hal4ATA_GetStatus(void) +{ + Hal4ATA_StatusByte = SDSTA; +} + +bool Hal4ATA_WaitOnBusy(void) +{ + Hal4ATA_GetStatus(); + while ( (Hal4ATA_StatusByte & 0x01) != 0x1) + { + Hal4ATA_GetStatus(); + } + return TRUE; +} + +bool Hal4ATA_WaitOnBusyNDrdy(void) +{ + Hal4ATA_GetStatus(); + while ( (Hal4ATA_StatusByte&0x40) !=0x40) + { + Hal4ATA_GetStatus(); + } + return TRUE; +} + + +void SendCommand(u16 command, u32 sectorn) +{ + SDCON=0x8; + SDIDA1=0x40+command; + SDIDA2=(sectorn>>7); + SDIDA3=(sectorn<<9); + + SDDIR=0x29; + Hal4ATA_WaitOnBusy(); + SDDIR=0x09; +} + + +#define DMA3SAD *(u32*)0x040000D4 +#define DMA3DAD *(u32*)0x040000D8 +#define DMA3CNT *(u32*)0x040000DC + +void DMA3(u32 src, u32 dst, u32 cnt) +{ + DMA3SAD=src; + DMA3DAD=dst; + DMA3CNT=cnt; +} + + + +void PassRespond(u32 num) +{ + u32 i,dmanum; + + dmanum=(64+(num<<3))>>2; + SDDIR=0x8; + SDCON=0x4; + DMA3(0x8800000,(u32)&i,0x80400000+dmanum); +} + +//bool M3SD_write1sector(u32 sectorn,u16 * p) +bool M3SD_write1sector(u32 sectorn,u32 p) +{ + u16 crc[4]; + + SendCommand(24,sectorn); + PassRespond(6); + + SDDIR=0x4; + SDCON=0x0; + + SD_crc16((u16 *)p,512,crc); + SD_data_write((u16 *)p,crc); + return true; +} +//================================================== + + +// GBAMP CF Addresses + +#define M3_REG_STS *(vu16*)(0x09800000) // Status of the CF Card / Device control + +#define M3_DATA (vu16*)(0x08800000) // Pointer to buffer of CF data transered from card + +// CF Card status +#define CF_STS_INSERTED1 0x20 +#define CF_STS_INSERTED2 0x30 + +/*----------------------------------------------------------------- +M3SD_IsInserted +Is a compact flash card inserted? +bool return OUT: true if a CF card is inserted +-----------------------------------------------------------------*/ +bool M3SD_IsInserted (void) +{ + int i; + u16 sta; + // Change register, then check if value did change + M3_REG_STS = CF_STS_INSERTED1; + + for(i=0;i= CARD_TIMEOUT) + return false; + + return true; +} + + +/*----------------------------------------------------------------- +M3SD_ReadSectors +Read 512 byte sector numbered "sector" into "buffer" +u32 sector IN: address of first 512 byte sector on CF card to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool M3SD_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + + + //void M3SD_read1sector(u32 sectorn,u32 TAddr) + bool r=true; + int i; + for(i=0;i + #ifdef ARM9 + #include + #endif + #endif +#endif + +//--------------------------------------------------------------- +// CF Addresses & Commands + +#define GAME_PAK 0x08000000 // Game pack start address + +// GBAMP CF Addresses +#define MP_REG_STS *(vu16*)(GAME_PAK + 0x018C0000) // Status of the CF Card / Device control +#define MP_REG_CMD *(vu16*)(GAME_PAK + 0x010E0000) // Commands sent to control chip and status return +#define MP_REG_ERR *(vu16*)(GAME_PAK + 0x01020000) // Errors / Features + +#define MP_REG_SEC *(vu16*)(GAME_PAK + 0x01040000) // Number of sector to transfer +#define MP_REG_LBA1 *(vu16*)(GAME_PAK + 0x01060000) // 1st byte of sector address +#define MP_REG_LBA2 *(vu16*)(GAME_PAK + 0x01080000) // 2nd byte of sector address +#define MP_REG_LBA3 *(vu16*)(GAME_PAK + 0x010A0000) // 3rd byte of sector address +#define MP_REG_LBA4 *(vu16*)(GAME_PAK + 0x010C0000) // last nibble of sector address | 0xE0 + +#define MP_DATA (vu16*)(GAME_PAK + 0x01000000) // Pointer to buffer of CF data transered from card + +// CF Card status +#define CF_STS_INSERTED 0x50 +#define CF_STS_REMOVED 0x00 +#define CF_STS_READY 0x58 + +#define CF_STS_DRQ 0x08 +#define CF_STS_BUSY 0x80 + +// CF Card commands +#define CF_CMD_LBA 0xE0 +#define CF_CMD_READ 0x20 +#define CF_CMD_WRITE 0x30 + +#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write + + +/*----------------------------------------------------------------- +MPCF_IsInserted +Is a compact flash card inserted? +bool return OUT: true if a CF card is inserted +-----------------------------------------------------------------*/ +bool MPCF_IsInserted (void) +{ + // Change register, then check if value did change + MP_REG_STS = CF_STS_INSERTED; + return ((MP_REG_STS & 0xff) == CF_STS_INSERTED); +} + + +/*----------------------------------------------------------------- +MPCF_ClearStatus +Tries to make the CF card go back to idle mode +bool return OUT: true if a CF card is idle +-----------------------------------------------------------------*/ +bool MPCF_ClearStatus (void) +{ + int i; + + // Wait until CF card is finished previous commands + i=0; + while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + return true; +} + + +/*----------------------------------------------------------------- +MPCF_ReadSectors +Read 512 byte sector numbered "sector" into "buffer" +u32 sector IN: address of first 512 byte sector on CF card to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + int j = (numSecs > 0 ? numSecs : 256); + u16 *buff = (u16*)buffer; +#ifdef _CF_ALLOW_UNALIGNED + u8 *buff_u8 = (u8*)buffer; + int temp; +#endif + +#if (defined _CF_USE_DMA) && (defined NDS) && (defined ARM9) + DC_FlushRange( buffer, j * BYTE_PER_READ); +#endif + + // Wait until CF card is finished previous commands + i=0; + while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Set number of sectors to read + MP_REG_SEC = numSecs; + + // Set read sector + MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number + MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number + MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number + MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number + + // Set command to read + MP_REG_CMD = CF_CMD_READ; + + + while (j--) + { + // Wait until card is ready for reading + i = 0; + while (((MP_REG_STS & 0xff)!= CF_STS_READY) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Read data +#ifdef _CF_USE_DMA + #ifdef NDS + DMA3_SRC = (u32)MP_DATA; + DMA3_DEST = (u32)buff; + DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX; + #else + DMA3COPY ( MP_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED); + #endif + buff += BYTE_PER_READ / 2; +#elif defined _CF_ALLOW_UNALIGNED + i=256; + if ((u32)buff_u8 & 0x01) { + while(i--) + { + temp = *MP_DATA; + *buff_u8++ = temp & 0xFF; + *buff_u8++ = temp >> 8; + } + } else { + while(i--) + *buff++ = *MP_DATA; + } +#else + i=256; + while(i--) + *buff++ = *MP_DATA; +#endif + } +#if (defined _CF_USE_DMA) && (defined NDS) + // Wait for end of transfer before returning + while(DMA3_CR & DMA_BUSY); +#endif + return true; +} + + + +/*----------------------------------------------------------------- +MPCF_WriteSectors +Write 512 byte sector numbered "sector" from "buffer" +u32 sector IN: address of 512 byte sector on CF card to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer IN: pointer to 512 byte buffer to read data from +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + int j = (numSecs > 0 ? numSecs : 256); + u16 *buff = (u16*)buffer; +#ifdef _CF_ALLOW_UNALIGNED + u8 *buff_u8 = (u8*)buffer; + int temp; +#endif + +#if defined _CF_USE_DMA && defined NDS && defined ARM9 + DC_FlushRange( buffer, j * BYTE_PER_READ); +#endif + + // Wait until CF card is finished previous commands + i=0; + while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Set number of sectors to write + MP_REG_SEC = numSecs; + + // Set write sector + MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number + MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number + MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number + MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number + + // Set command to write + MP_REG_CMD = CF_CMD_WRITE; + + while (j--) + { + // Wait until card is ready for writing + i = 0; + while (((MP_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Write data +#ifdef _CF_USE_DMA + #ifdef NDS + DMA3_SRC = (u32)buff; + DMA3_DEST = (u32)MP_DATA; + DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX; + #else + DMA3COPY( buff, MP_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED); + #endif + buff += BYTE_PER_READ / 2; +#elif defined _CF_ALLOW_UNALIGNED + i=256; + if ((u32)buff_u8 & 0x01) { + while(i--) + { + temp = *buff_u8++; + temp |= *buff_u8++ << 8; + *MP_DATA = temp; + } + } else { + while(i--) + *MP_DATA = *buff++; + } +#else + i=256; + while(i--) + *MP_DATA = *buff++; +#endif + } +#if defined _CF_USE_DMA && defined NDS + // Wait for end of transfer before returning + while(DMA3_CR & DMA_BUSY); +#endif + + return true; +} + +/*----------------------------------------------------------------- +MPCF_Shutdown +unload the GBAMP CF interface +-----------------------------------------------------------------*/ +bool MPCF_Shutdown(void) +{ + return MPCF_ClearStatus() ; +} + +/*----------------------------------------------------------------- +MPCF_StartUp +initializes the CF interface, returns true if successful, +otherwise returns false +-----------------------------------------------------------------*/ +bool MPCF_StartUp(void) +{ + u8 temp = MP_REG_LBA1; + MP_REG_LBA1 = (~temp & 0xFF); + temp = (~temp & 0xFF); + return (MP_REG_LBA1 == temp) ; +} + +/*----------------------------------------------------------------- +the actual interface structure +-----------------------------------------------------------------*/ +IO_INTERFACE io_mpcf = { + DEVICE_TYPE_MPCF, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA, + (FN_MEDIUM_STARTUP)&MPCF_StartUp, + (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted, + (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&MPCF_Shutdown +} ; + +/*----------------------------------------------------------------- +MPCF_GetInterface +returns the interface structure to host +-----------------------------------------------------------------*/ +LPIO_INTERFACE MPCF_GetInterface(void) { + return &io_mpcf ; +} ; + +#endif // SUPPORT_MPCF +/* + io_mpcf.c based on + + compact_flash.c + By chishm (Michael Chisholm) + + Hardware Routines for reading a compact flash card + using the GBA Movie Player + + CF routines modified with help from Darkfader + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + + +#include "io_mpcf.h" + +#ifdef SUPPORT_MPCF + +//--------------------------------------------------------------- +// DMA +#ifdef _CF_USE_DMA + #ifndef NDS + #include "gba_dma.h" + #else + #include + #ifdef ARM9 + #include + #endif + #endif +#endif + +//--------------------------------------------------------------- +// CF Addresses & Commands + +#define GAME_PAK 0x08000000 // Game pack start address + +// GBAMP CF Addresses +#define MP_REG_STS *(vu16*)(GAME_PAK + 0x018C0000) // Status of the CF Card / Device control +#define MP_REG_CMD *(vu16*)(GAME_PAK + 0x010E0000) // Commands sent to control chip and status return +#define MP_REG_ERR *(vu16*)(GAME_PAK + 0x01020000) // Errors / Features + +#define MP_REG_SEC *(vu16*)(GAME_PAK + 0x01040000) // Number of sector to transfer +#define MP_REG_LBA1 *(vu16*)(GAME_PAK + 0x01060000) // 1st byte of sector address +#define MP_REG_LBA2 *(vu16*)(GAME_PAK + 0x01080000) // 2nd byte of sector address +#define MP_REG_LBA3 *(vu16*)(GAME_PAK + 0x010A0000) // 3rd byte of sector address +#define MP_REG_LBA4 *(vu16*)(GAME_PAK + 0x010C0000) // last nibble of sector address | 0xE0 + +#define MP_DATA (vu16*)(GAME_PAK + 0x01000000) // Pointer to buffer of CF data transered from card + +// CF Card status +#define CF_STS_INSERTED 0x50 +#define CF_STS_REMOVED 0x00 +#define CF_STS_READY 0x58 + +#define CF_STS_DRQ 0x08 +#define CF_STS_BUSY 0x80 + +// CF Card commands +#define CF_CMD_LBA 0xE0 +#define CF_CMD_READ 0x20 +#define CF_CMD_WRITE 0x30 + +#define CARD_TIMEOUT 10000000 // Updated due to suggestion from SaTa, otherwise card will timeout sometimes on a write + + +/*----------------------------------------------------------------- +MPCF_IsInserted +Is a compact flash card inserted? +bool return OUT: true if a CF card is inserted +-----------------------------------------------------------------*/ +bool MPCF_IsInserted (void) +{ + // Change register, then check if value did change + MP_REG_STS = CF_STS_INSERTED; + return ((MP_REG_STS & 0xff) == CF_STS_INSERTED); +} + + +/*----------------------------------------------------------------- +MPCF_ClearStatus +Tries to make the CF card go back to idle mode +bool return OUT: true if a CF card is idle +-----------------------------------------------------------------*/ +bool MPCF_ClearStatus (void) +{ + int i; + + // Wait until CF card is finished previous commands + i=0; + while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + return true; +} + + +/*----------------------------------------------------------------- +MPCF_ReadSectors +Read 512 byte sector numbered "sector" into "buffer" +u32 sector IN: address of first 512 byte sector on CF card to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer OUT: pointer to 512 byte buffer to store data in +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + int j = (numSecs > 0 ? numSecs : 256); + u16 *buff = (u16*)buffer; +#ifdef _CF_ALLOW_UNALIGNED + u8 *buff_u8 = (u8*)buffer; + int temp; +#endif + +#if (defined _CF_USE_DMA) && (defined NDS) && (defined ARM9) + DC_FlushRange( buffer, j * BYTE_PER_READ); +#endif + + // Wait until CF card is finished previous commands + i=0; + while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Set number of sectors to read + MP_REG_SEC = numSecs; + + // Set read sector + MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number + MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number + MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number + MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number + + // Set command to read + MP_REG_CMD = CF_CMD_READ; + + + while (j--) + { + // Wait until card is ready for reading + i = 0; + while (((MP_REG_STS & 0xff)!= CF_STS_READY) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Read data +#ifdef _CF_USE_DMA + #ifdef NDS + DMA3_SRC = (u32)MP_DATA; + DMA3_DEST = (u32)buff; + DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_SRC_FIX; + #else + DMA3COPY ( MP_DATA, buff, 256 | DMA16 | DMA_ENABLE | DMA_SRC_FIXED); + #endif + buff += BYTE_PER_READ / 2; +#elif defined _CF_ALLOW_UNALIGNED + i=256; + if ((u32)buff_u8 & 0x01) { + while(i--) + { + temp = *MP_DATA; + *buff_u8++ = temp & 0xFF; + *buff_u8++ = temp >> 8; + } + } else { + while(i--) + *buff++ = *MP_DATA; + } +#else + i=256; + while(i--) + *buff++ = *MP_DATA; +#endif + } +#if (defined _CF_USE_DMA) && (defined NDS) + // Wait for end of transfer before returning + while(DMA3_CR & DMA_BUSY); +#endif + return true; +} + + + +/*----------------------------------------------------------------- +MPCF_WriteSectors +Write 512 byte sector numbered "sector" from "buffer" +u32 sector IN: address of 512 byte sector on CF card to read +u8 numSecs IN: number of 512 byte sectors to read, + 1 to 256 sectors can be read, 0 = 256 +void* buffer IN: pointer to 512 byte buffer to read data from +bool return OUT: true if successful +-----------------------------------------------------------------*/ +bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + int i; + int j = (numSecs > 0 ? numSecs : 256); + u16 *buff = (u16*)buffer; +#ifdef _CF_ALLOW_UNALIGNED + u8 *buff_u8 = (u8*)buffer; + int temp; +#endif + +#if defined _CF_USE_DMA && defined NDS && defined ARM9 + DC_FlushRange( buffer, j * BYTE_PER_READ); +#endif + + // Wait until CF card is finished previous commands + i=0; + while ((MP_REG_CMD & CF_STS_BUSY) && (i < CARD_TIMEOUT)) + { + i++; + } + + // Wait until card is ready for commands + i = 0; + while ((!(MP_REG_STS & CF_STS_INSERTED)) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Set number of sectors to write + MP_REG_SEC = numSecs; + + // Set write sector + MP_REG_LBA1 = sector & 0xFF; // 1st byte of sector number + MP_REG_LBA2 = (sector >> 8) & 0xFF; // 2nd byte of sector number + MP_REG_LBA3 = (sector >> 16) & 0xFF; // 3rd byte of sector number + MP_REG_LBA4 = ((sector >> 24) & 0x0F )| CF_CMD_LBA; // last nibble of sector number + + // Set command to write + MP_REG_CMD = CF_CMD_WRITE; + + while (j--) + { + // Wait until card is ready for writing + i = 0; + while (((MP_REG_STS & 0xff) != CF_STS_READY) && (i < CARD_TIMEOUT)) + { + i++; + } + if (i >= CARD_TIMEOUT) + return false; + + // Write data +#ifdef _CF_USE_DMA + #ifdef NDS + DMA3_SRC = (u32)buff; + DMA3_DEST = (u32)MP_DATA; + DMA3_CR = 256 | DMA_COPY_HALFWORDS | DMA_DST_FIX; + #else + DMA3COPY( buff, MP_DATA, 256 | DMA16 | DMA_ENABLE | DMA_DST_FIXED); + #endif + buff += BYTE_PER_READ / 2; +#elif defined _CF_ALLOW_UNALIGNED + i=256; + if ((u32)buff_u8 & 0x01) { + while(i--) + { + temp = *buff_u8++; + temp |= *buff_u8++ << 8; + *MP_DATA = temp; + } + } else { + while(i--) + *MP_DATA = *buff++; + } +#else + i=256; + while(i--) + *MP_DATA = *buff++; +#endif + } +#if defined _CF_USE_DMA && defined NDS + // Wait for end of transfer before returning + while(DMA3_CR & DMA_BUSY); +#endif + + return true; +} + +/*----------------------------------------------------------------- +MPCF_Shutdown +unload the GBAMP CF interface +-----------------------------------------------------------------*/ +bool MPCF_Shutdown(void) +{ + return MPCF_ClearStatus() ; +} + +/*----------------------------------------------------------------- +MPCF_StartUp +initializes the CF interface, returns true if successful, +otherwise returns false +-----------------------------------------------------------------*/ +bool MPCF_StartUp(void) +{ + u8 temp = MP_REG_LBA1; + MP_REG_LBA1 = (~temp & 0xFF); + temp = (~temp & 0xFF); + return (MP_REG_LBA1 == temp) ; +} + +/*----------------------------------------------------------------- +the actual interface structure +-----------------------------------------------------------------*/ +IO_INTERFACE io_mpcf = { + DEVICE_TYPE_MPCF, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA, + (FN_MEDIUM_STARTUP)&MPCF_StartUp, + (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted, + (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&MPCF_Shutdown +} ; + +/*----------------------------------------------------------------- +MPCF_GetInterface +returns the interface structure to host +-----------------------------------------------------------------*/ +LPIO_INTERFACE MPCF_GetInterface(void) { + return &io_mpcf ; +} ; + +#endif // SUPPORT_MPCF diff --git a/backends/platform/ds/arm9/source/fat/io_mpcf.h b/backends/platform/ds/arm9/source/fat/io_mpcf.h new file mode 100644 index 0000000000..58cab41b4c --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_mpcf.h @@ -0,0 +1,50 @@ +/* + io_mpcf.h + + Hardware Routines for reading a compact flash card + using the GBA Movie Player + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_MPCF_H +#define IO_MPCF_H + +// 'MPCF' +#define DEVICE_TYPE_MPCF 0x4643504D + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE MPCF_GetInterface(void) ; + +#endif // define IO_MPCF_H +/* + io_mpcf.h + + Hardware Routines for reading a compact flash card + using the GBA Movie Player + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_MPCF_H +#define IO_MPCF_H + +// 'MPCF' +#define DEVICE_TYPE_MPCF 0x4643504D + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE MPCF_GetInterface(void) ; + +#endif // define IO_MPCF_H diff --git a/backends/platform/ds/arm9/source/fat/io_nmmc.c b/backends/platform/ds/arm9/source/fat/io_nmmc.c new file mode 100644 index 0000000000..2e6b99de24 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_nmmc.c @@ -0,0 +1,716 @@ +/* + io_nmmc.c + + Hardware Routines for reading an SD or MMC card using + a Neoflash MK2 or MK3. + + Written by www.neoflash.com + + Submit bug reports for this device to the NeoFlash forums + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. + + 2006-02-09 - www.neoflash.com: + * First stable release + + 2006-02-13 - Chishm + * Added ReadMK2Config function + * Added read config test to init function so no unnecessary card commands are sent + * Changed data read and write functions to use multiple block commands +*/ + +#include "io_nmmc.h" + +#ifdef SUPPORT_NMMC + +#include + +int spi_freq = 3; + +#define MK2_CONFIG_ZIP_RAM_CLOSE (1 << 5) +#define MK2_CONFIG_GAME_FLASH_CLOSE ((1 << 4) | (1 << 0)) +//#define MK2_CONFIG_ZIP_RAM_CLOSE ((1 << 5) | (1 << 1)) +//#define MK2_CONFIG_GAME_FLASH_CLOSE (1 << 4) + +#define MMC_READ_MULTIPLE_BLOCK 18 +#define MMC_READ_BLOCK 17 +#define MMC_WRITE_MULTIPLE_BLOCK 25 +#define MMC_WRITE_BLOCK 24 +#define MMC_STOP_TRANSMISSION 12 +#define MMC_SET_BLOCKLEN 16 +#define MMC_SET_BLOCK_COUNT 23 +#define MMC_SEND_CSD 9 + +// SPI functions + +static inline void Neo_OpenSPI( u8 frequency ) +{ + CARD_CR1 = 0x0000A040 | frequency; +} + +static inline u8 Neo_SPI( u8 dataByte ) +{ + CARD_EEPDATA = dataByte; + while (CARD_CR1 & 0x80); // card busy + return CARD_EEPDATA; +} + +static inline void Neo_CloseSPI ( void ) +{ + CARD_CR1 = 0; +} + +static inline void Neo_MK2GameMode() { + Neo_OpenSPI(spi_freq); // Enable DS Card's SPI port + Neo_SPI(0xF1); // Switch to game mode + Neo_CloseSPI(); // Disable DS Card's SPI port +} + +static inline void Neo_EnableEEPROM( bool enable ) { + Neo_OpenSPI(spi_freq); + if(enable) Neo_SPI(0x06); + else Neo_SPI(0x0E); + Neo_CloseSPI(); +} + +void Neo_WriteMK2Config(u8 config) { + Neo_EnableEEPROM(true); + Neo_OpenSPI(spi_freq); + Neo_SPI(0xFA); // Send mem conf write command + Neo_SPI(0x01); // Send high byte (0x01) + Neo_SPI(config); // Send low byte + Neo_CloseSPI(); + Neo_EnableEEPROM(false); +} + +u8 Neo_ReadMK2Config(void) +{ + u8 config; + Neo_EnableEEPROM(true); + Neo_OpenSPI(spi_freq); + Neo_SPI(0xf8); // Send mem conf read command + Neo_SPI(0x01); // Send high byte + config = Neo_SPI(0x00); // Get low byte + Neo_CloseSPI(); + Neo_EnableEEPROM(false); + return config; +} + +// Low level functions + +u8 selectMMC_command [8] = {0xFF, 0x00, 0x6A, 0xDF, 0x37, 0x59, 0x33, 0xA3}; + +void Neo_SelectMMC (u8 dataByte) +{ + selectMMC_command[1] = dataByte; // Set enable / disable byte + cardWriteCommand (selectMMC_command); // Send "5. Use the EEPROM CS to access the MK2 MMC/SD card" + CARD_CR2 = CARD_ACTIVATE | CARD_nRESET; + while (CARD_CR2 & CARD_BUSY); + return; +} + +void Neo_EnableMMC( bool enable ) +{ + if ( enable == false) { + Neo_CloseSPI (); + Neo_SelectMMC (0); + Neo_SelectMMC (0); + } else { + Neo_SelectMMC (1); + Neo_SelectMMC (1); + Neo_OpenSPI (spi_freq); + } + return; +} + +void Neo_SendMMCCommand( u8 command, u32 argument ) +{ + Neo_SPI (0xFF); + Neo_SPI (command | 0x40); + Neo_SPI ((argument >> 24) & 0xff); + Neo_SPI ((argument >> 16) & 0xff); + Neo_SPI ((argument >> 8) & 0xff) ; + Neo_SPI (argument & 0xff); + Neo_SPI (0x95); + Neo_SPI (0xFF); + return; +} + +bool Neo_CheckMMCResponse( u8 response, u8 mask ) { + u32 i; + for(i=0;i<256;i++) { + if( ( Neo_SPI( 0xFF ) & mask ) == response ) + return true; + } + return false; +} + +// Neo MMC functions + +bool Neo_InitMMC() { + Neo_MK2GameMode(); + Neo_WriteMK2Config( MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE); + + // Make sure the configuration was accepted + if (Neo_ReadMK2Config() != (MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE)) { + return false; // If not, then it wasn't initialised properly + } + + return true; +} + +// Neo MMC driver functions + +bool NMMC_IsInserted(void) { + int i; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + Neo_SendMMCCommand(MMC_SEND_CSD, 0); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + + // consume data from card, and send clocks. + for (i = 0; i < 28; i++) { + Neo_SPI(0xff); + } + + return true; +} + +bool NMMC_ClearStatus (void) { + u32 i; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + for (i = 0; i < 10; i++) { + Neo_SPI(0xFF); // Send 10 0xFF bytes to MMC card + } + Neo_SendMMCCommand(0, 0); // Send GO_IDLE_STATE command + if( Neo_CheckMMCResponse( 0x01, 0xFF ) == false ) { // Check that it replied with 0x01 (not idle, no other error) + Neo_EnableMMC( false ); + return false; + } + for(i=0;i<256;i++) { + Neo_SendMMCCommand(1, 0); // Poll with SEND_OP_COND + if( Neo_CheckMMCResponse( 0x00, 0x01 ) == true ) { // Check for idle state + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; // Card is now idle + } + } + Neo_EnableMMC( false ); + return false; +} + +bool NMMC_Shutdown(void) { + return NMMC_ClearStatus(); +} + +bool NMMC_StartUp(void) { + int i; + int transSpeed; + if (Neo_InitMMC() == false) { + return false; + } + if (NMMC_ClearStatus() == false) { + return false; + } + Neo_EnableMMC( true ); // Open SPI port to MMC card + + // Set block length + Neo_SendMMCCommand(MMC_SET_BLOCKLEN, BYTE_PER_READ ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + // Check if we can use a higher SPI frequency + Neo_SendMMCCommand(MMC_SEND_CSD, 0); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + for (i = 0; i < 3; i++) { + Neo_SPI(0xFF); + } + transSpeed = Neo_SPI (0xFF); + for (i = 0; i < 24; i++) { + Neo_SPI(0xFF); + } + if ((transSpeed & 0xf0) >= 0x30) { + spi_freq = 0; + } + + Neo_EnableMMC( false ); + return true; +} + + +bool NMMC_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + u32 i; + u8 *p=buffer; + + int totalSecs = (numSecs == 0) ? 256 : numSecs; + sector *= BYTE_PER_READ; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + Neo_SendMMCCommand( 25, sector ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + while (totalSecs--) { + Neo_SPI( 0xFC ); // Send Start Block token + for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of data + Neo_SPI( *p++ ); + Neo_SPI( 0xFF ); // Send fake CRC16 + Neo_SPI( 0xFF ); // Send fake CRC16 + + if( ( Neo_SPI( 0xFF ) & 0x0F ) != 0x05 ) { // Make sure the block was accepted + Neo_EnableMMC( false ); + return false; + } + while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the block to be written + } + + // Stop transmission block + Neo_SPI( 0xFD ); // Send Stop Transmission Block token + for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of fake data + Neo_SPI( 0xFF ); + Neo_SPI( 0xFF ); // Send fake CRC16 + Neo_SPI( 0xFF ); // Send fake CRC16 + + Neo_SPI (0xFF); // Send 8 clocks + while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the busy signal to clear + + + for ( i = 0; i < 0x10; i++) { + Neo_SPI (0xFF); // Send clocks for the MMC card to finish what it's doing + } + + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; +} + +bool NMMC_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + u32 i; + u8 *p=buffer; + + int totalSecs = (numSecs == 0) ? 256 : numSecs; + sector *= BYTE_PER_READ; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + + while (totalSecs--) { + Neo_SendMMCCommand(MMC_READ_BLOCK, sector ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + for( i = 0; i < BYTE_PER_READ; i++ ) // Read in a block of data + *p++ = Neo_SPI( 0xFF ); + Neo_SPI( 0xFF ); // Ignore CRC16 + Neo_SPI( 0xFF ); // Ignore CRC16 + sector += BYTE_PER_READ; + } + + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; +} + + +IO_INTERFACE io_nmmc = { + DEVICE_TYPE_NMMC, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE, + (FN_MEDIUM_STARTUP)&NMMC_StartUp, + (FN_MEDIUM_ISINSERTED)&NMMC_IsInserted, + (FN_MEDIUM_READSECTORS)&NMMC_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&NMMC_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&NMMC_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&NMMC_Shutdown +} ; + + +LPIO_INTERFACE NMMC_GetInterface(void) { + return &io_nmmc ; +} + +#endif // #ifdef SUPPORT_NMMC +/* + io_nmmc.c + + Hardware Routines for reading an SD or MMC card using + a Neoflash MK2 or MK3. + + Written by www.neoflash.com + + Submit bug reports for this device to the NeoFlash forums + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. + + 2006-02-09 - www.neoflash.com: + * First stable release + + 2006-02-13 - Chishm + * Added ReadMK2Config function + * Added read config test to init function so no unnecessary card commands are sent + * Changed data read and write functions to use multiple block commands +*/ + +#include "io_nmmc.h" + +#ifdef SUPPORT_NMMC + +#include + +int spi_freq = 3; + +#define MK2_CONFIG_ZIP_RAM_CLOSE (1 << 5) +#define MK2_CONFIG_GAME_FLASH_CLOSE ((1 << 4) | (1 << 0)) +//#define MK2_CONFIG_ZIP_RAM_CLOSE ((1 << 5) | (1 << 1)) +//#define MK2_CONFIG_GAME_FLASH_CLOSE (1 << 4) + +#define MMC_READ_MULTIPLE_BLOCK 18 +#define MMC_READ_BLOCK 17 +#define MMC_WRITE_MULTIPLE_BLOCK 25 +#define MMC_WRITE_BLOCK 24 +#define MMC_STOP_TRANSMISSION 12 +#define MMC_SET_BLOCKLEN 16 +#define MMC_SET_BLOCK_COUNT 23 +#define MMC_SEND_CSD 9 + +// SPI functions + +static inline void Neo_OpenSPI( u8 frequency ) +{ + CARD_CR1 = 0x0000A040 | frequency; +} + +static inline u8 Neo_SPI( u8 dataByte ) +{ + CARD_EEPDATA = dataByte; + while (CARD_CR1 & 0x80); // card busy + return CARD_EEPDATA; +} + +static inline void Neo_CloseSPI ( void ) +{ + CARD_CR1 = 0; +} + +static inline void Neo_MK2GameMode() { + Neo_OpenSPI(spi_freq); // Enable DS Card's SPI port + Neo_SPI(0xF1); // Switch to game mode + Neo_CloseSPI(); // Disable DS Card's SPI port +} + +static inline void Neo_EnableEEPROM( bool enable ) { + Neo_OpenSPI(spi_freq); + if(enable) Neo_SPI(0x06); + else Neo_SPI(0x0E); + Neo_CloseSPI(); +} + +void Neo_WriteMK2Config(u8 config) { + Neo_EnableEEPROM(true); + Neo_OpenSPI(spi_freq); + Neo_SPI(0xFA); // Send mem conf write command + Neo_SPI(0x01); // Send high byte (0x01) + Neo_SPI(config); // Send low byte + Neo_CloseSPI(); + Neo_EnableEEPROM(false); +} + +u8 Neo_ReadMK2Config(void) +{ + u8 config; + Neo_EnableEEPROM(true); + Neo_OpenSPI(spi_freq); + Neo_SPI(0xf8); // Send mem conf read command + Neo_SPI(0x01); // Send high byte + config = Neo_SPI(0x00); // Get low byte + Neo_CloseSPI(); + Neo_EnableEEPROM(false); + return config; +} + +// Low level functions + +u8 selectMMC_command [8] = {0xFF, 0x00, 0x6A, 0xDF, 0x37, 0x59, 0x33, 0xA3}; + +void Neo_SelectMMC (u8 dataByte) +{ + selectMMC_command[1] = dataByte; // Set enable / disable byte + cardWriteCommand (selectMMC_command); // Send "5. Use the EEPROM CS to access the MK2 MMC/SD card" + CARD_CR2 = CARD_ACTIVATE | CARD_nRESET; + while (CARD_CR2 & CARD_BUSY); + return; +} + +void Neo_EnableMMC( bool enable ) +{ + if ( enable == false) { + Neo_CloseSPI (); + Neo_SelectMMC (0); + Neo_SelectMMC (0); + } else { + Neo_SelectMMC (1); + Neo_SelectMMC (1); + Neo_OpenSPI (spi_freq); + } + return; +} + +void Neo_SendMMCCommand( u8 command, u32 argument ) +{ + Neo_SPI (0xFF); + Neo_SPI (command | 0x40); + Neo_SPI ((argument >> 24) & 0xff); + Neo_SPI ((argument >> 16) & 0xff); + Neo_SPI ((argument >> 8) & 0xff) ; + Neo_SPI (argument & 0xff); + Neo_SPI (0x95); + Neo_SPI (0xFF); + return; +} + +bool Neo_CheckMMCResponse( u8 response, u8 mask ) { + u32 i; + for(i=0;i<256;i++) { + if( ( Neo_SPI( 0xFF ) & mask ) == response ) + return true; + } + return false; +} + +// Neo MMC functions + +bool Neo_InitMMC() { + Neo_MK2GameMode(); + Neo_WriteMK2Config( MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE); + + // Make sure the configuration was accepted + if (Neo_ReadMK2Config() != (MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE)) { + return false; // If not, then it wasn't initialised properly + } + + return true; +} + +// Neo MMC driver functions + +bool NMMC_IsInserted(void) { + int i; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + Neo_SendMMCCommand(MMC_SEND_CSD, 0); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + + // consume data from card, and send clocks. + for (i = 0; i < 28; i++) { + Neo_SPI(0xff); + } + + return true; +} + +bool NMMC_ClearStatus (void) { + u32 i; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + for (i = 0; i < 10; i++) { + Neo_SPI(0xFF); // Send 10 0xFF bytes to MMC card + } + Neo_SendMMCCommand(0, 0); // Send GO_IDLE_STATE command + if( Neo_CheckMMCResponse( 0x01, 0xFF ) == false ) { // Check that it replied with 0x01 (not idle, no other error) + Neo_EnableMMC( false ); + return false; + } + for(i=0;i<256;i++) { + Neo_SendMMCCommand(1, 0); // Poll with SEND_OP_COND + if( Neo_CheckMMCResponse( 0x00, 0x01 ) == true ) { // Check for idle state + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; // Card is now idle + } + } + Neo_EnableMMC( false ); + return false; +} + +bool NMMC_Shutdown(void) { + return NMMC_ClearStatus(); +} + +bool NMMC_StartUp(void) { + int i; + int transSpeed; + if (Neo_InitMMC() == false) { + return false; + } + if (NMMC_ClearStatus() == false) { + return false; + } + Neo_EnableMMC( true ); // Open SPI port to MMC card + + // Set block length + Neo_SendMMCCommand(MMC_SET_BLOCKLEN, BYTE_PER_READ ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + // Check if we can use a higher SPI frequency + Neo_SendMMCCommand(MMC_SEND_CSD, 0); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + for (i = 0; i < 3; i++) { + Neo_SPI(0xFF); + } + transSpeed = Neo_SPI (0xFF); + for (i = 0; i < 24; i++) { + Neo_SPI(0xFF); + } + if ((transSpeed & 0xf0) >= 0x30) { + spi_freq = 0; + } + + Neo_EnableMMC( false ); + return true; +} + + +bool NMMC_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + u32 i; + u8 *p=buffer; + + int totalSecs = (numSecs == 0) ? 256 : numSecs; + sector *= BYTE_PER_READ; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + Neo_SendMMCCommand( 25, sector ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + while (totalSecs--) { + Neo_SPI( 0xFC ); // Send Start Block token + for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of data + Neo_SPI( *p++ ); + Neo_SPI( 0xFF ); // Send fake CRC16 + Neo_SPI( 0xFF ); // Send fake CRC16 + + if( ( Neo_SPI( 0xFF ) & 0x0F ) != 0x05 ) { // Make sure the block was accepted + Neo_EnableMMC( false ); + return false; + } + while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the block to be written + } + + // Stop transmission block + Neo_SPI( 0xFD ); // Send Stop Transmission Block token + for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of fake data + Neo_SPI( 0xFF ); + Neo_SPI( 0xFF ); // Send fake CRC16 + Neo_SPI( 0xFF ); // Send fake CRC16 + + Neo_SPI (0xFF); // Send 8 clocks + while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the busy signal to clear + + + for ( i = 0; i < 0x10; i++) { + Neo_SPI (0xFF); // Send clocks for the MMC card to finish what it's doing + } + + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; +} + +bool NMMC_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + u32 i; + u8 *p=buffer; + + int totalSecs = (numSecs == 0) ? 256 : numSecs; + sector *= BYTE_PER_READ; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + + while (totalSecs--) { + Neo_SendMMCCommand(MMC_READ_BLOCK, sector ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + for( i = 0; i < BYTE_PER_READ; i++ ) // Read in a block of data + *p++ = Neo_SPI( 0xFF ); + Neo_SPI( 0xFF ); // Ignore CRC16 + Neo_SPI( 0xFF ); // Ignore CRC16 + sector += BYTE_PER_READ; + } + + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; +} + + +IO_INTERFACE io_nmmc = { + DEVICE_TYPE_NMMC, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE, + (FN_MEDIUM_STARTUP)&NMMC_StartUp, + (FN_MEDIUM_ISINSERTED)&NMMC_IsInserted, + (FN_MEDIUM_READSECTORS)&NMMC_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&NMMC_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&NMMC_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&NMMC_Shutdown +} ; + + +LPIO_INTERFACE NMMC_GetInterface(void) { + return &io_nmmc ; +} + +#endif // #ifdef SUPPORT_NMMC diff --git a/backends/platform/ds/arm9/source/fat/io_nmmc.h b/backends/platform/ds/arm9/source/fat/io_nmmc.h new file mode 100644 index 0000000000..5244d21868 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_nmmc.h @@ -0,0 +1,54 @@ +/* + io_NMMC.h + + Hardware Routines for reading an SD or MMC card using + a Neoflash MK2 or MK3. + + Written by www.neoflash.com + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_NMMC_H +#define IO_NMMC_H + +// 'NMMC' +#define DEVICE_TYPE_NMMC 0x434D4D4E + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE NMMC_GetInterface(void) ; + +#endif // define IO_NMMC_H +/* + io_NMMC.h + + Hardware Routines for reading an SD or MMC card using + a Neoflash MK2 or MK3. + + Written by www.neoflash.com + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_NMMC_H +#define IO_NMMC_H + +// 'NMMC' +#define DEVICE_TYPE_NMMC 0x434D4D4E + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE NMMC_GetInterface(void) ; + +#endif // define IO_NMMC_H diff --git a/backends/platform/ds/arm9/source/fat/io_sccf.c b/backends/platform/ds/arm9/source/fat/io_sccf.c new file mode 100644 index 0000000000..336387e9d7 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_sccf.c @@ -0,0 +1,174 @@ +/* + io_sccf.c based on + + compact_flash.c + By chishm (Michael Chisholm) + + Hardware Routines for reading a compact flash card + using the Super Card CF + + CF routines modified with help from Darkfader + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + + +#include "io_sccf.h" + +#ifdef SUPPORT_SCCF + +#ifndef SUPPORT_MPCF + #error Supercard CF support requires GBAMP CF support +#endif // SUPPORT_MPCF + +/*----------------------------------------------------------------- +Since all CF addresses and commands are the same for the GBAMP, +simply use it's functions instead. +-----------------------------------------------------------------*/ + +extern bool MPCF_IsInserted (void); +extern bool MPCF_ClearStatus (void); +extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer); +extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer); + + +/*----------------------------------------------------------------- +SCCF_Unlock +Returns true if SuperCard was unlocked, false if failed +Added by MightyMax +Modified by Chishm +-----------------------------------------------------------------*/ +bool SCCF_Unlock(void) +{ +#define CF_REG_LBA1 *(volatile unsigned short *)0x09060000 + unsigned char temp; + volatile short *unlockAddress = (volatile short *)0x09FFFFFE; + *unlockAddress = 0xA55A ; + *unlockAddress = 0xA55A ; + *unlockAddress = 0x3 ; + *unlockAddress = 0x3 ; + // provoke a ready reply + temp = CF_REG_LBA1; + CF_REG_LBA1 = (~temp & 0xFF); + temp = (~temp & 0xFF); + return (CF_REG_LBA1 == temp); +#undef CF_REG_LBA1 +} + +bool SCCF_Shutdown(void) { + return MPCF_ClearStatus() ; +} ; + +bool SCCF_StartUp(void) { + return SCCF_Unlock() ; +} ; + + +IO_INTERFACE io_sccf = { + DEVICE_TYPE_SCCF, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA, + (FN_MEDIUM_STARTUP)&SCCF_StartUp, + (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted, + (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&SCCF_Shutdown +} ; + + +LPIO_INTERFACE SCCF_GetInterface(void) { + return &io_sccf ; +} ; + +#endif // SUPPORT_SCCF +/* + io_sccf.c based on + + compact_flash.c + By chishm (Michael Chisholm) + + Hardware Routines for reading a compact flash card + using the Super Card CF + + CF routines modified with help from Darkfader + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + + +#include "io_sccf.h" + +#ifdef SUPPORT_SCCF + +#ifndef SUPPORT_MPCF + #error Supercard CF support requires GBAMP CF support +#endif // SUPPORT_MPCF + +/*----------------------------------------------------------------- +Since all CF addresses and commands are the same for the GBAMP, +simply use it's functions instead. +-----------------------------------------------------------------*/ + +extern bool MPCF_IsInserted (void); +extern bool MPCF_ClearStatus (void); +extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer); +extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer); + + +/*----------------------------------------------------------------- +SCCF_Unlock +Returns true if SuperCard was unlocked, false if failed +Added by MightyMax +Modified by Chishm +-----------------------------------------------------------------*/ +bool SCCF_Unlock(void) +{ +#define CF_REG_LBA1 *(volatile unsigned short *)0x09060000 + unsigned char temp; + volatile short *unlockAddress = (volatile short *)0x09FFFFFE; + *unlockAddress = 0xA55A ; + *unlockAddress = 0xA55A ; + *unlockAddress = 0x3 ; + *unlockAddress = 0x3 ; + // provoke a ready reply + temp = CF_REG_LBA1; + CF_REG_LBA1 = (~temp & 0xFF); + temp = (~temp & 0xFF); + return (CF_REG_LBA1 == temp); +#undef CF_REG_LBA1 +} + +bool SCCF_Shutdown(void) { + return MPCF_ClearStatus() ; +} ; + +bool SCCF_StartUp(void) { + return SCCF_Unlock() ; +} ; + + +IO_INTERFACE io_sccf = { + DEVICE_TYPE_SCCF, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_GBA, + (FN_MEDIUM_STARTUP)&SCCF_StartUp, + (FN_MEDIUM_ISINSERTED)&MPCF_IsInserted, + (FN_MEDIUM_READSECTORS)&MPCF_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&MPCF_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&SCCF_Shutdown +} ; + + +LPIO_INTERFACE SCCF_GetInterface(void) { + return &io_sccf ; +} ; + +#endif // SUPPORT_SCCF diff --git a/backends/platform/ds/arm9/source/fat/io_sccf.h b/backends/platform/ds/arm9/source/fat/io_sccf.h new file mode 100644 index 0000000000..961909fbce --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_sccf.h @@ -0,0 +1,50 @@ +/* + io_sccf.h + + Hardware Routines for reading a compact flash card + using the Supercard CF + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_SCCF_H +#define IO_SCCF_H + +// 'SCCF' +#define DEVICE_TYPE_SCCF 0x46434353 + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE SCCF_GetInterface(void) ; + +#endif // define IO_SCCF_H +/* + io_sccf.h + + Hardware Routines for reading a compact flash card + using the Supercard CF + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_SCCF_H +#define IO_SCCF_H + +// 'SCCF' +#define DEVICE_TYPE_SCCF 0x46434353 + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE SCCF_GetInterface(void) ; + +#endif // define IO_SCCF_H diff --git a/backends/platform/ds/arm9/source/fat/io_scsd.c b/backends/platform/ds/arm9/source/fat/io_scsd.c new file mode 100644 index 0000000000..a2fccf45d2 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_scsd.c @@ -0,0 +1,186 @@ +/* + io_scsd.c by SaTa. + based on io_sccf.c + + +*/ + +/* + io_sccf.c based on + + compact_flash.c + By chishm (Michael Chisholm) + + Hardware Routines for reading a compact flash card + using the Super Card CF + + CF routines modified with help from Darkfader + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + + +#include "io_scsd.h" + +/*----------------------------------------------------------------- +Since all CF addresses and commands are the same for the GBAMP, +simply use it's functions instead. +-----------------------------------------------------------------*/ + +extern bool MPCF_IsInserted (void); +extern bool MPCF_ClearStatus (void); +extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer); +extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer); + +// add by SaTa. +extern void InitSCMode(void); // CF‚Æ“¯‚¶ +extern void ReadSector(u16 *buff,u32 sector,u8 ReadNumber); +extern void WriteSector(u16 *buff,u32 sector,u8 writeNumber); +extern bool MemoryCard_IsInserted(void); // CF‚ƈႤ +// + +/*----------------------------------------------------------------- +SCSD_Unlock +Returns true if SuperCard was unlocked, false if failed +Added by MightyMax +Modified by Chishm +-----------------------------------------------------------------*/ +bool SCSD_Unlock(void) +{ + InitSCMode(); + return MemoryCard_IsInserted(); +} + +bool SCSD_Shutdown(void) { + return MPCF_ClearStatus() ; +} ; + +bool SCSD_StartUp(void) { + return SCSD_Unlock() ; +} ; + +bool SCSD_ReadSectors (u32 sector, u8 ReadNumber, void* buff) +{ + ReadSector((u16 *)buff,sector,ReadNumber); + return true; +} + +bool SCSD_WriteSectors (u32 sector, u8 writeNumber, void* buff) +{ + WriteSector((u16 *)buff,sector,writeNumber); + return true; +} + + +IO_INTERFACE io_scsd = { + 0x44534353, // 'SCSD' + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE, + (FN_MEDIUM_STARTUP)&SCSD_StartUp, + (FN_MEDIUM_ISINSERTED)&SCSD_Unlock, + (FN_MEDIUM_READSECTORS)&SCSD_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&SCSD_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&SCSD_Shutdown +} ; + + +LPIO_INTERFACE SCSD_GetInterface(void) { + return &io_scsd ; +} ; +/* + io_scsd.c by SaTa. + based on io_sccf.c + + +*/ + +/* + io_sccf.c based on + + compact_flash.c + By chishm (Michael Chisholm) + + Hardware Routines for reading a compact flash card + using the Super Card CF + + CF routines modified with help from Darkfader + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + + +#include "io_scsd.h" + +/*----------------------------------------------------------------- +Since all CF addresses and commands are the same for the GBAMP, +simply use it's functions instead. +-----------------------------------------------------------------*/ + +extern bool MPCF_IsInserted (void); +extern bool MPCF_ClearStatus (void); +extern bool MPCF_ReadSectors (u32 sector, u8 numSecs, void* buffer); +extern bool MPCF_WriteSectors (u32 sector, u8 numSecs, void* buffer); + +// add by SaTa. +extern void InitSCMode(void); // CF‚Æ“¯‚¶ +extern void ReadSector(u16 *buff,u32 sector,u8 ReadNumber); +extern void WriteSector(u16 *buff,u32 sector,u8 writeNumber); +extern bool MemoryCard_IsInserted(void); // CF‚ƈႤ +// + +/*----------------------------------------------------------------- +SCSD_Unlock +Returns true if SuperCard was unlocked, false if failed +Added by MightyMax +Modified by Chishm +-----------------------------------------------------------------*/ +bool SCSD_Unlock(void) +{ + InitSCMode(); + return MemoryCard_IsInserted(); +} + +bool SCSD_Shutdown(void) { + return MPCF_ClearStatus() ; +} ; + +bool SCSD_StartUp(void) { + return SCSD_Unlock() ; +} ; + +bool SCSD_ReadSectors (u32 sector, u8 ReadNumber, void* buff) +{ + ReadSector((u16 *)buff,sector,ReadNumber); + return true; +} + +bool SCSD_WriteSectors (u32 sector, u8 writeNumber, void* buff) +{ + WriteSector((u16 *)buff,sector,writeNumber); + return true; +} + + +IO_INTERFACE io_scsd = { + 0x44534353, // 'SCSD' + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE, + (FN_MEDIUM_STARTUP)&SCSD_StartUp, + (FN_MEDIUM_ISINSERTED)&SCSD_Unlock, + (FN_MEDIUM_READSECTORS)&SCSD_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&SCSD_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&MPCF_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&SCSD_Shutdown +} ; + + +LPIO_INTERFACE SCSD_GetInterface(void) { + return &io_scsd ; +} ; diff --git a/backends/platform/ds/arm9/source/fat/io_scsd.h b/backends/platform/ds/arm9/source/fat/io_scsd.h new file mode 100644 index 0000000000..1e4e17dbb8 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_scsd.h @@ -0,0 +1,58 @@ +/* + io_scsd.h by SaTa. + based on io_sccf.h + + +*/ + +/* + io_sccf.h + + Hardware Routines for reading a compact flash card + using the GBA Movie Player + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_SCSD_H +#define IO_SCSD_H + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE SCSD_GetInterface(void) ; + +#endif // define IO_SCSD_H +/* + io_scsd.h by SaTa. + based on io_sccf.h + + +*/ + +/* + io_sccf.h + + Hardware Routines for reading a compact flash card + using the GBA Movie Player + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. +*/ + +#ifndef IO_SCSD_H +#define IO_SCSD_H + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE SCSD_GetInterface(void) ; + +#endif // define IO_SCSD_H diff --git a/backends/platform/ds/arm9/source/fat/io_scsd_asm.s b/backends/platform/ds/arm9/source/fat/io_scsd_asm.s new file mode 100644 index 0000000000..f138c07205 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_scsd_asm.s @@ -0,0 +1,1042 @@ + .TEXT +@--------------------------------sd data-------------------------------- +.equ sd_comadd,0x9800000 +.equ sd_dataadd,0x9000000 +.equ sd_dataradd,0x9100000 +@-----------------viod sd_data_write_s(u16 *buff,u16* crc16buff)------------------- + .ALIGN + .GLOBAL sd_data_write_s + .CODE 32 +sd_data_write_s: + stmfd r13!,{r4-r5} + mov r5,#512 + mov r2,#sd_dataadd +sd_data_write_busy: + ldrh r3,[r2] + tst r3,#0x100 + beq sd_data_write_busy + + ldrh r3,[r2] + + mov r3,#0 @star bit + strh r3,[r2] +sd_data_write_loop: + ldrh r3,[r0],#2 + add r3,r3,r3,lsl #20 + mov r4,r3,lsl #8 + stmia r2,{r3-r4} + + subs r5, r5, #2 + bne sd_data_write_loop + + cmp r1,#0 + movne r0,r1 + movne r1,#0 + movne r5,#8 + bne sd_data_write_loop + + mov r3,#0xff @end bit + strh r3,[r2] +sd_data_write_loop2: + ldrh r3,[r2] + tst r3,#0x100 + bne sd_data_write_loop2 + + ldmia r2,{r3-r4} + + ldmfd r13!,{r4-r5} + bx r14 +@-----------------end sd_data_write_s------------------- + +@----------void sd_data_read_s(u16 *buff)------------- + .ALIGN + .GLOBAL sd_data_read_s + .CODE 32 +sd_data_read_s: + stmfd r13!,{r4} + mov r1,#sd_dataradd +sd_data_read_loop1: + ldrh r3,[r1] @star bit + tst r3,#0x100 + bne sd_data_read_loop1 + mov r2,#512 +sd_data_read_loop: + ldmia r1,{r3-r4} + mov r3,r4,lsr #16 + strh r3 ,[r0],#2 + + ldmia r1,{r3-r4} + mov r3,r4,lsr #16 + strh r3 ,[r0],#2 + + ldmia r1,{r3-r4} + mov r3,r4,lsr #16 + strh r3 ,[r0],#2 + + ldmia r1,{r3-r4} + mov r3,r4,lsr #16 + strh r3 ,[r0],#2 + + ldmia r1,{r3-r4} + mov r3,r4,lsr #16 + strh r3 ,[r0],#2 + + ldmia r1,{r3-r4} + mov r3,r4,lsr #16 + strh r3 ,[r0],#2 + + ldmia r1,{r3-r4} + mov r3,r4,lsr #16 + strh r3 ,[r0],#2 + + ldmia r1,{r3-r4} + mov r3,r4,lsr #16 + strh r3 ,[r0],#2 + + subs r2, r2, #16 + bne sd_data_read_loop + + ldmia r1,{r3-r4} @crc 16 + ldmia r1,{r3-r4} + ldmia r1,{r3-r4} + ldmia r1,{r3-r4} + ldrh r3,[r1] @end bit + ldmfd r13!,{r4} + bx r14 +@----------end sd_data_read_s------------- + +@------void sd_com_crc16_s(u16* buff,u16 num,u16* crc16buff) + .ALIGN + .GLOBAL sd_crc16_s + .CODE 32 +sd_crc16_s: + stmfd r13!,{r4-r9} + mov r9,r2 + + mov r3,#0 + mov r4,#0 + mov r5,#0 + mov r6,#0 + + ldr r7,=0x80808080 + ldr r8,=0x1021 + mov r1,r1,lsl #3 +sd_crc16_loop: + + tst r7,#0x80 + ldrneb r2,[r0],#1 + + mov r3,r3,lsl #1 + tst r3,#0x10000 + eorne r3,r3,r8 + tst r2,r7,lsr #24 + eorne r3,r3,r8 + + mov r4,r4,lsl #1 + tst r4,#0x10000 + eorne r4,r4,r8 + tst r2,r7,lsr #25 + eorne r4,r4,r8 + + mov r5,r5,lsl #1 + tst r5,#0x10000 + eorne r5,r5,r8 + tst r2,r7,lsr #26 + eorne r5,r5,r8 + + mov r6,r6,lsl #1 + tst r6,#0x10000 + eorne r6,r6,r8 + tst r2,r7,lsr #27 + eorne r6,r6,r8 + + mov r7,r7,ror #4 + subs r1,r1,#4 + bne sd_crc16_loop + + mov r2,r9 + mov r8,#16 +sd_crc16_write_data: + mov r7,r7,lsl #4 + tst r3,#0x8000 + orrne r7,r7,#8 + tst r4,#0x8000 + orrne r7,r7,#4 + tst r5,#0x8000 + orrne r7,r7,#2 + tst r6,#0x8000 + orrne r7,r7,#1 + + mov r3,r3,lsl #1 + mov r4,r4,lsl #1 + mov r5,r5,lsl #1 + mov r6,r6,lsl #1 + + sub r8,r8,#1 + tst r8,#1 + streqb r7,[r2],#1 + cmp r8,#0 + bne sd_crc16_write_data + + ldmfd r13!,{r4-r9} + bx r14 +@------end sd_com_crc16_s----------------------------------- + +@--------------u8 sd_crc7_s(u16* buff,u16 num)---------------------------- + .ALIGN + .GLOBAL sd_crc7_s + .CODE 32 +sd_crc7_s: + stmfd r13!,{r4} + + mov r3,#0 + ldr r4,=0x80808080 + mov r1,r1,lsl #3 @ *8 +sd_crc7_loop: + tst r4,#0x80 + ldrneb r2,[r0],#1 + + mov r3,r3,lsl #1 + + tst r3,#0x80 + eorne r3,r3,#0x9 + + tst r2,r4,lsr #24 + eorne r3,r3,#0x9 + + mov r4,r4,ror #1 + subs r1,r1,#1 + bne sd_crc7_loop + + mov r3,r3,lsl #1 + add r0,r3,#1 + ldmfd r13!,{r4} + bx r14 +@--------------end sd_crc7_s---------------------------- + +@--------------sd_com_read_s(u16* buff,u32 num)------------------ + .ALIGN + .GLOBAL sd_com_read_s + .CODE 32 + +sd_com_read_s: + stmfd r13!,{r4-r6} + mov r2,#sd_comadd +sd_com_read_loop1: + ldrh r3,[r2] @star bit + tst r3,#1 + bne sd_com_read_loop1 + +sd_com_read_loop: + ldmia r2,{r3-r6} + subs r1, r1, #1 + bne sd_com_read_loop + ldmfd r13!,{r4-r6} + bx r14 +@--------------end sd_com_read_s------------------ + +@-------------------void sd_com_write_s(u16* buff,u32 num)----------------- + + .ALIGN + .GLOBAL sd_com_write_s + .CODE 32 +sd_com_write_s: + stmfd r13!,{r4-r6} + + mov r2,#sd_comadd +sd_com_write_busy: + ldrh r3,[r2] + tst r3,#0x1 + beq sd_com_write_busy + + ldrh r3,[r2] + +sd_com_write_loop: + ldrb r3,[r0],#1 + add r3,r3,r3,lsl #17 + mov r4,r3,lsl #2 + mov r5,r4,lsl #2 + mov r6,r5,lsl #2 + stmia r2,{r3-r6} + subs r1, r1, #1 + bne sd_com_write_loop + ldmfd r13!,{r4-r6} + + bx r14 +@-------------------end sd_com_write_s----------------- + +@-----------------void send_clk(u32 num)--------- + .ALIGN + .GLOBAL send_clk + .CODE 32 + +send_clk: + mov r1,#sd_comadd +send_clk_loop1: + ldrh r3,[r1] + subs r0,r0,#1 + bne send_clk_loop1 + bx r14 +@-----------------end send_clk--------- + +@---------------------------void SDCommand(u8 command,u8 num,u32 sector)-------------------- + .ALIGN + .GLOBAL SDCommand + .CODE 32 +@void SDCommand(u8 command,u8 num,u32 sector ) +@{ +@ u8 databuff[6]; +@ register u8 *char1; +@ register u8 *char2; +@ +@ char1=(u8*)(((u32)§or)+3); +@ char2=(u8*)databuff; +@ *char2++=coma+0x40; +@ *char2++=*char1--; +@ *char2++=*char1--; +@ *char2++=*char1--; +@ *char2++=*char1; +@ *char2=sd_crc7_s((u32)databuff,5); +@ +@ sd_com_write_s((u32)databuff,6); +@ +@} +SDCommand: + stmfd r13!,{r14} + + sub r13,r13,#16 + add r0,r0,#0x40 + strb r0,[r13,#4] + + mov r1,r2,lsr #24 + strb r1,[r13,#5] + mov r1,r2,lsr #16 + strb r1,[r13,#6] + mov r1,r2,lsr #8 + strb r1,[r13,#7] + strb r2,[r13,#8] + add r0,r13,#4 + mov r1,#5 + bl sd_crc7_s + strb r0,[r13,#9] + add r0,r13,#4 + mov r1,#6 + bl sd_com_write_s + + add r13,r13,#16 + ldmfd r13!,{r15} +@ bx r14 +@---------------------------end SDCommand-------------------- + +@----------void ReadSector(u16 *buff,u32 sector,u8 readnum)------------- + .ALIGN + .GLOBAL ReadSector @ r0:Srcp r1:num ok + .CODE 32 + +@void ReadSector(u16 *buff,u32 sector,u8 readnum) +@{ +@ register u16 i,j; +@ i=readnum; +@ sectno<<=9; +@ SDCommand(18,0,sector); +@ for (j=0;j SAVE_BUFFER_SIZE) { + flushSaveBuffer(); + saveSize += size; + DS::std_fwrite(buf, 1, size, handle); +/* int pos = 0; + + int rest = SAVE_BUFFER_SIZE - bufferPos; + memcpy(buffer + bufferPos, buf, rest); + bufferPos = 512; + pos += rest; + flushSaveBuffer(); + size -= rest; +// consolePrintf("First section: %d\n", rest); + + while (size >= 512) { + DS::std_fwrite(((char *) (buf)) + pos, 1, 512, handle); + size -= 512; + pos += 512; +// consolePrintf("Full chunk, %d left ", size); + } + + bufferPos = 0; + memcpy(buffer + bufferPos, ((char *) (buf)) + pos, size); + bufferPos += size; +// consolePrintf("%d left in buffer ", bufferPos);*/ + + } else { + + memcpy(buffer + bufferPos, buf, size); + bufferPos += size; + + saveSize += size; + } + +// if ((size > 100) || (size <= 0)) consolePrintf("Write %d bytes\n", size); + return size; +} + + +////////////////////////// +// GBAMP Save File Manager +////////////////////////// + +GBAMPSaveFileManager::GBAMPSaveFileManager() { + +} + +GBAMPSaveFileManager::~GBAMPSaveFileManager() { + +} + +Common::SaveFile* GBAMPSaveFileManager::openSavefile(char const* name, bool saveOrLoad) { + char fileSpec[128]; + + strcpy(fileSpec, getSavePath()); + + if (fileSpec[strlen(fileSpec) - 1] == '/') { + sprintf(fileSpec, "%s%s", getSavePath(), name); + } else { + sprintf(fileSpec, "%s/%s", getSavePath(), name); + } + +// consolePrintf(fileSpec); + + return new GBAMPSaveFile(fileSpec, saveOrLoad); +} + +void GBAMPSaveFileManager::listSavefiles(char const* prefix, bool* marks, int num) { + enum { TYPE_NO_MORE = 0, TYPE_FILE = 1, TYPE_DIR = 2 }; + char name[128]; + char path[128]; + + DS::std_cwd((char *) getSavePath()); + + int fileType = FAT_FindFirstFile(name); + + for (int r = 0; r < num; r++) { + marks[r] = false; + } + + do { + + if (fileType == TYPE_FILE) { + + FAT_GetLongFilename(name); + + for (int r = 0; r < num; r++) { + char str[128]; + + + sprintf(str, "%s%02d", prefix, r); +// consolePrintf("%s != %s", str, name); + if (!stricmp(str, name)) { + marks[r] = true; +// consolePrintf("Matched %d", r); + } + + } + + } + + } while ((fileType = FAT_FindNextFile(name))); + + FAT_chdir("/"); +} +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "gbampsave.h" +#include "gba_nds_fat.h" + +///////////////////////// +// GBAMP Save File +///////////////////////// + +GBAMPSaveFile::GBAMPSaveFile(char* name, bool saveOrLoad) { + handle = DS::std_fopen(name, saveOrLoad? "w": "r"); + //consolePrintf("%s handle is %d\n", name, handle); +// consolePrintf("Created %s\n", name); + bufferPos = 0; + saveSize = 0; + flushed = 0; +} + +GBAMPSaveFile::~GBAMPSaveFile() { + flushSaveBuffer(); + DS::std_fclose(handle); +} + +uint32 GBAMPSaveFile::read(void *buf, uint32 size) { + saveSize += size; +// consolePrintf("Read %d %d ", size, saveSize); + return DS::std_fread(buf, 1, size, handle); +} + +bool GBAMPSaveFile::eos() const { + return DS::std_feof(handle); +} + +void GBAMPSaveFile::skip(uint32 bytes) { + DS::std_fseek(handle, bytes, SEEK_CUR); +} + +void GBAMPSaveFile::flushSaveBuffer() { + if (bufferPos != 0) { +// consolePrintf("Flushing %d bytes\n", bufferPos); + flushed += bufferPos; + DS::std_fwrite(buffer, 1, bufferPos, handle); + bufferPos = 0; + } +} + +uint32 GBAMPSaveFile::pos() const { + return DS::std_ftell(handle); +} + +uint32 GBAMPSaveFile::size() const { + int position = pos(); + DS::std_fseek(handle, 0, SEEK_END); + int size = DS::std_ftell(handle); + DS::std_fseek(handle, position, SEEK_SET); + return size; +} + +void GBAMPSaveFile::seek(int32 pos, int whence) { + DS::std_fseek(handle, pos, whence); +} + + +uint32 GBAMPSaveFile::write(const void *buf, uint32 size) { + if (bufferPos + size > SAVE_BUFFER_SIZE) { + flushSaveBuffer(); + saveSize += size; + DS::std_fwrite(buf, 1, size, handle); +/* int pos = 0; + + int rest = SAVE_BUFFER_SIZE - bufferPos; + memcpy(buffer + bufferPos, buf, rest); + bufferPos = 512; + pos += rest; + flushSaveBuffer(); + size -= rest; +// consolePrintf("First section: %d\n", rest); + + while (size >= 512) { + DS::std_fwrite(((char *) (buf)) + pos, 1, 512, handle); + size -= 512; + pos += 512; +// consolePrintf("Full chunk, %d left ", size); + } + + bufferPos = 0; + memcpy(buffer + bufferPos, ((char *) (buf)) + pos, size); + bufferPos += size; +// consolePrintf("%d left in buffer ", bufferPos);*/ + + } else { + + memcpy(buffer + bufferPos, buf, size); + bufferPos += size; + + saveSize += size; + } + +// if ((size > 100) || (size <= 0)) consolePrintf("Write %d bytes\n", size); + return size; +} + + +////////////////////////// +// GBAMP Save File Manager +////////////////////////// + +GBAMPSaveFileManager::GBAMPSaveFileManager() { + +} + +GBAMPSaveFileManager::~GBAMPSaveFileManager() { + +} + +Common::SaveFile* GBAMPSaveFileManager::openSavefile(char const* name, bool saveOrLoad) { + char fileSpec[128]; + + strcpy(fileSpec, getSavePath()); + + if (fileSpec[strlen(fileSpec) - 1] == '/') { + sprintf(fileSpec, "%s%s", getSavePath(), name); + } else { + sprintf(fileSpec, "%s/%s", getSavePath(), name); + } + +// consolePrintf(fileSpec); + + return new GBAMPSaveFile(fileSpec, saveOrLoad); +} + +void GBAMPSaveFileManager::listSavefiles(char const* prefix, bool* marks, int num) { + enum { TYPE_NO_MORE = 0, TYPE_FILE = 1, TYPE_DIR = 2 }; + char name[128]; + char path[128]; + + DS::std_cwd((char *) getSavePath()); + + int fileType = FAT_FindFirstFile(name); + + for (int r = 0; r < num; r++) { + marks[r] = false; + } + + do { + + if (fileType == TYPE_FILE) { + + FAT_GetLongFilename(name); + + for (int r = 0; r < num; r++) { + char str[128]; + + + sprintf(str, "%s%02d", prefix, r); +// consolePrintf("%s != %s", str, name); + if (!stricmp(str, name)) { + marks[r] = true; +// consolePrintf("Matched %d", r); + } + + } + + } + + } while ((fileType = FAT_FindNextFile(name))); + + FAT_chdir("/"); +} diff --git a/backends/platform/ds/arm9/source/gbampsave.h b/backends/platform/ds/arm9/source/gbampsave.h new file mode 100644 index 0000000000..e454b49d4c --- /dev/null +++ b/backends/platform/ds/arm9/source/gbampsave.h @@ -0,0 +1,164 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _GBAMPSAVE_H_ +#define _GBAMPSAVE_H_ + +#include "stdafx.h" +#include "system.h" + +#define SAVE_BUFFER_SIZE 100000 + +class GBAMPSaveFile : public Common::SaveFile { + FILE* handle; + char buffer[SAVE_BUFFER_SIZE]; + int bufferPos; + int saveSize; + int flushed; + +public: + GBAMPSaveFile(char* name, bool saveOrLoad); + ~GBAMPSaveFile(); + + virtual uint32 read(void *buf, uint32 size); + virtual uint32 write(const void *buf, uint32 size); + + virtual bool eos() const; + virtual void skip(uint32 bytes); + + virtual uint32 pos() const; + virtual uint32 size() const; + virtual void seek(int32 pos, int whence); + + void flushSaveBuffer(); + + virtual bool isOpen() const { + return true; + } +}; + + +class GBAMPSaveFileManager : public Common::SaveFileManager { + + +public: + GBAMPSaveFileManager(); + ~GBAMPSaveFileManager(); + +// static GBAMPSaveFileManager* instance() { return instancePtr; } + + Common::SaveFile *openSavefile(const char *filename, bool saveOrLoad); + + virtual Common::OutSaveFile* openForSaving(const char* filename) { return openSavefile(filename, true); } + virtual Common::InSaveFile* openForLoading(const char* filename) { return openSavefile(filename, false); } + + + void listSavefiles(const char *prefix, bool *marks, int num); + + void deleteFile(char* name); + void listFiles(); + +protected: + Common::SaveFile *makeSaveFile(const char *filename, bool saveOrLoad); +}; + + +#endif +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _GBAMPSAVE_H_ +#define _GBAMPSAVE_H_ + +#include "stdafx.h" +#include "system.h" + +#define SAVE_BUFFER_SIZE 100000 + +class GBAMPSaveFile : public Common::SaveFile { + FILE* handle; + char buffer[SAVE_BUFFER_SIZE]; + int bufferPos; + int saveSize; + int flushed; + +public: + GBAMPSaveFile(char* name, bool saveOrLoad); + ~GBAMPSaveFile(); + + virtual uint32 read(void *buf, uint32 size); + virtual uint32 write(const void *buf, uint32 size); + + virtual bool eos() const; + virtual void skip(uint32 bytes); + + virtual uint32 pos() const; + virtual uint32 size() const; + virtual void seek(int32 pos, int whence); + + void flushSaveBuffer(); + + virtual bool isOpen() const { + return true; + } +}; + + +class GBAMPSaveFileManager : public Common::SaveFileManager { + + +public: + GBAMPSaveFileManager(); + ~GBAMPSaveFileManager(); + +// static GBAMPSaveFileManager* instance() { return instancePtr; } + + Common::SaveFile *openSavefile(const char *filename, bool saveOrLoad); + + virtual Common::OutSaveFile* openForSaving(const char* filename) { return openSavefile(filename, true); } + virtual Common::InSaveFile* openForLoading(const char* filename) { return openSavefile(filename, false); } + + + void listSavefiles(const char *prefix, bool *marks, int num); + + void deleteFile(char* name); + void listFiles(); + +protected: + Common::SaveFile *makeSaveFile(const char *filename, bool saveOrLoad); +}; + + +#endif diff --git a/backends/platform/ds/arm9/source/mad/readme.txt b/backends/platform/ds/arm9/source/mad/readme.txt new file mode 100644 index 0000000000..09a2ec07df --- /dev/null +++ b/backends/platform/ds/arm9/source/mad/readme.txt @@ -0,0 +1,2 @@ +Put mad.h here if you are compiling with Madlib support enabled. +Put mad.h here if you are compiling with Madlib support enabled. \ No newline at end of file diff --git a/backends/platform/ds/arm9/source/osystem_ds.cpp b/backends/platform/ds/arm9/source/osystem_ds.cpp new file mode 100644 index 0000000000..37a79728c3 --- /dev/null +++ b/backends/platform/ds/arm9/source/osystem_ds.cpp @@ -0,0 +1,968 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include "stdafx.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "system.h" + +#include "common/util.h" +#include "common/rect.h" +#include "common/savefile.h" + +#include "osystem_ds.h" +#include "nds.h" +#include "dsmain.h" +#include "nds/registers_alt.h" +#include "config-manager.h" +#include "common/str.h" +#include "cdaudio.h" +#include "graphics/surface.h" + +OSystem_DS* OSystem_DS::_instance = NULL; + +OSystem_DS::OSystem_DS() +{ + eventNum = 0; + lastPenFrame = 0; + queuePos = 0; + _instance = this; +} + +OSystem_DS::~OSystem_DS() { +} + +void OSystem_DS::initBackend() { + ConfMan.setInt("autosave_period", 0); + ConfMan.setBool("FM_medium_quality", true); +} + +bool OSystem_DS::hasFeature(Feature f) { +// consolePrintf("hasfeature\n"); + return (f == kFeatureVirtualKeyboard); +} + +void OSystem_DS::setFeatureState(Feature f, bool enable) { +// consolePrintf("setfeature f=%d e=%d\n", f, enable); + if (f == kFeatureVirtualKeyboard) DS::setKeyboardIcon(enable); +} + +bool OSystem_DS::getFeatureState(Feature f) { +// consolePrintf("getfeat\n"); + if (f == kFeatureVirtualKeyboard) return DS::getKeyboardIcon(); + return false; +} + +const OSystem::GraphicsMode* OSystem_DS::getSupportedGraphicsModes() const { + return s_supportedGraphicsModes; +} + + +int OSystem_DS::getDefaultGraphicsMode() const { + return 0; +} + +bool OSystem_DS::setGraphicsMode(int mode) { + return true; +} + +bool OSystem_DS::setGraphicsMode(const char *name) { +// consolePrintf("Set gfx mode %s\n", name); + return true; +} + +int OSystem_DS::getGraphicsMode() const { + return -1; +} + +void OSystem_DS::initSize(uint width, uint height) { +// consolePrintf("Set gfx mode %d x %d\n", width, height); + DS::setGameSize(width, height); +} + +int16 OSystem_DS::getHeight() { + return 200; +} + +int16 OSystem_DS::getWidth() { + return 320; +} + +void OSystem_DS::setPalette(const byte *colors, uint start, uint num) { +// consolePrintf("Set palette %d, %d colours\n", start, num); + if (!DS::getIsDisplayMode8Bit()) return; + + for (unsigned int r = start; r < start + num; r++) { + int red = *colors; + int green = *(colors + 1); + int blue = *(colors + 2); + + red >>= 3; + green >>= 3; + blue >>= 3; + + BG_PALETTE[r] = red | (green << 5) | (blue << 10); + if (!DS::getKeyboardEnable()) { + BG_PALETTE_SUB[r] = red | (green << 5) | (blue << 10); + } +// if (num == 16) consolePrintf("pal:%d r:%d g:%d b:%d\n", r, red, green, blue); + + colors += 4; + } +} + + +void OSystem_DS::grabPalette(unsigned char *colors, uint start, uint num) { +// consolePrintf("Grabpalette"); + + for (unsigned int r = start; r < start + num; r++) { + *colors++ = (BG_PALETTE[r] & 0x001F) << 3; + *colors++ = (BG_PALETTE[r] & 0x03E0) >> 5 << 3; + *colors++ = (BG_PALETTE[r] & 0x7C00) >> 10 << 3; + } +} + + +void OSystem_DS::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) +{ +// consolePrintf("Copy rect %d, %d %d, %d ", x, y, w, h); + + if (w <= 1) return; + if (h < 0) return; + if (!DS::getIsDisplayMode8Bit()) return; + + u16* bgSub = (u16 *) BG_GFX_SUB; + u16* bg = (u16 *) DS::get8BitBackBuffer(); + u16* src = (u16 *) buf; + + if (DS::getKeyboardEnable()) { + + for (int dy = y; dy < y + h; dy++) { + u16* dest = bg + (dy << 8) + (x >> 1); + + DC_FlushRange(src, w << 1); + DC_FlushRange(dest, w << 1); + dmaCopyHalfWords(3, src, dest, w); + + src += pitch >> 1; + } + + } else { + for (int dy = y; dy < y + h; dy++) { + u16* dest1 = bg + (dy << 8) + (x >> 1); + u16* dest2 = bgSub + (dy << 8) + (x >> 1); + + DC_FlushRange(src, w << 1); + DC_FlushRange(dest1, w << 1); + DC_FlushRange(dest2, w << 1); + + dmaCopyHalfWords(3, src, dest1, w); + dmaCopyHalfWords(3, src, dest2, w); + + src += pitch >> 1; + } + } + +// consolePrintf("Done\n"); + + + +} + +void OSystem_DS::updateScreen() +{ + DS::displayMode16BitFlipBuffer(); + DS::doSoundCallback(); +// DS::doTimerCallback(); + DS::addEventsToQueue(); +} + +void OSystem_DS::setShakePos(int shakeOffset) { + DS::setShakePos(shakeOffset); +} + +void OSystem_DS::showOverlay () +{ +// consolePrintf("showovl\n"); + DS::displayMode16Bit(); +} + +void OSystem_DS::hideOverlay () +{ + DS::displayMode8Bit(); +} + +void OSystem_DS::clearOverlay () +{ + memset((u16 *) DS::get16BitBackBuffer(), 0, 512 * 256 * 2); +// consolePrintf("clearovl\n"); +} + +void OSystem_DS::grabOverlay (OverlayColor *buf, int pitch) +{ +// consolePrintf("grabovl\n"); +} + +void OSystem_DS::copyRectToOverlay (const OverlayColor *buf, int pitch, int x, int y, int w, int h) +{ + u16* bg = (u16 *) DS::get16BitBackBuffer(); + u16* src = (u16 *) buf; + +// if (x + w > 256) w = 256 - x; + //if (x + h > 256) h = 256 - y; + +// consolePrintf("Copy rect ovl %d, %d %d, %d %d\n", x, y, w, h, pitch); + + + + for (int dy = y; dy < y + h; dy++) { + + + // Slow but save copy: + for (int dx = x; dx < x + w; dx++) { + + *(bg + (dy * 512) + dx) = *src; + //if ((*src) != 0) consolePrintf("%d,%d: %d ", dx, dy, *src); + //consolePrintf("%d,", *src); + src++; + } + src += (pitch - w); + + // Fast but broken copy: (why?) + /* + REG_IME = 0; + dmaCopy(src, bg + (dy << 9) + x, w * 2); + REG_IME = 1; + + src += pitch;*/ + } + +// consolePrintf("Copy rect ovl done"); + +} + +int16 OSystem_DS::getOverlayHeight() +{ +// consolePrintf("getovlheight\n"); + return getHeight(); +} + +int16 OSystem_DS::getOverlayWidth() +{ +// consolePrintf("getovlwid\n"); + return getWidth(); +} + + +bool OSystem_DS::showMouse(bool visible) +{ + return true; +} + +void OSystem_DS::warpMouse(int x, int y) +{ +} + +void OSystem_DS::setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor, int targetCursorScale) { + DS::setCursorIcon(buf, w, h, keycolor); +} + +void OSystem_DS::addEvent(Event& e) { + eventQueue[queuePos++] = e; +} + +bool OSystem_DS::pollEvent(Event &event) +{ + + if (lastPenFrame != DS::getMillis()) { + + if (eventNum == queuePos) { + eventNum = 0; + queuePos = 0; + // Bodge - this last event seems to be processed sometimes and not others. + // So we make it something harmless which won't cause any adverse effects. + event.type = EVENT_KEYUP; + event.kbd.ascii = 0; + event.kbd.keycode = 0; + event.kbd.flags = 0; + return false; + } else { + event = eventQueue[eventNum++]; + return true; + } + } + + return false; + +/* if (lastPenFrame != DS::getMillis()) { + if ((eventNum == 0)) { + event.type = EVENT_MOUSEMOVE; + event.mouse = Common::Point(DS::getPenX(), DS::getPenY()); + eventNum = 1; + return true; + } + if (eventNum == 1) { + eventNum = 0; + lastPenFrame = DS::getMillis(); + if (DS::getPenDown()) { + event.type = EVENT_LBUTTONDOWN; + event.mouse = Common::Point(DS::getPenX(), DS::getPenY()); + consolePrintf("Down %d, %d ", event.mouse.x, event.mouse.y); + return true; + } else if (DS::getPenReleased()) { + event.type = EVENT_LBUTTONUP; + event.mouse = Common::Point(DS::getPenX(), DS::getPenY()); + consolePrintf("Up %d, %d ", event.mouse.x, event.mouse.y); + return true; + } else { + return false; + } + } + }*/ + + return false; +} + +uint32 OSystem_DS::getMillis() +{ + return DS::getMillis(); +} + +void OSystem_DS::delayMillis(uint msecs) +{ + int st = getMillis(); + DS::addEventsToQueue(); + DS::CD::update(); + + DS::doSoundCallback(); + while (st + msecs >= getMillis()) { + DS::doSoundCallback(); + } + + DS::doTimerCallback(); + DS::checkSleepMode(); + DS::addEventsToQueue(); +} + +void OSystem_DS::setTimerCallback(TimerProc callback, int interval) +{ +// consolePrintf("Settimercallback interval=%d\n", interval); + DS::setTimerCallback(callback, interval); +} + +OSystem::MutexRef OSystem_DS::createMutex(void) +{ + return NULL; +} + +void OSystem_DS::lockMutex(MutexRef mutex) +{ +} + +void OSystem_DS::unlockMutex(MutexRef mutex) +{ +} + +void OSystem_DS::deleteMutex(MutexRef mutex) +{ +} + +bool OSystem_DS::setSoundCallback(SoundProc proc, void *param) +{ +// consolePrintf("Setsoundcallback"); + DS::setSoundProc(proc, param); + return true; +} + +void OSystem_DS::clearSoundCallback() +{ + consolePrintf("Clearing sound callback"); +// DS::setSoundProc(NULL, NULL); +} + +int OSystem_DS::getOutputSampleRate() const +{ + return 11025; +} + +bool OSystem_DS::openCD(int drive) +{ + return DS::CD::checkCD(); +} + +bool OSystem_DS::pollCD() +{ + return DS::CD::isPlaying(); +} + +void OSystem_DS::playCD(int track, int num_loops, int start_frame, int duration) +{ + DS::CD::playTrack(track, num_loops, start_frame, duration); +} + +void OSystem_DS::stopCD() +{ + DS::CD::stopTrack(); +} + +void OSystem_DS::updateCD() +{ +} + +void OSystem_DS::quit() +{ +/* consolePrintf("Soft resetting..."); + IPC->reset = 1; + REG_IE = 0; + + asm("swi 0x26\n"); + swiSoftReset();*/ +} + +void OSystem_DS::setWindowCaption(const char *caption) +{ +} + +void OSystem_DS::displayMessageOnOSD(const char *msg) +{ +} + +Common::SaveFileManager* OSystem_DS::getSavefileManager() +{ + bool forceSram; + + if (ConfMan.hasKey("forcesramsave", "ds")) { + forceSram = ConfMan.getBool("forcesramsave", "ds"); + } else { + forceSram = false; + } + if (forceSram) { + consolePrintf("Using SRAM save method!\n"); + } + + if (DS::isGBAMPAvailable() && (!forceSram)) { + return &mpSaveManager; + } else { + return &saveManager; + } +} + +bool OSystem_DS::grabRawScreen(Graphics::Surface* surf) { + surf->create(DS::getGameWidth(), DS::getGameHeight(), 1); + memcpy(surf->pixels, DS::get8BitBackBuffer(), DS::getGameWidth() * DS::getGameHeight()); + return true; +} + +void OSystem_DS::setFocusRectangle(const Common::Rect& rect) { + DS::setTalkPos(rect.left + rect.width() / 2, rect.top + rect.height() / 2); +} + +void OSystem_DS::clearFocusRectangle() { + +} + + +OSystem *OSystem_DS_create() { + return new OSystem_DS(); +} + +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include "stdafx.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "system.h" + +#include "common/util.h" +#include "common/rect.h" +#include "common/savefile.h" + +#include "osystem_ds.h" +#include "nds.h" +#include "dsmain.h" +#include "nds/registers_alt.h" +#include "config-manager.h" +#include "common/str.h" +#include "cdaudio.h" +#include "graphics/surface.h" + +OSystem_DS* OSystem_DS::_instance = NULL; + +OSystem_DS::OSystem_DS() +{ + eventNum = 0; + lastPenFrame = 0; + queuePos = 0; + _instance = this; +} + +OSystem_DS::~OSystem_DS() { +} + +void OSystem_DS::initBackend() { + ConfMan.setInt("autosave_period", 0); + ConfMan.setBool("FM_medium_quality", true); +} + +bool OSystem_DS::hasFeature(Feature f) { +// consolePrintf("hasfeature\n"); + return (f == kFeatureVirtualKeyboard); +} + +void OSystem_DS::setFeatureState(Feature f, bool enable) { +// consolePrintf("setfeature f=%d e=%d\n", f, enable); + if (f == kFeatureVirtualKeyboard) DS::setKeyboardIcon(enable); +} + +bool OSystem_DS::getFeatureState(Feature f) { +// consolePrintf("getfeat\n"); + if (f == kFeatureVirtualKeyboard) return DS::getKeyboardIcon(); + return false; +} + +const OSystem::GraphicsMode* OSystem_DS::getSupportedGraphicsModes() const { + return s_supportedGraphicsModes; +} + + +int OSystem_DS::getDefaultGraphicsMode() const { + return 0; +} + +bool OSystem_DS::setGraphicsMode(int mode) { + return true; +} + +bool OSystem_DS::setGraphicsMode(const char *name) { +// consolePrintf("Set gfx mode %s\n", name); + return true; +} + +int OSystem_DS::getGraphicsMode() const { + return -1; +} + +void OSystem_DS::initSize(uint width, uint height) { +// consolePrintf("Set gfx mode %d x %d\n", width, height); + DS::setGameSize(width, height); +} + +int16 OSystem_DS::getHeight() { + return 200; +} + +int16 OSystem_DS::getWidth() { + return 320; +} + +void OSystem_DS::setPalette(const byte *colors, uint start, uint num) { +// consolePrintf("Set palette %d, %d colours\n", start, num); + if (!DS::getIsDisplayMode8Bit()) return; + + for (unsigned int r = start; r < start + num; r++) { + int red = *colors; + int green = *(colors + 1); + int blue = *(colors + 2); + + red >>= 3; + green >>= 3; + blue >>= 3; + + BG_PALETTE[r] = red | (green << 5) | (blue << 10); + if (!DS::getKeyboardEnable()) { + BG_PALETTE_SUB[r] = red | (green << 5) | (blue << 10); + } +// if (num == 16) consolePrintf("pal:%d r:%d g:%d b:%d\n", r, red, green, blue); + + colors += 4; + } +} + + +void OSystem_DS::grabPalette(unsigned char *colors, uint start, uint num) { +// consolePrintf("Grabpalette"); + + for (unsigned int r = start; r < start + num; r++) { + *colors++ = (BG_PALETTE[r] & 0x001F) << 3; + *colors++ = (BG_PALETTE[r] & 0x03E0) >> 5 << 3; + *colors++ = (BG_PALETTE[r] & 0x7C00) >> 10 << 3; + } +} + + +void OSystem_DS::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) +{ +// consolePrintf("Copy rect %d, %d %d, %d ", x, y, w, h); + + if (w <= 1) return; + if (h < 0) return; + if (!DS::getIsDisplayMode8Bit()) return; + + u16* bgSub = (u16 *) BG_GFX_SUB; + u16* bg = (u16 *) DS::get8BitBackBuffer(); + u16* src = (u16 *) buf; + + if (DS::getKeyboardEnable()) { + + for (int dy = y; dy < y + h; dy++) { + u16* dest = bg + (dy << 8) + (x >> 1); + + DC_FlushRange(src, w << 1); + DC_FlushRange(dest, w << 1); + dmaCopyHalfWords(3, src, dest, w); + + src += pitch >> 1; + } + + } else { + for (int dy = y; dy < y + h; dy++) { + u16* dest1 = bg + (dy << 8) + (x >> 1); + u16* dest2 = bgSub + (dy << 8) + (x >> 1); + + DC_FlushRange(src, w << 1); + DC_FlushRange(dest1, w << 1); + DC_FlushRange(dest2, w << 1); + + dmaCopyHalfWords(3, src, dest1, w); + dmaCopyHalfWords(3, src, dest2, w); + + src += pitch >> 1; + } + } + +// consolePrintf("Done\n"); + + + +} + +void OSystem_DS::updateScreen() +{ + DS::displayMode16BitFlipBuffer(); + DS::doSoundCallback(); +// DS::doTimerCallback(); + DS::addEventsToQueue(); +} + +void OSystem_DS::setShakePos(int shakeOffset) { + DS::setShakePos(shakeOffset); +} + +void OSystem_DS::showOverlay () +{ +// consolePrintf("showovl\n"); + DS::displayMode16Bit(); +} + +void OSystem_DS::hideOverlay () +{ + DS::displayMode8Bit(); +} + +void OSystem_DS::clearOverlay () +{ + memset((u16 *) DS::get16BitBackBuffer(), 0, 512 * 256 * 2); +// consolePrintf("clearovl\n"); +} + +void OSystem_DS::grabOverlay (OverlayColor *buf, int pitch) +{ +// consolePrintf("grabovl\n"); +} + +void OSystem_DS::copyRectToOverlay (const OverlayColor *buf, int pitch, int x, int y, int w, int h) +{ + u16* bg = (u16 *) DS::get16BitBackBuffer(); + u16* src = (u16 *) buf; + +// if (x + w > 256) w = 256 - x; + //if (x + h > 256) h = 256 - y; + +// consolePrintf("Copy rect ovl %d, %d %d, %d %d\n", x, y, w, h, pitch); + + + + for (int dy = y; dy < y + h; dy++) { + + + // Slow but save copy: + for (int dx = x; dx < x + w; dx++) { + + *(bg + (dy * 512) + dx) = *src; + //if ((*src) != 0) consolePrintf("%d,%d: %d ", dx, dy, *src); + //consolePrintf("%d,", *src); + src++; + } + src += (pitch - w); + + // Fast but broken copy: (why?) + /* + REG_IME = 0; + dmaCopy(src, bg + (dy << 9) + x, w * 2); + REG_IME = 1; + + src += pitch;*/ + } + +// consolePrintf("Copy rect ovl done"); + +} + +int16 OSystem_DS::getOverlayHeight() +{ +// consolePrintf("getovlheight\n"); + return getHeight(); +} + +int16 OSystem_DS::getOverlayWidth() +{ +// consolePrintf("getovlwid\n"); + return getWidth(); +} + + +bool OSystem_DS::showMouse(bool visible) +{ + return true; +} + +void OSystem_DS::warpMouse(int x, int y) +{ +} + +void OSystem_DS::setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor, int targetCursorScale) { + DS::setCursorIcon(buf, w, h, keycolor); +} + +void OSystem_DS::addEvent(Event& e) { + eventQueue[queuePos++] = e; +} + +bool OSystem_DS::pollEvent(Event &event) +{ + + if (lastPenFrame != DS::getMillis()) { + + if (eventNum == queuePos) { + eventNum = 0; + queuePos = 0; + // Bodge - this last event seems to be processed sometimes and not others. + // So we make it something harmless which won't cause any adverse effects. + event.type = EVENT_KEYUP; + event.kbd.ascii = 0; + event.kbd.keycode = 0; + event.kbd.flags = 0; + return false; + } else { + event = eventQueue[eventNum++]; + return true; + } + } + + return false; + +/* if (lastPenFrame != DS::getMillis()) { + if ((eventNum == 0)) { + event.type = EVENT_MOUSEMOVE; + event.mouse = Common::Point(DS::getPenX(), DS::getPenY()); + eventNum = 1; + return true; + } + if (eventNum == 1) { + eventNum = 0; + lastPenFrame = DS::getMillis(); + if (DS::getPenDown()) { + event.type = EVENT_LBUTTONDOWN; + event.mouse = Common::Point(DS::getPenX(), DS::getPenY()); + consolePrintf("Down %d, %d ", event.mouse.x, event.mouse.y); + return true; + } else if (DS::getPenReleased()) { + event.type = EVENT_LBUTTONUP; + event.mouse = Common::Point(DS::getPenX(), DS::getPenY()); + consolePrintf("Up %d, %d ", event.mouse.x, event.mouse.y); + return true; + } else { + return false; + } + } + }*/ + + return false; +} + +uint32 OSystem_DS::getMillis() +{ + return DS::getMillis(); +} + +void OSystem_DS::delayMillis(uint msecs) +{ + int st = getMillis(); + DS::addEventsToQueue(); + DS::CD::update(); + + DS::doSoundCallback(); + while (st + msecs >= getMillis()) { + DS::doSoundCallback(); + } + + DS::doTimerCallback(); + DS::checkSleepMode(); + DS::addEventsToQueue(); +} + +void OSystem_DS::setTimerCallback(TimerProc callback, int interval) +{ +// consolePrintf("Settimercallback interval=%d\n", interval); + DS::setTimerCallback(callback, interval); +} + +OSystem::MutexRef OSystem_DS::createMutex(void) +{ + return NULL; +} + +void OSystem_DS::lockMutex(MutexRef mutex) +{ +} + +void OSystem_DS::unlockMutex(MutexRef mutex) +{ +} + +void OSystem_DS::deleteMutex(MutexRef mutex) +{ +} + +bool OSystem_DS::setSoundCallback(SoundProc proc, void *param) +{ +// consolePrintf("Setsoundcallback"); + DS::setSoundProc(proc, param); + return true; +} + +void OSystem_DS::clearSoundCallback() +{ + consolePrintf("Clearing sound callback"); +// DS::setSoundProc(NULL, NULL); +} + +int OSystem_DS::getOutputSampleRate() const +{ + return 11025; +} + +bool OSystem_DS::openCD(int drive) +{ + return DS::CD::checkCD(); +} + +bool OSystem_DS::pollCD() +{ + return DS::CD::isPlaying(); +} + +void OSystem_DS::playCD(int track, int num_loops, int start_frame, int duration) +{ + DS::CD::playTrack(track, num_loops, start_frame, duration); +} + +void OSystem_DS::stopCD() +{ + DS::CD::stopTrack(); +} + +void OSystem_DS::updateCD() +{ +} + +void OSystem_DS::quit() +{ +/* consolePrintf("Soft resetting..."); + IPC->reset = 1; + REG_IE = 0; + + asm("swi 0x26\n"); + swiSoftReset();*/ +} + +void OSystem_DS::setWindowCaption(const char *caption) +{ +} + +void OSystem_DS::displayMessageOnOSD(const char *msg) +{ +} + +Common::SaveFileManager* OSystem_DS::getSavefileManager() +{ + bool forceSram; + + if (ConfMan.hasKey("forcesramsave", "ds")) { + forceSram = ConfMan.getBool("forcesramsave", "ds"); + } else { + forceSram = false; + } + if (forceSram) { + consolePrintf("Using SRAM save method!\n"); + } + + if (DS::isGBAMPAvailable() && (!forceSram)) { + return &mpSaveManager; + } else { + return &saveManager; + } +} + +bool OSystem_DS::grabRawScreen(Graphics::Surface* surf) { + surf->create(DS::getGameWidth(), DS::getGameHeight(), 1); + memcpy(surf->pixels, DS::get8BitBackBuffer(), DS::getGameWidth() * DS::getGameHeight()); + return true; +} + +void OSystem_DS::setFocusRectangle(Common::Rect& rect) { + DS::setTalkPos(rect.left + rect.width() / 2, rect.top + rect.height() / 2); +} + +void OSystem_DS::clearFocusRectangle() { + +} + + +OSystem *OSystem_DS_create() { + return new OSystem_DS(); +} + diff --git a/backends/platform/ds/arm9/source/osystem_ds.h b/backends/platform/ds/arm9/source/osystem_ds.h new file mode 100644 index 0000000000..fc108db670 --- /dev/null +++ b/backends/platform/ds/arm9/source/osystem_ds.h @@ -0,0 +1,284 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _OSYSTEM_DS_H_ +#define _OSYSTEM_DS_H_ +#include "common/system.h" +#include "nds.h" +#include "ramsave.h" +#include "gbampsave.h" + +class OSystem_DS : public OSystem { +public: + static OSystem_DS *instance() { return _instance; } + int eventNum; + int lastPenFrame; + + Event eventQueue[64]; + int queuePos; + + DSSaveFileManager saveManager; + GBAMPSaveFileManager mpSaveManager; + + static OSystem_DS* _instance; + +public: + + OSystem_DS(); + virtual ~OSystem_DS(); + + virtual bool hasFeature(Feature f); + virtual void setFeatureState(Feature f, bool enable); + virtual bool getFeatureState(Feature f); + virtual const GraphicsMode *getSupportedGraphicsModes() const; + virtual int getDefaultGraphicsMode() const; + virtual bool setGraphicsMode(int mode); + bool setGraphicsMode(const char *name); + virtual int getGraphicsMode() const; + virtual void initSize(uint width, uint height); + virtual int16 getHeight(); + virtual int16 getWidth(); + virtual void setPalette(const byte *colors, uint start, uint num); + virtual void grabPalette(unsigned char* colors, uint start, uint num); + + virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); + virtual void updateScreen(); + virtual void setShakePos(int shakeOffset); + + virtual void showOverlay(); + virtual void hideOverlay(); + virtual void clearOverlay(); + virtual void grabOverlay(OverlayColor *buf, int pitch); + virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h); + virtual int16 getOverlayHeight(); + virtual int16 getOverlayWidth(); + + inline virtual OverlayColor RGBToColor(uint8 r, uint8 g, uint8 b); + inline virtual void colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b); + + virtual bool showMouse(bool visible); + + virtual void warpMouse(int x, int y); + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int targetCursorScale = 1); + + virtual bool pollEvent(Event &event); + virtual uint32 getMillis(); + virtual void delayMillis(uint msecs); + + virtual void setTimerCallback(TimerProc callback, int interval); + + virtual MutexRef createMutex(void); + virtual void lockMutex(MutexRef mutex); + virtual void unlockMutex(MutexRef mutex); + virtual void deleteMutex(MutexRef mutex); + + virtual bool setSoundCallback(SoundProc proc, void *param); + virtual void clearSoundCallback(); + virtual int getOutputSampleRate() const; + + virtual bool openCD(int drive); + virtual bool pollCD(); + + virtual void playCD(int track, int num_loops, int start_frame, int duration); + virtual void stopCD(); + virtual void updateCD(); + + virtual void quit(); + + virtual void setWindowCaption(const char *caption); + + virtual void displayMessageOnOSD(const char *msg); + + virtual Common::SaveFileManager *getSavefileManager(); + + void addEvent(Event& e); + bool isEventQueueEmpty() { return queuePos == 0; } + + virtual bool grabRawScreen(Graphics::Surface* surf); + + virtual void setFocusRectangle(const Common::Rect& rect); + + virtual void clearFocusRectangle(); + + virtual void initBackend(); +}; + +static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { + {0, 0, 0}, +}; + +OverlayColor OSystem_DS::RGBToColor(uint8 r, uint8 g, uint8 b) +{ + return 0x8000 | (r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 5); + //consolePrintf("rgbtocol\n"); + return 0; +} + +void OSystem_DS::colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b) +{ + r = (color & 0x001F) << 3; + g = (color & 0x03E0) >> 5 << 3; + b = (color & 0x7C00) >> 10 << 3; + //consolePrintf("coltorgb\n"); +} + +#endif +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _OSYSTEM_DS_H_ +#define _OSYSTEM_DS_H_ +#include "common/system.h" +#include "nds.h" +#include "ramsave.h" +#include "gbampsave.h" + +class OSystem_DS : public OSystem { +public: + static OSystem_DS *instance() { return _instance; } + int eventNum; + int lastPenFrame; + + Event eventQueue[64]; + int queuePos; + + DSSaveFileManager saveManager; + GBAMPSaveFileManager mpSaveManager; + + static OSystem_DS* _instance; + +public: + + OSystem_DS(); + virtual ~OSystem_DS(); + + virtual bool hasFeature(Feature f); + virtual void setFeatureState(Feature f, bool enable); + virtual bool getFeatureState(Feature f); + virtual const GraphicsMode *getSupportedGraphicsModes() const; + virtual int getDefaultGraphicsMode() const; + virtual bool setGraphicsMode(int mode); + bool setGraphicsMode(const char *name); + virtual int getGraphicsMode() const; + virtual void initSize(uint width, uint height); + virtual int16 getHeight(); + virtual int16 getWidth(); + virtual void setPalette(const byte *colors, uint start, uint num); + virtual void grabPalette(unsigned char* colors, uint start, uint num); + + virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); + virtual void updateScreen(); + virtual void setShakePos(int shakeOffset); + + virtual void showOverlay(); + virtual void hideOverlay(); + virtual void clearOverlay(); + virtual void grabOverlay(OverlayColor *buf, int pitch); + virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h); + virtual int16 getOverlayHeight(); + virtual int16 getOverlayWidth(); + + inline virtual OverlayColor RGBToColor(uint8 r, uint8 g, uint8 b); + inline virtual void colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b); + + virtual bool showMouse(bool visible); + + virtual void warpMouse(int x, int y); + virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int targetCursorScale = 1); + + virtual bool pollEvent(Event &event); + virtual uint32 getMillis(); + virtual void delayMillis(uint msecs); + + virtual void setTimerCallback(TimerProc callback, int interval); + + virtual MutexRef createMutex(void); + virtual void lockMutex(MutexRef mutex); + virtual void unlockMutex(MutexRef mutex); + virtual void deleteMutex(MutexRef mutex); + + virtual bool setSoundCallback(SoundProc proc, void *param); + virtual void clearSoundCallback(); + virtual int getOutputSampleRate() const; + + virtual bool openCD(int drive); + virtual bool pollCD(); + + virtual void playCD(int track, int num_loops, int start_frame, int duration); + virtual void stopCD(); + virtual void updateCD(); + + virtual void quit(); + + virtual void setWindowCaption(const char *caption); + + virtual void displayMessageOnOSD(const char *msg); + + virtual Common::SaveFileManager *getSavefileManager(); + + void addEvent(Event& e); + bool isEventQueueEmpty() { return queuePos == 0; } + + virtual bool grabRawScreen(Graphics::Surface* surf); + + virtual void setFocusRectangle(Common::Rect& rect); + + virtual void clearFocusRectangle(); + + virtual void initBackend(); +}; + +static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { + {0, 0, 0}, +}; + +OverlayColor OSystem_DS::RGBToColor(uint8 r, uint8 g, uint8 b) +{ + return 0x8000 | (r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 5); + //consolePrintf("rgbtocol\n"); + return 0; +} + +void OSystem_DS::colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b) +{ + r = (color & 0x001F) << 3; + g = (color & 0x03E0) >> 5 << 3; + b = (color & 0x7C00) >> 10 << 3; + //consolePrintf("coltorgb\n"); +} + +#endif diff --git a/backends/platform/ds/arm9/source/portdefs.cpp b/backends/platform/ds/arm9/source/portdefs.cpp new file mode 100644 index 0000000000..b9d107a4db --- /dev/null +++ b/backends/platform/ds/arm9/source/portdefs.cpp @@ -0,0 +1,84 @@ + +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "portdefs.h" +#include +#include "nds/dma.h" +#include "osystem_ds.h" + +time_t DS_time(time_t) { + if (OSystem_DS::instance()) { + return 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000); + } else { + return 0xABCD1234; + } +} + +time_t DS_time(long* t) { + if (OSystem_DS::instance()) { + if (t) *t = 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000); + return 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000); + } else { + if (t) *t = 0xABCD1234; + return 0xABCD1234; + } +} + +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "portdefs.h" +#include +#include "nds/dma.h" +#include "osystem_ds.h" + +time_t DS_time(time_t) { + if (OSystem_DS::instance()) { + return 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000); + } else { + return 0xABCD1234; + } +} + +time_t DS_time(long* t) { + if (OSystem_DS::instance()) { + if (t) *t = 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000); + return 0xABCD1234 + (OSystem_DS::instance()->getMillis() / 1000); + } else { + if (t) *t = 0xABCD1234; + return 0xABCD1234; + } +} diff --git a/backends/platform/ds/arm9/source/portdefs.h b/backends/platform/ds/arm9/source/portdefs.h new file mode 100644 index 0000000000..1683583432 --- /dev/null +++ b/backends/platform/ds/arm9/source/portdefs.h @@ -0,0 +1,100 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _PORTDEFS_H_ +#define _PORTDEFS_H_ + + +typedef unsigned char u8; +typedef signed char s8; + +typedef unsigned short u16; +typedef signed short s16; + +typedef unsigned int u32; +typedef signed int s32; + +//#define double float + + +#undef assert +#define assert(expr) consolePrintf("Asserted!") +//#define NO_DEBUG_MSGS +#include "ds-fs.h" + +//#define debug(fmt, ...) consolePrintf(fmt, ##__VA_ARGS__) +//#define debug(fmt, ...) debug(0, fmt, ##__VA_ARGS__) +#define time(t) DS_time(t) +//#define memcpy(dest, src, size) DS_memcpy(dest, src, size) + +time_t DS_time(time_t* t); +time_t DS_time(long* t); +void* DS_memcpy(void* s1, void const* s2, size_t n); + +#endif +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _PORTDEFS_H_ +#define _PORTDEFS_H_ + + +typedef unsigned char u8; +typedef signed char s8; + +typedef unsigned short u16; +typedef signed short s16; + +typedef unsigned int u32; +typedef signed int s32; + +//#define double float + + +#undef assert +#define assert(expr) consolePrintf("Asserted!") +//#define NO_DEBUG_MSGS +#include "ds-fs.h" + +//#define debug(fmt, ...) consolePrintf(fmt, ##__VA_ARGS__) +//#define debug(fmt, ...) debug(0, fmt, ##__VA_ARGS__) +#define time(t) DS_time(t) +//#define memcpy(dest, src, size) DS_memcpy(dest, src, size) + +time_t DS_time(time_t* t); +time_t DS_time(long* t); +void* DS_memcpy(void* s1, void const* s2, size_t n); + +#endif diff --git a/backends/platform/ds/arm9/source/ramsave.cpp b/backends/platform/ds/arm9/source/ramsave.cpp new file mode 100644 index 0000000000..8a23baff73 --- /dev/null +++ b/backends/platform/ds/arm9/source/ramsave.cpp @@ -0,0 +1,918 @@ +/* Ramsave + * Copyright (C) 2002-2004 Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + // Save in order 1,2,3,4,larger 2,5 +#include "stdafx.h" +#include "system.h" +#include "ramsave.h" +#include "nds.h" +#include "compressor/lz.h" + +#define CART_RAM ((vu8 *) (0x0A000000)) + +DSSaveFile::DSSaveFile() { + ptr = 0; + saveCompressed = false; + save.isValid = false; + ownsData = false; + isOpenFlag = true; + isTempFile = false; +} + +DSSaveFile::DSSaveFile(SCUMMSave* s, bool compressed, u8* data) { + save = *s; + saveData = data; + ptr = 0; + saveCompressed = compressed; + isOpenFlag = true; + + if (saveCompressed) { + u8* uncompressed = new unsigned char[save.size]; + if (!uncompressed) consolePrintf("Out of memory allocating %d!\n", save.size); + LZ_Uncompress(saveData, uncompressed, save.compressedSize); + saveData = uncompressed; + ownsData = true; + saveCompressed = false; +// consolePrintf("Decompressed. name=%s size=%d (%d)", save.name, save.size, save.compressedSize); + + } else { + ownsData = false; + origHeader = s; + } + + if (save.magic == (int) 0xBEEFCAFE) { + save.isValid = true; + } else { + save.isValid = false; + } + + isTempFile = false; +} + +DSSaveFile::~DSSaveFile() { + if (!ownsData) { + *origHeader = save; + DSSaveFileManager::instance()->flushToSaveRAM(); + } + if (ownsData) { + delete saveData; + } +} + +bool DSSaveFile::loadFromSaveRAM(vu8* address) { + + SCUMMSave newSave; + + for (int t = 0; t < (int) sizeof(newSave); t++) { + ((char *) (&newSave))[t] = *(address + t); + } + + + if (newSave.magic == 0xBEEFCAFE) { + newSave.isValid = true; + + *((u16 *) (0x4000204)) |= 0x3; + + saveData = new unsigned char[newSave.compressedSize]; + + for (int t = 0; t < (int) newSave.compressedSize; t++) { + ((char *) (saveData))[t] = *(address + t + sizeof(newSave)); + } + + if (ownsData) delete this->saveData; + save = newSave; + saveCompressed = true; + this->saveData = saveData; + ownsData = true; + ptr = 0; + return true; + } + + return false; +} + +void DSSaveFile::compress() { + if (!saveCompressed) { + unsigned char* compBuffer = new unsigned char[(save.size * 110) / 100]; + int compSize = LZ_Compress((u8 *) saveData, compBuffer, save.size); + save.compressedSize = compSize; + + + + delete saveData; + + // Make the save smaller + saveData = (u8 *) realloc(compBuffer, save.compressedSize); + saveCompressed = true; + } +} + +int DSSaveFile::saveToSaveRAM(vu8* address) { + + unsigned char* compBuffer; + bool failed; + + + int compSize; + + compress(); + + compSize = save.compressedSize; + compBuffer = saveData; + + if (DSSaveFileManager::instance()->getBytesFree() >= getRamUsage()) { + + DSSaveFileManager::instance()->addBytesFree(-getRamUsage()); + + // Write header + for (int t = 0; t < sizeof(save); t++) { + while (*(address + t) != ((char *) (&save))[t]) { + *(address + t) = ((char *) (&save))[t]; + } + } + + // Write compressed buffer + for (int t = sizeof(save); t < (int) sizeof(save) + compSize; t++) { + while (*(address + t) != compBuffer[t - sizeof(save)]) { + *(address + t) = compBuffer[t - sizeof(save)]; + } + } + + failed = false; + } else { + failed = true; + } + + + return failed? 0: compSize + sizeof(save); + +} + +void DSSaveFile::reset() { + ptr = 0; +} + +uint32 DSSaveFile::read(void *buf, uint32 size) { + if (ptr + size > save.size) { + size = save.size - ptr; + if (size < 0) size = 0; + } + memcpy(buf, saveData + ptr, size); +// consolePrintf("byte: %d ", ((u8 *) (buf))[0]); + + ptr += size; + return size; +} + +uint32 DSSaveFile::pos() const { + return ptr; +} + +uint32 DSSaveFile::size() const { + return save.size; +} + +void DSSaveFile::seek(int32 pos, int whence) { + switch (whence) { + case SEEK_SET: { + ptr = pos; + break; + } + case SEEK_CUR: { + ptr += pos; + break; + } + case SEEK_END: { + ptr = save.size - pos; + break; + } + } +} + +bool DSSaveFile::eos() const { + return ptr >= (int) save.size; +} + +void DSSaveFile::skip(uint32 bytes) { + ptr = ptr + bytes; + if (ptr > (int) save.size) ptr = save.size; +} + +uint32 DSSaveFile::write(const void *buf, uint32 size) { + + if (ptr + size > DS_MAX_SAVE_SIZE) { + size = DS_MAX_SAVE_SIZE - ptr; + } + + memcpy(saveData + ptr, buf, size); + ptr += size; + save.size += size; + return size; +} + +bool DSSaveFile::matches(char* prefix, int num) { + char str[16]; + if (isValid()) { + sprintf(str, "%s%02d", prefix, num); + if (!strcmp(str, save.name)) { + return true; + } else { + return false; + } + } else { + return false; + } +} + +bool DSSaveFile::matches(char* filename) { + if (isValid()) { + return !strcmp(save.name, filename); + } else { + return false; + } +} + +void DSSaveFile::setName(char *name) { + save.isValid = true; + save.magic = 0xBEEFCAFE; + ownsData = true; + save.size = 0; + save.compressedSize = 0; + saveData = new unsigned char[DS_MAX_SAVE_SIZE]; + strcpy(save.name, name); + + if ((strstr(name, ".s99")) || (strstr(name, ".c"))) { + isTempFile = true; + } else { + isTempFile = false; + } +} + +void DSSaveFile::clearData() { + save.size = 0; + + if (saveCompressed) { + if (ownsData) { + delete saveData; + DSSaveFileManager::instance()->addBytesFree(getRamUsage()); + } + saveData = new unsigned char[DS_MAX_SAVE_SIZE]; + saveCompressed = false; + ownsData = true; + } + +} + +void DSSaveFile::deleteFile() { + if (isValid()) { + if (ownsData) { + DSSaveFileManager::instance()->addBytesFree(getRamUsage()); + delete saveData; + saveData = NULL; + } + ptr = 0; + saveCompressed = false; + save.isValid = false; + ownsData = false; + isOpenFlag = true; + } +} + +DSSaveFileManager::DSSaveFileManager() { + instancePtr = this; + + *((u16 *) (0x4000204)) |= 0x3; + swiWaitForVBlank(); + + loadAllFromSRAM(); +} + +DSSaveFileManager::~DSSaveFileManager() { + instancePtr = NULL; +} + +void DSSaveFileManager::loadAllFromSRAM() { + int addr = 1; + + for (int r = 0; r < 8; r++) { + gbaSave[r].deleteFile(); + } + + sramBytesFree = 65533; + + // Try to find saves in save RAM + for (int r = 0; r < 8; r++) { + if (gbaSave[r].loadFromSaveRAM(CART_RAM + addr)) { + addr += gbaSave[r].getRamUsage(); + sramBytesFree -= gbaSave[r].getRamUsage(); + } + } + +} + +void DSSaveFileManager::formatSram() { + for (int r = 0; r < 65533; r++) { + *(CART_RAM + r) = 0; + } + + loadAllFromSRAM(); +} + +void DSSaveFileManager::listFiles() { + for (int r = 0; r < 8; r++) { + if (gbaSave[r].isValid()) { + consolePrintf("'%s': %d bytes\n", gbaSave[r].getName(), gbaSave[r].getRamUsage()); + } + } + consolePrintf("SRAM free: %d bytes\n", getBytesFree()); +} + +DSSaveFileManager* DSSaveFileManager::instancePtr = NULL; + +Common::SaveFile *DSSaveFileManager::openSavefile(const char* filename, bool saveOrLoad) { + for (int r = 0; r < 8; r++) { + if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) filename))) { +// consolePrintf("Matched save %d (%d)\n", r, gbaSave[r].getSize()); + gbaSave[r].reset(); + //consolePrintf("reset "); + if (saveOrLoad) gbaSave[r].clearData(); +// consolePrintf("cleared "); + return gbaSave[r].clone(); + } + } + + if (saveOrLoad) { + return makeSaveFile(filename, saveOrLoad); + } else { + return NULL; + } +} + + + +DSSaveFile* DSSaveFile::clone() { +// consolePrintf("Clone %s %d\n", save.name, save.size); + return new DSSaveFile(&save, saveCompressed, saveData); +} + +void DSSaveFileManager::deleteFile(char* name) { +// consolePrintf("Deleting %s", name); + for (int r = 0; r < 8; r++) { + if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) name))) { + gbaSave[r].deleteFile(); + } + } + flushToSaveRAM(); +} + +void DSSaveFileManager::listSavefiles(const char *prefix, bool *marks, int num) { + memset(marks, false, num*sizeof(bool)); + + for (int saveNum = 0; saveNum < num; saveNum++) { + for (int r = 0; r < 8; r++) { + if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) prefix, saveNum))) { + marks[saveNum] = true; + } + } + } + +} + +Common::SaveFile *DSSaveFileManager::makeSaveFile(const char *filename, bool saveOrLoad) { + + // Find a free save slot + int r = 0; + + while ((r < 8) && (gbaSave[r].isValid())) { + r++; + } + + if ((r == 8) && (gbaSave[r].isValid())) { + // No more saves + return NULL; + } else { + // Allocate this save +// consolePrintf("Allocated save %d\n", r); + gbaSave[r].setName((char *) filename); + gbaSave[r].reset(); + return gbaSave[r].clone(); + } +} + +void DSSaveFileManager::flushToSaveRAM() { + int cartAddr = 1; + int s; + + *((u16 *) (0x4000204)) |= 0x3; + + swiWaitForVBlank(); + + int size = 0; + for (int r = 0; (r < 8); r++) { + if (gbaSave[r].isValid()) { + gbaSave[r].compress(); + if (!gbaSave[r].isTemp()) size += gbaSave[r].getRamUsage(); + } + } + + if (size <= 65533) { + + for (int r = 0; r < 65533; r++) { + *(CART_RAM + r) = 0; + } + + sramBytesFree = 65533; + + for (int r = 0; (r < 8); r++) { + if (gbaSave[r].isValid() && (!gbaSave[r].isTemp())) { + + cartAddr += s = gbaSave[r].saveToSaveRAM(CART_RAM + cartAddr); + + /* if (s == 0) { + consolePrintf("WARNING: Save didn't fit in cart RAM and has been lost!! Delete files and save again.", gbaSave[r].getName()); + failed = true; + }*/ + } + } + } else { + + consolePrintf("WARNING: Save didn't fit in cart RAM and has been lost!! Delete files and save again."); + loadAllFromSRAM(); + + } +// consolePrintf("SRAM free: %d bytes\n", getBytesFree()); +} +/* Ramsave + * Copyright (C) 2002-2004 Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + // Save in order 1,2,3,4,larger 2,5 +#include "stdafx.h" +#include "system.h" +#include "ramsave.h" +#include "nds.h" +#include "compressor/lz.h" + +#define CART_RAM ((vu8 *) (0x0A000000)) + +DSSaveFile::DSSaveFile() { + ptr = 0; + saveCompressed = false; + save.isValid = false; + ownsData = false; + isOpenFlag = true; + isTempFile = false; +} + +DSSaveFile::DSSaveFile(SCUMMSave* s, bool compressed, u8* data) { + save = *s; + saveData = data; + ptr = 0; + saveCompressed = compressed; + isOpenFlag = true; + + if (saveCompressed) { + u8* uncompressed = new unsigned char[save.size]; + if (!uncompressed) consolePrintf("Out of memory allocating %d!\n", save.size); + LZ_Uncompress(saveData, uncompressed, save.compressedSize); + saveData = uncompressed; + ownsData = true; + saveCompressed = false; +// consolePrintf("Decompressed. name=%s size=%d (%d)", save.name, save.size, save.compressedSize); + + } else { + ownsData = false; + origHeader = s; + } + + if (save.magic == (int) 0xBEEFCAFE) { + save.isValid = true; + } else { + save.isValid = false; + } + + isTempFile = false; +} + +DSSaveFile::~DSSaveFile() { + if (!ownsData) { + *origHeader = save; + DSSaveFileManager::instance()->flushToSaveRAM(); + } + if (ownsData) { + delete saveData; + } +} + +bool DSSaveFile::loadFromSaveRAM(vu8* address) { + + SCUMMSave newSave; + + for (int t = 0; t < (int) sizeof(newSave); t++) { + ((char *) (&newSave))[t] = *(address + t); + } + + + if (newSave.magic == 0xBEEFCAFE) { + newSave.isValid = true; + + *((u16 *) (0x4000204)) |= 0x3; + + saveData = new unsigned char[newSave.compressedSize]; + + for (int t = 0; t < (int) newSave.compressedSize; t++) { + ((char *) (saveData))[t] = *(address + t + sizeof(newSave)); + } + + if (ownsData) delete this->saveData; + save = newSave; + saveCompressed = true; + this->saveData = saveData; + ownsData = true; + ptr = 0; + return true; + } + + return false; +} + +void DSSaveFile::compress() { + if (!saveCompressed) { + unsigned char* compBuffer = new unsigned char[(save.size * 110) / 100]; + int compSize = LZ_Compress((u8 *) saveData, compBuffer, save.size); + save.compressedSize = compSize; + + + + delete saveData; + + // Make the save smaller + saveData = (u8 *) realloc(compBuffer, save.compressedSize); + saveCompressed = true; + } +} + +int DSSaveFile::saveToSaveRAM(vu8* address) { + + unsigned char* compBuffer; + bool failed; + + + int compSize; + + compress(); + + compSize = save.compressedSize; + compBuffer = saveData; + + if (DSSaveFileManager::instance()->getBytesFree() >= getRamUsage()) { + + DSSaveFileManager::instance()->addBytesFree(-getRamUsage()); + + // Write header + for (int t = 0; t < sizeof(save); t++) { + while (*(address + t) != ((char *) (&save))[t]) { + *(address + t) = ((char *) (&save))[t]; + } + } + + // Write compressed buffer + for (int t = sizeof(save); t < (int) sizeof(save) + compSize; t++) { + while (*(address + t) != compBuffer[t - sizeof(save)]) { + *(address + t) = compBuffer[t - sizeof(save)]; + } + } + + failed = false; + } else { + failed = true; + } + + + return failed? 0: compSize + sizeof(save); + +} + +void DSSaveFile::reset() { + ptr = 0; +} + +uint32 DSSaveFile::read(void *buf, uint32 size) { + if (ptr + size > save.size) { + size = save.size - ptr; + if (size < 0) size = 0; + } + memcpy(buf, saveData + ptr, size); +// consolePrintf("byte: %d ", ((u8 *) (buf))[0]); + + ptr += size; + return size; +} + +uint32 DSSaveFile::pos() const { + return ptr; +} + +uint32 DSSaveFile::size() const { + return save.size; +} + +void DSSaveFile::seek(int32 pos, int whence) { + switch (whence) { + case SEEK_SET: { + ptr = pos; + break; + } + case SEEK_CUR: { + ptr += pos; + break; + } + case SEEK_END: { + ptr = save.size - pos; + break; + } + } +} + +bool DSSaveFile::eos() const { + return ptr >= (int) save.size; +} + +void DSSaveFile::skip(uint32 bytes) { + ptr = ptr + bytes; + if (ptr > (int) save.size) ptr = save.size; +} + +uint32 DSSaveFile::write(const void *buf, uint32 size) { + + if (ptr + size > DS_MAX_SAVE_SIZE) { + size = DS_MAX_SAVE_SIZE - ptr; + } + + memcpy(saveData + ptr, buf, size); + ptr += size; + save.size += size; + return size; +} + +bool DSSaveFile::matches(char* prefix, int num) { + char str[16]; + if (isValid()) { + sprintf(str, "%s%02d", prefix, num); + if (!strcmp(str, save.name)) { + return true; + } else { + return false; + } + } else { + return false; + } +} + +bool DSSaveFile::matches(char* filename) { + if (isValid()) { + return !strcmp(save.name, filename); + } else { + return false; + } +} + +void DSSaveFile::setName(char *name) { + save.isValid = true; + save.magic = 0xBEEFCAFE; + ownsData = true; + save.size = 0; + save.compressedSize = 0; + saveData = new unsigned char[DS_MAX_SAVE_SIZE]; + strcpy(save.name, name); + + if ((strstr(name, ".s99")) || (strstr(name, ".c"))) { + isTempFile = true; + } else { + isTempFile = false; + } +} + +void DSSaveFile::clearData() { + save.size = 0; + + if (saveCompressed) { + if (ownsData) { + delete saveData; + DSSaveFileManager::instance()->addBytesFree(getRamUsage()); + } + saveData = new unsigned char[DS_MAX_SAVE_SIZE]; + saveCompressed = false; + ownsData = true; + } + +} + +void DSSaveFile::deleteFile() { + if (isValid()) { + if (ownsData) { + DSSaveFileManager::instance()->addBytesFree(getRamUsage()); + delete saveData; + saveData = NULL; + } + ptr = 0; + saveCompressed = false; + save.isValid = false; + ownsData = false; + isOpenFlag = true; + } +} + +DSSaveFileManager::DSSaveFileManager() { + instancePtr = this; + + *((u16 *) (0x4000204)) |= 0x3; + swiWaitForVBlank(); + + loadAllFromSRAM(); +} + +DSSaveFileManager::~DSSaveFileManager() { + instancePtr = NULL; +} + +void DSSaveFileManager::loadAllFromSRAM() { + int addr = 1; + + for (int r = 0; r < 8; r++) { + gbaSave[r].deleteFile(); + } + + sramBytesFree = 65533; + + // Try to find saves in save RAM + for (int r = 0; r < 8; r++) { + if (gbaSave[r].loadFromSaveRAM(CART_RAM + addr)) { + addr += gbaSave[r].getRamUsage(); + sramBytesFree -= gbaSave[r].getRamUsage(); + } + } + +} + +void DSSaveFileManager::formatSram() { + for (int r = 0; r < 65533; r++) { + *(CART_RAM + r) = 0; + } + + loadAllFromSRAM(); +} + +void DSSaveFileManager::listFiles() { + for (int r = 0; r < 8; r++) { + if (gbaSave[r].isValid()) { + consolePrintf("'%s': %d bytes\n", gbaSave[r].getName(), gbaSave[r].getRamUsage()); + } + } + consolePrintf("SRAM free: %d bytes\n", getBytesFree()); +} + +DSSaveFileManager* DSSaveFileManager::instancePtr = NULL; + +Common::SaveFile *DSSaveFileManager::openSavefile(const char* filename, bool saveOrLoad) { + for (int r = 0; r < 8; r++) { + if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) filename))) { +// consolePrintf("Matched save %d (%d)\n", r, gbaSave[r].getSize()); + gbaSave[r].reset(); + //consolePrintf("reset "); + if (saveOrLoad) gbaSave[r].clearData(); +// consolePrintf("cleared "); + return gbaSave[r].clone(); + } + } + + if (saveOrLoad) { + return makeSaveFile(filename, saveOrLoad); + } else { + return NULL; + } +} + + + +DSSaveFile* DSSaveFile::clone() { +// consolePrintf("Clone %s %d\n", save.name, save.size); + return new DSSaveFile(&save, saveCompressed, saveData); +} + +void DSSaveFileManager::deleteFile(char* name) { +// consolePrintf("Deleting %s", name); + for (int r = 0; r < 8; r++) { + if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) name))) { + gbaSave[r].deleteFile(); + } + } + flushToSaveRAM(); +} + +void DSSaveFileManager::listSavefiles(const char *prefix, bool *marks, int num) { + memset(marks, false, num*sizeof(bool)); + + for (int saveNum = 0; saveNum < num; saveNum++) { + for (int r = 0; r < 8; r++) { + if (gbaSave[r].isValid() && (gbaSave[r].matches((char *) prefix, saveNum))) { + marks[saveNum] = true; + } + } + } + +} + +Common::SaveFile *DSSaveFileManager::makeSaveFile(const char *filename, bool saveOrLoad) { + + // Find a free save slot + int r = 0; + + while ((r < 8) && (gbaSave[r].isValid())) { + r++; + } + + if ((r == 8) && (gbaSave[r].isValid())) { + // No more saves + return NULL; + } else { + // Allocate this save +// consolePrintf("Allocated save %d\n", r); + gbaSave[r].setName((char *) filename); + gbaSave[r].reset(); + return gbaSave[r].clone(); + } +} + +void DSSaveFileManager::flushToSaveRAM() { + int cartAddr = 1; + int s; + + *((u16 *) (0x4000204)) |= 0x3; + + swiWaitForVBlank(); + + int size = 0; + for (int r = 0; (r < 8); r++) { + if (gbaSave[r].isValid()) { + gbaSave[r].compress(); + if (!gbaSave[r].isTemp()) size += gbaSave[r].getRamUsage(); + } + } + + if (size <= 65533) { + + for (int r = 0; r < 65533; r++) { + *(CART_RAM + r) = 0; + } + + sramBytesFree = 65533; + + for (int r = 0; (r < 8); r++) { + if (gbaSave[r].isValid() && (!gbaSave[r].isTemp())) { + + cartAddr += s = gbaSave[r].saveToSaveRAM(CART_RAM + cartAddr); + + /* if (s == 0) { + consolePrintf("WARNING: Save didn't fit in cart RAM and has been lost!! Delete files and save again.", gbaSave[r].getName()); + failed = true; + }*/ + } + } + } else { + + consolePrintf("WARNING: Save didn't fit in cart RAM and has been lost!! Delete files and save again."); + loadAllFromSRAM(); + + } +// consolePrintf("SRAM free: %d bytes\n", getBytesFree()); +} diff --git a/backends/platform/ds/arm9/source/ramsave.h b/backends/platform/ds/arm9/source/ramsave.h new file mode 100644 index 0000000000..d7d029df78 --- /dev/null +++ b/backends/platform/ds/arm9/source/ramsave.h @@ -0,0 +1,280 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _RAMSAVE_H_ +#define _RAMSAVE_H_ + +#include "system.h" +#include "savefile.h" + +// SaveFileManager class + +#define DS_MAX_SAVE_SIZE 150000 + +class DSSaveFile : public Common::SaveFile { + int address; + int ptr; + bool ownsData; + bool saveCompressed; + + struct SCUMMSave { + u32 magic; + bool isValid; + bool pad; + char name[16]; + u32 size; + u32 compressedSize; + u16 pad2; + u32 reserved; + } __attribute__ ((packed)); + + SCUMMSave save; + u8* saveData; + SCUMMSave* origHeader; + bool isOpenFlag; + bool isTempFile; + +public: + DSSaveFile(); + DSSaveFile(SCUMMSave* s, bool saveCompressed, u8* data); + ~DSSaveFile(); + + void reset(); + + bool isOpen() const { return isOpenFlag; } + virtual bool eos() const; + virtual void skip(uint32 size); + + virtual uint32 pos() const; + virtual uint32 size() const; + virtual void seek(int32 pos, int whence); + + uint32 read(void *buf, uint32 size); + uint32 write(const void *buf, uint32 size); + + void setName(char *name); + char* getName() { return save.name; } + + bool isValid() { return save.isValid; } + bool isTemp() { return isTempFile; } + bool matches(char* prefix, int num); + bool matches(char* filename); + + void clearData(); + void compress(); + + int getRamUsage() { return sizeof(save) + save.compressedSize; } + char* getRamImage() { return (char *) &save; } + + int getSize() { return save.size; } + + DSSaveFile* clone(); + + bool loadFromSaveRAM(vu8* address); + int saveToSaveRAM(vu8* address); + + + void deleteFile(); + + void operator delete(void *p) { +// consolePrintf("Finished! size=%d\n", ((DSSaveFile *) (p))->save->size); + } + + + +}; + + + +class DSSaveFileManager : public Common::SaveFileManager { + + DSSaveFile gbaSave[8]; + static DSSaveFileManager* instancePtr; + int sramBytesFree; + +public: + DSSaveFileManager(); + ~DSSaveFileManager(); + + static DSSaveFileManager* instance() { return instancePtr; } + + Common::SaveFile *openSavefile(const char *filename, bool saveOrLoad); + + virtual Common::OutSaveFile* openForSaving(const char* filename) { return openSavefile(filename, true); } + virtual Common::InSaveFile* openForLoading(const char* filename) { return openSavefile(filename, false); } + + + void listSavefiles(const char *prefix, bool *marks, int num); + + void flushToSaveRAM(); + + void addBytesFree(int size) { sramBytesFree += size; } + int getBytesFree() { return sramBytesFree; } + + void deleteFile(char* name); + void listFiles(); + void formatSram(); + + void loadAllFromSRAM(); + +protected: + Common::SaveFile *makeSaveFile(const char *filename, bool saveOrLoad); +}; + +#endif +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _RAMSAVE_H_ +#define _RAMSAVE_H_ + +#include "system.h" +#include "savefile.h" + +// SaveFileManager class + +#define DS_MAX_SAVE_SIZE 150000 + +class DSSaveFile : public Common::SaveFile { + int address; + int ptr; + bool ownsData; + bool saveCompressed; + + struct SCUMMSave { + u32 magic; + bool isValid; + bool pad; + char name[16]; + u32 size; + u32 compressedSize; + u16 pad2; + u32 reserved; + } __attribute__ ((packed)); + + SCUMMSave save; + u8* saveData; + SCUMMSave* origHeader; + bool isOpenFlag; + bool isTempFile; + +public: + DSSaveFile(); + DSSaveFile(SCUMMSave* s, bool saveCompressed, u8* data); + ~DSSaveFile(); + + void reset(); + + bool isOpen() const { return isOpenFlag; } + virtual bool eos() const; + virtual void skip(uint32 size); + + virtual uint32 pos() const; + virtual uint32 size() const; + virtual void seek(int32 pos, int whence); + + uint32 read(void *buf, uint32 size); + uint32 write(const void *buf, uint32 size); + + void setName(char *name); + char* getName() { return save.name; } + + bool isValid() { return save.isValid; } + bool isTemp() { return isTempFile; } + bool matches(char* prefix, int num); + bool matches(char* filename); + + void clearData(); + void compress(); + + int getRamUsage() { return sizeof(save) + save.compressedSize; } + char* getRamImage() { return (char *) &save; } + + int getSize() { return save.size; } + + DSSaveFile* clone(); + + bool loadFromSaveRAM(vu8* address); + int saveToSaveRAM(vu8* address); + + + void deleteFile(); + + void operator delete(void *p) { +// consolePrintf("Finished! size=%d\n", ((DSSaveFile *) (p))->save->size); + } + + + +}; + + + +class DSSaveFileManager : public Common::SaveFileManager { + + DSSaveFile gbaSave[8]; + static DSSaveFileManager* instancePtr; + int sramBytesFree; + +public: + DSSaveFileManager(); + ~DSSaveFileManager(); + + static DSSaveFileManager* instance() { return instancePtr; } + + Common::SaveFile *openSavefile(const char *filename, bool saveOrLoad); + + virtual Common::OutSaveFile* openForSaving(const char* filename) { return openSavefile(filename, true); } + virtual Common::InSaveFile* openForLoading(const char* filename) { return openSavefile(filename, false); } + + + void listSavefiles(const char *prefix, bool *marks, int num); + + void flushToSaveRAM(); + + void addBytesFree(int size) { sramBytesFree += size; } + int getBytesFree() { return sramBytesFree; } + + void deleteFile(char* name); + void listFiles(); + void formatSram(); + + void loadAllFromSRAM(); + +protected: + Common::SaveFile *makeSaveFile(const char *filename, bool saveOrLoad); +}; + +#endif diff --git a/backends/platform/ds/arm9/source/scummconsole.c b/backends/platform/ds/arm9/source/scummconsole.c new file mode 100644 index 0000000000..7efa43e24c --- /dev/null +++ b/backends/platform/ds/arm9/source/scummconsole.c @@ -0,0 +1,1122 @@ +////////////////////////////////////////////////////////////////////// +// +// console.cpp -- provides basic print functionality +// +// version 0.1, February 14, 2005 +// +// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto) +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any +// damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any +// purpose, including commercial applications, and to alter it and +// redistribute it freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you +// must not claim that you wrote the original software. If you use +// this software in a product, an acknowledgment in the product +// documentation would be appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and +// must not be misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source +// distribution. +// +// Changelog: +// 0.1: First version +// 0.2: Fixed sprite mapping bug. 1D mapping should work now. +// Changed some register defines for consistency. +// +////////////////////////////////////////////////////////////////////// + + +#include +#include "scummconsole.h" + +#include + +#include + +///////////////////////////////////////// +//global console variables + +#define CONSOLE_WIDTH 32 +#define CONSOLE_HEIGHT 24 +#define TAB_SIZE 3 + +//map to print to +u16* fontMap; + +//location of cursor +u8 row, col; + +//font may not start on a character base boundry +u16 fontOffset; + +//the first character in the set (0 if you have a full set) +u16 fontStart; + +//the 16-color palette to use +u16 fontPal; + + + + +/////////////////////////////////////////////////////////// +//consoleInit +// param: +// font: 16 color font +// charBase: the location the font data will be loaded to +// numCharacters: count of characters in the font +// charStart: The ascii number of the first character in the font set +// if you have a full set this will be zero +// map: pointer to the map you will be printing to. +// pal: specifies the 16 color palette to use, if > 15 it will change all non-zero +// entries in the font to use palette index 255 +void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth) +{ + int i; + + row = col = 0; + + fontStart = charStart; + + fontOffset = 0; + + fontMap = map; + + if(bitDepth == 16) + { + if(pal < 16) + { + fontPal = pal << 12; + + for (i = 0; i < numCharacters * 16; i++) + charBase[i] = font[i]; + } + else + { + fontPal = 15 << 12; + + for (i = 0; i < numCharacters * 16; i++) + { + u16 temp = 0; + + if(font[i] & 0xF) + temp |= 0xF; + if(font[i] & 0xF0) + temp |= 0xF0; + if(font[i] & 0xF00) + temp |= 0xF00; + if(font[i] & 0xF000) + temp |= 0xF000; + + charBase[i] = temp; + } + } + }//end if bitdepth + else + { + fontPal = 0; + for(i = 0; i < numCharacters * 16; i++) + { + u32 temp = 0; + + if(font[i] & 0xF) + temp = 255; + if(font[i] & 0xF0) + temp |= 255 << 8; + if(font[i] & 0xF00) + temp |= 255 << 16; + if(font[i] & 0xF000) + temp |= 255 << 24; + + ((u32*)charBase)[i] = temp; + + } + } +} + +void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth) +{ + consoleInit((u16 *) default_font_bin, charBase, 256, 0, map, CONSOLE_USE_COLOR255, bitDepth); +} + +void consolePrintSet(int x, int y) +{ + if(y < CONSOLE_HEIGHT) + row = y; + else + row = CONSOLE_HEIGHT - 1; + + if(x < CONSOLE_WIDTH) + col = x; + else + col = CONSOLE_WIDTH - 1; +} + +void consolePrintChar(char c) +{ + int i; + + if(col >= CONSOLE_WIDTH) + { + col = 0; + + row++; + } + + if(row >= CONSOLE_HEIGHT) + { + row--; + + for(i = CONSOLE_WIDTH; i < CONSOLE_HEIGHT * CONSOLE_WIDTH; i++) + fontMap[i - CONSOLE_WIDTH] = fontMap[i]; + for(i = 0; i < CONSOLE_WIDTH; i++) + fontMap[i + (CONSOLE_HEIGHT-1)*CONSOLE_WIDTH] = fontPal | (u16)(' ' + fontOffset - fontStart); + + + } + + switch(c) + { + + case 10: + case 11: + case 12: + case 13: + row++; + col = 0; + break; + case 9: + col += TAB_SIZE; + break; + default: + fontMap[col + row * CONSOLE_WIDTH] = fontPal | (u16)(c + fontOffset - fontStart); + col++; + break; + + } + + +} + + +void printX(int w, unsigned d) +{ + int loop = 0; + int i = 0; + + char buf[20] = {0}; + + + while(d > 0) + { + buf[loop++] = d & 0xF; + d = d>>4; + } + + for (i = 7; i >= 0; i--) + { + if(buf[i] || i < loop) + { + if(buf[i] < 10) + consolePrintChar(buf[i] + '0'); + else + consolePrintChar(buf[i] + 'A' - 10); + } + else if(i < w) + consolePrintChar(' '); + } +} + +void printx(int w, unsigned int d) +{ + int loop = 0; + int i = 0; + + char buf[20] = {0}; + + while(d > 0) + { + buf[loop++] = d & 0xF; + d = d>>4; + } + + for (i = 7; i >= 0; i--) + { + if(buf[i] || i < loop) + { + if(buf[i] < 10) + consolePrintChar(buf[i] + '0'); + else + consolePrintChar(buf[i] + 'a' - 10); + } + else if(i < w) + consolePrintChar(' '); + } +} + +void printInt(int w, int d) +{ + int loop = 0; + int i = 0; + + char buf[20] = {0}; + + if(d < 0) + { + consolePrintChar('-'); + d *= -1; + } + + if (d == 0) + buf[loop++] = 0; + else while (d > 0) + { + buf[loop++] = d % 10; + d /= 10; + } + + for (i = 7; i >= 0; i--) + { + if(buf[i] || i < loop) + consolePrintChar(buf[i] + '0'); + else if(i < w) + consolePrintChar(' '); + } +} + +void printBin(int w, int d) +{ + int i; + int first = 0; + for (i = 31; i >= 0; i--) + { + if(d & BIT(i)) + { + first = 1; + consolePrintChar('1'); + } + else if (first || i == 0) + consolePrintChar('0'); + else if (i < w) + consolePrintChar(' '); + } +} + +void print0X(int w, unsigned d) +{ + int loop = 0; + int i = 0; + + char buf[] = {0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday + + + while(d > 0) + { + buf[loop++] = d & 0xF; + d = d>>4; + } + + for (i = 7; i >= 0; i--) + { + if(buf[i] || i < w || i < loop) + { + if(buf[i] < 10) + consolePrintChar(buf[i] + '0'); + else + consolePrintChar(buf[i] + 'A' - 10); + } + } +} + +void print0x(int w, unsigned int d) +{ + int loop = 0; + int i = 0; + + char buf[] = {0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday + + + while(d > 0) + { + buf[loop++] = d & 0xF; + d = d>>4; + } + + for (i = 7; i >= 0; i--) + { + if(buf[i] || i < w || i < loop) + { + if(buf[i] < 10) + consolePrintChar(buf[i] + '0'); + else + consolePrintChar(buf[i] + 'a' - 10); + } + } +} + +void print0Int(int w, int d) +{ + int loop = 0; + int i = 0; + + char buf[] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday + + if(d < 0) + { + consolePrintChar('-'); + d *= -1; + } + + while(d > 0) + { + buf[loop++] = d % 10; + d /= 10; + } + + for (i = 15; i >= 0; i--) + if(buf[i] || i < w || i < loop) + consolePrintChar(buf[i] + '0'); + +} + +void print0Bin(int w, int d) +{ + int i; + int first = 0; + for (i = 31; i >= 0; i--) + { + if(d & BIT(i)) + { + first = 1; + consolePrintChar('1'); + } + else if (first || i == 0) + consolePrintChar('0'); + else if (i < w) + consolePrintChar('0'); + } +} + +void print(const char* s) +{ + for(; *s; s++) consolePrintChar(*s); +} + +void printF(int w, float f) +{ + unsigned int* t = (unsigned int*)&f; + unsigned int fraction = (*t) & 0x007FFFFF; + int exp = ((*t) >> 23) & 0xFF; + + if(*t & BIT(31)) + consolePrintChar('-'); + + + print0Bin(32, fraction); + + printInt(1, fraction); + consolePrintChar('e'); + printInt(1, exp - 127); + + /* + if(exp == 0 && fraction == 0) + { + printInt(1,0); + } + else if(exp == 0xFF && fraction == 0) + { + print("Inifinite"); + } + else + { + printInt(w,fraction); + consolePrintChar('e'); + printInt(1,exp - 127); + } + */ +} + +void consolePrintf(const char* s, ...) +{ + int w = 1, z = 0; + + va_list argp; + + va_start(argp, s); + + + while(*s) + { + w = 1; + z = 0; + + switch(*s) + { + case '%': + s++; + if(*s == '0') + { + z = 1; + s++; + } + if(*s > '0' && *s <= '9') + { + w = *s - '0'; + s++; + } + switch (*s) + { + case 'i': + case 'I': + case 'd': + case 'D': + if(z)print0Int(w, va_arg(argp, int)); + else printInt(w, va_arg(argp, int)); + s++; + break; + case 'X': + if(z)print0X(w, va_arg(argp, int)); + else printX(w, va_arg(argp, int)); + s++; + break; + + case 'x': + if(z)print0x(w, va_arg(argp, int)); + else printx(w, va_arg(argp, int)); + s++; + break; + + case 'b': + case 'B': + if(z)print0Bin(w, va_arg(argp, int)); + else printBin(w, va_arg(argp, int)); + s++; + break; + case 'f': + case 'F': + printF(w,va_arg(argp, double)); + s++; + break; + case 's': + case 'S': + print(va_arg(argp, char*)); + s++; + break; + default: + consolePrintChar('%'); + break; + } + default: + consolePrintChar(*s); + break; + } + + s++; + } + va_end(argp); +} + +void consolePutString(int x, int y, char* s) +{ + consolePrintSet(x, y); + consolePrintf(s); +} + +void consolePutInt(int x, int y, int d) +{ + consolePrintSet(x,y); + printInt(1,d); +} + +void consolePutX(int x, int y, int d) +{ + consolePrintSet(x, y); + printX(1,d); +} + +void consolePutChar(int x, int y, char c) +{ + consolePrintSet(x, y); + consolePrintChar(c); +} + +void consolePutBin(int x, int y, int b) +{ + consolePrintSet(x, y); + printBin(1,b); +} +void consoleClear(void) +{ + int i = 0; + consolePrintSet(0,0); + + while(i++ < CONSOLE_HEIGHT * CONSOLE_WIDTH) + consolePrintChar(' '); + + consolePrintSet(0,0); +} +////////////////////////////////////////////////////////////////////// +// +// console.cpp -- provides basic print functionality +// +// version 0.1, February 14, 2005 +// +// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto) +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any +// damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any +// purpose, including commercial applications, and to alter it and +// redistribute it freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you +// must not claim that you wrote the original software. If you use +// this software in a product, an acknowledgment in the product +// documentation would be appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and +// must not be misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source +// distribution. +// +// Changelog: +// 0.1: First version +// 0.2: Fixed sprite mapping bug. 1D mapping should work now. +// Changed some register defines for consistency. +// +////////////////////////////////////////////////////////////////////// + + +#include +#include "scummconsole.h" + +#include + +#include + +///////////////////////////////////////// +//global console variables + +#define CONSOLE_WIDTH 32 +#define CONSOLE_HEIGHT 24 +#define TAB_SIZE 3 + +//map to print to +u16* fontMap; + +//location of cursor +u8 row, col; + +//font may not start on a character base boundry +u16 fontOffset; + +//the first character in the set (0 if you have a full set) +u16 fontStart; + +//the 16-color palette to use +u16 fontPal; + + + + +/////////////////////////////////////////////////////////// +//consoleInit +// param: +// font: 16 color font +// charBase: the location the font data will be loaded to +// numCharacters: count of characters in the font +// charStart: The ascii number of the first character in the font set +// if you have a full set this will be zero +// map: pointer to the map you will be printing to. +// pal: specifies the 16 color palette to use, if > 15 it will change all non-zero +// entries in the font to use palette index 255 +void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth) +{ + int i; + + row = col = 0; + + fontStart = charStart; + + fontOffset = 0; + + fontMap = map; + + if(bitDepth == 16) + { + if(pal < 16) + { + fontPal = pal << 12; + + for (i = 0; i < numCharacters * 16; i++) + charBase[i] = font[i]; + } + else + { + fontPal = 15 << 12; + + for (i = 0; i < numCharacters * 16; i++) + { + u16 temp = 0; + + if(font[i] & 0xF) + temp |= 0xF; + if(font[i] & 0xF0) + temp |= 0xF0; + if(font[i] & 0xF00) + temp |= 0xF00; + if(font[i] & 0xF000) + temp |= 0xF000; + + charBase[i] = temp; + } + } + }//end if bitdepth + else + { + fontPal = 0; + for(i = 0; i < numCharacters * 16; i++) + { + u32 temp = 0; + + if(font[i] & 0xF) + temp = 255; + if(font[i] & 0xF0) + temp |= 255 << 8; + if(font[i] & 0xF00) + temp |= 255 << 16; + if(font[i] & 0xF000) + temp |= 255 << 24; + + ((u32*)charBase)[i] = temp; + + } + } +} + +void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth) +{ + consoleInit((u16 *) default_font_bin, charBase, 256, 0, map, CONSOLE_USE_COLOR255, bitDepth); +} + +void consolePrintSet(int x, int y) +{ + if(y < CONSOLE_HEIGHT) + row = y; + else + row = CONSOLE_HEIGHT - 1; + + if(x < CONSOLE_WIDTH) + col = x; + else + col = CONSOLE_WIDTH - 1; +} + +void consolePrintChar(char c) +{ + int i; + + if(col >= CONSOLE_WIDTH) + { + col = 0; + + row++; + } + + if(row >= CONSOLE_HEIGHT) + { + row--; + + for(i = CONSOLE_WIDTH; i < CONSOLE_HEIGHT * CONSOLE_WIDTH; i++) + fontMap[i - CONSOLE_WIDTH] = fontMap[i]; + for(i = 0; i < CONSOLE_WIDTH; i++) + fontMap[i + (CONSOLE_HEIGHT-1)*CONSOLE_WIDTH] = fontPal | (u16)(' ' + fontOffset - fontStart); + + + } + + switch(c) + { + + case 10: + case 11: + case 12: + case 13: + row++; + col = 0; + break; + case 9: + col += TAB_SIZE; + break; + default: + fontMap[col + row * CONSOLE_WIDTH] = fontPal | (u16)(c + fontOffset - fontStart); + col++; + break; + + } + + +} + + +void printX(int w, unsigned d) +{ + int loop = 0; + int i = 0; + + char buf[20] = {0}; + + + while(d > 0) + { + buf[loop++] = d & 0xF; + d = d>>4; + } + + for (i = 7; i >= 0; i--) + { + if(buf[i] || i < loop) + { + if(buf[i] < 10) + consolePrintChar(buf[i] + '0'); + else + consolePrintChar(buf[i] + 'A' - 10); + } + else if(i < w) + consolePrintChar(' '); + } +} + +void printx(int w, unsigned int d) +{ + int loop = 0; + int i = 0; + + char buf[20] = {0}; + + while(d > 0) + { + buf[loop++] = d & 0xF; + d = d>>4; + } + + for (i = 7; i >= 0; i--) + { + if(buf[i] || i < loop) + { + if(buf[i] < 10) + consolePrintChar(buf[i] + '0'); + else + consolePrintChar(buf[i] + 'a' - 10); + } + else if(i < w) + consolePrintChar(' '); + } +} + +void printInt(int w, int d) +{ + int loop = 0; + int i = 0; + + char buf[20] = {0}; + + if(d < 0) + { + consolePrintChar('-'); + d *= -1; + } + + if (d == 0) + buf[loop++] = 0; + else while (d > 0) + { + buf[loop++] = d % 10; + d /= 10; + } + + for (i = 7; i >= 0; i--) + { + if(buf[i] || i < loop) + consolePrintChar(buf[i] + '0'); + else if(i < w) + consolePrintChar(' '); + } +} + +void printBin(int w, int d) +{ + int i; + int first = 0; + for (i = 31; i >= 0; i--) + { + if(d & BIT(i)) + { + first = 1; + consolePrintChar('1'); + } + else if (first || i == 0) + consolePrintChar('0'); + else if (i < w) + consolePrintChar(' '); + } +} + +void print0X(int w, unsigned d) +{ + int loop = 0; + int i = 0; + + char buf[] = {0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday + + + while(d > 0) + { + buf[loop++] = d & 0xF; + d = d>>4; + } + + for (i = 7; i >= 0; i--) + { + if(buf[i] || i < w || i < loop) + { + if(buf[i] < 10) + consolePrintChar(buf[i] + '0'); + else + consolePrintChar(buf[i] + 'A' - 10); + } + } +} + +void print0x(int w, unsigned int d) +{ + int loop = 0; + int i = 0; + + char buf[] = {0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday + + + while(d > 0) + { + buf[loop++] = d & 0xF; + d = d>>4; + } + + for (i = 7; i >= 0; i--) + { + if(buf[i] || i < w || i < loop) + { + if(buf[i] < 10) + consolePrintChar(buf[i] + '0'); + else + consolePrintChar(buf[i] + 'a' - 10); + } + } +} + +void print0Int(int w, int d) +{ + int loop = 0; + int i = 0; + + char buf[] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; //set to zero cause I may add formatted output someday + + if(d < 0) + { + consolePrintChar('-'); + d *= -1; + } + + while(d > 0) + { + buf[loop++] = d % 10; + d /= 10; + } + + for (i = 15; i >= 0; i--) + if(buf[i] || i < w || i < loop) + consolePrintChar(buf[i] + '0'); + +} + +void print0Bin(int w, int d) +{ + int i; + int first = 0; + for (i = 31; i >= 0; i--) + { + if(d & BIT(i)) + { + first = 1; + consolePrintChar('1'); + } + else if (first || i == 0) + consolePrintChar('0'); + else if (i < w) + consolePrintChar('0'); + } +} + +void print(const char* s) +{ + for(; *s; s++) consolePrintChar(*s); +} + +void printF(int w, float f) +{ + unsigned int* t = (unsigned int*)&f; + unsigned int fraction = (*t) & 0x007FFFFF; + int exp = ((*t) >> 23) & 0xFF; + + if(*t & BIT(31)) + consolePrintChar('-'); + + + print0Bin(32, fraction); + + printInt(1, fraction); + consolePrintChar('e'); + printInt(1, exp - 127); + + /* + if(exp == 0 && fraction == 0) + { + printInt(1,0); + } + else if(exp == 0xFF && fraction == 0) + { + print("Inifinite"); + } + else + { + printInt(w,fraction); + consolePrintChar('e'); + printInt(1,exp - 127); + } + */ +} + +void consolePrintf(const char* s, ...) +{ + int w = 1, z = 0; + + va_list argp; + + va_start(argp, s); + + + while(*s) + { + w = 1; + z = 0; + + switch(*s) + { + case '%': + s++; + if(*s == '0') + { + z = 1; + s++; + } + if(*s > '0' && *s <= '9') + { + w = *s - '0'; + s++; + } + switch (*s) + { + case 'i': + case 'I': + case 'd': + case 'D': + if(z)print0Int(w, va_arg(argp, int)); + else printInt(w, va_arg(argp, int)); + s++; + break; + case 'X': + if(z)print0X(w, va_arg(argp, int)); + else printX(w, va_arg(argp, int)); + s++; + break; + + case 'x': + if(z)print0x(w, va_arg(argp, int)); + else printx(w, va_arg(argp, int)); + s++; + break; + + case 'b': + case 'B': + if(z)print0Bin(w, va_arg(argp, int)); + else printBin(w, va_arg(argp, int)); + s++; + break; + case 'f': + case 'F': + printF(w,va_arg(argp, double)); + s++; + break; + case 's': + case 'S': + print(va_arg(argp, char*)); + s++; + break; + default: + consolePrintChar('%'); + break; + } + default: + consolePrintChar(*s); + break; + } + + s++; + } + va_end(argp); +} + +void consolePutString(int x, int y, char* s) +{ + consolePrintSet(x, y); + consolePrintf(s); +} + +void consolePutInt(int x, int y, int d) +{ + consolePrintSet(x,y); + printInt(1,d); +} + +void consolePutX(int x, int y, int d) +{ + consolePrintSet(x, y); + printX(1,d); +} + +void consolePutChar(int x, int y, char c) +{ + consolePrintSet(x, y); + consolePrintChar(c); +} + +void consolePutBin(int x, int y, int b) +{ + consolePrintSet(x, y); + printBin(1,b); +} +void consoleClear(void) +{ + int i = 0; + consolePrintSet(0,0); + + while(i++ < CONSOLE_HEIGHT * CONSOLE_WIDTH) + consolePrintChar(' '); + + consolePrintSet(0,0); +} diff --git a/backends/platform/ds/arm9/source/scummconsole.h b/backends/platform/ds/arm9/source/scummconsole.h new file mode 100644 index 0000000000..6b9a677cc7 --- /dev/null +++ b/backends/platform/ds/arm9/source/scummconsole.h @@ -0,0 +1,124 @@ +////////////////////////////////////////////////////////////////////// +// +// consol.h --provides basic consol type print functionality +// +// version 0.1, February 14, 2005 +// +// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto) +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any +// damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any +// purpose, including commercial applications, and to alter it and +// redistribute it freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you +// must not claim that you wrote the original software. If you use +// this software in a product, an acknowledgment in the product +// documentation would be appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and +// must not be misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source +// distribution. +// +// Changelog: +// 0.1: First version +// 0.2: Fixed sprite mapping bug. 1D mapping should work now. +// Changed some register defines for consistency. +// +////////////////////////////////////////////////////////////////////// +#ifndef CONSOLE_H2 +#define CONSOLE_H2 + +#define CONSOLE_USE_COLOR255 16 + +#ifdef __cplusplus +extern "C" { +#endif + +void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth); +void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth); + +void consolePrintf(const char* s, ...); + +void consolePrintSet(int x, int y); + +void consolePrintChar(char c); + +void consolePutString(int x, int y, char* s); +void consolePutInt(int x, int y, int d); +void consolePutX(int x, int y, int d); +void consolePutChar(int x, int y, char c); +void consolePutBin(int x, int y, int b); + +void consoleClear(void); + +#ifdef __cplusplus +} +#endif + +#endif +////////////////////////////////////////////////////////////////////// +// +// consol.h --provides basic consol type print functionality +// +// version 0.1, February 14, 2005 +// +// Copyright (C) 2005 Michael Noland (joat) and Jason Rogers (dovoto) +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any +// damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any +// purpose, including commercial applications, and to alter it and +// redistribute it freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you +// must not claim that you wrote the original software. If you use +// this software in a product, an acknowledgment in the product +// documentation would be appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and +// must not be misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source +// distribution. +// +// Changelog: +// 0.1: First version +// 0.2: Fixed sprite mapping bug. 1D mapping should work now. +// Changed some register defines for consistency. +// +////////////////////////////////////////////////////////////////////// +#ifndef CONSOLE_H2 +#define CONSOLE_H2 + +#define CONSOLE_USE_COLOR255 16 + +#ifdef __cplusplus +extern "C" { +#endif + +void consoleInit(u16* font, u16* charBase, u16 numCharacters, u8 charStart, u16* map, u8 pal, u8 bitDepth); +void consoleInitDefault(u16* map, u16* charBase, u8 bitDepth); + +void consolePrintf(const char* s, ...); + +void consolePrintSet(int x, int y); + +void consolePrintChar(char c); + +void consolePutString(int x, int y, char* s); +void consolePutInt(int x, int y, int d); +void consolePutX(int x, int y, int d); +void consolePutChar(int x, int y, char c); +void consolePutBin(int x, int y, int b); + +void consoleClear(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/backends/platform/ds/arm9/source/scummhelp.cpp b/backends/platform/ds/arm9/source/scummhelp.cpp new file mode 100644 index 0000000000..a76e1fa10f --- /dev/null +++ b/backends/platform/ds/arm9/source/scummhelp.cpp @@ -0,0 +1,172 @@ +#include "stdafx.h" +#include "scummhelp.h" + +#define ADD_BIND(k,d) do { key[i] = k; dsc[i] = d; i++; } while (0) +#define ADD_TEXT(d) ADD_BIND("",d) +#define ADD_LINE ADD_BIND("","") + +#define HELP_NUM_LINES 15 + +namespace DS { + +void updateStrings(byte gameId, byte version, Common::Platform platform, + int page, Common::String &title, Common::String *&key, Common::String *&dsc) { + key = new Common::String[HELP_NUM_LINES]; + dsc = new Common::String[HELP_NUM_LINES]; + int i = 0; + switch (page) { + case 1: { + title = "DS Controls (right handed):"; + ADD_BIND("Pad Left", "Left mouse button"); + ADD_BIND("Pad Right", "Right mouse button"); + ADD_BIND("Pad Up", "Mouse hover mode (no click)"); + ADD_BIND("Pad Down", "Skip dialog line (some games)"); + ADD_BIND("Start", "Pause/Game menu"); + ADD_BIND("Select", "DS Options menu"); + ADD_BIND("B", "Skip cutscenes"); + ADD_BIND("A", "Switch screens"); + ADD_BIND("Y", "Show/hide debug console"); + ADD_BIND("X", "Show/hide keyboard"); + ADD_BIND("L + Pad", "Scroll current touch screen view"); + ADD_BIND("L + Pen", "Scroll current touch screen view"); + ADD_BIND("L + B", "Zoom in"); + ADD_BIND("L + A", "Zoom out"); + break; + } + + case 2: { + title = "DS Controls (left handed):"; + ADD_BIND("Y", "Left mouse button"); + ADD_BIND("A", "Right mouse button"); + ADD_BIND("X", "Mouse hover mode (no click)"); + ADD_BIND("B", "Skip dialog line (some games)"); + ADD_BIND("Start", "Pause/Game menu"); + ADD_BIND("Select", "DS Options menu"); + ADD_BIND("Pad Down", "Skip cutscenes"); + ADD_BIND("Pad Up", "Show/hide keyboard"); + ADD_BIND("Pad Left", "Show/hide debug console"); + ADD_BIND("Pad Right", "Swap screens"); + ADD_BIND("R + Pad", "Scroll current touch screen view"); + ADD_BIND("R + Pen", "Scroll current touch screen view"); + ADD_BIND("R + B", "Zoom in"); + ADD_BIND("R + A", "Zoom out"); + break; + } + + case 3: { + title = "Indiana Jones Fighting controls:"; + ADD_BIND("Pad Left", "Move left"); + ADD_BIND("Pad Right", "Move right"); + ADD_BIND("Pad Up", "High guard"); + ADD_BIND("Pad Down", "Guard down"); + ADD_BIND("Y", "Guard middle"); + ADD_BIND("X", "Punch high"); + ADD_BIND("A", "Punch middle"); + ADD_BIND("B", "Punch low"); + + ADD_BIND("L+R", "Hold during bootup to clear SRAM"); + ADD_BIND("", "(flash cart only)"); + break; + } + } + + + while (i < HELP_NUM_LINES) { + ADD_LINE; + } + +} + +} + + +#undef ADD_BIND +#undef ADD_TEXT +#undef ADD_LINE + +#include "stdafx.h" +#include "scummhelp.h" + +#define ADD_BIND(k,d) do { key[i] = k; dsc[i] = d; i++; } while (0) +#define ADD_TEXT(d) ADD_BIND("",d) +#define ADD_LINE ADD_BIND("","") + +#define HELP_NUM_LINES 15 + +namespace DS { + +void updateStrings(byte gameId, byte version, Common::Platform platform, + int page, Common::String &title, Common::String *&key, Common::String *&dsc) { + key = new Common::String[HELP_NUM_LINES]; + dsc = new Common::String[HELP_NUM_LINES]; + int i = 0; + switch (page) { + case 1: { + title = "DS Controls (right handed):"; + ADD_BIND("Pad Left", "Left mouse button"); + ADD_BIND("Pad Right", "Right mouse button"); + ADD_BIND("Pad Up", "Mouse hover mode (no click)"); + ADD_BIND("Pad Down", "Skip dialog line (some games)"); + ADD_BIND("Start", "Pause/Game menu"); + ADD_BIND("Select", "DS Options menu"); + ADD_BIND("B", "Skip cutscenes"); + ADD_BIND("A", "Switch screens"); + ADD_BIND("Y", "Show/hide debug console"); + ADD_BIND("X", "Show/hide keyboard"); + ADD_BIND("L + Pad", "Scroll current touch screen view"); + ADD_BIND("L + Pen", "Scroll current touch screen view"); + ADD_BIND("L + B", "Zoom in"); + ADD_BIND("L + A", "Zoom out"); + break; + } + + case 2: { + title = "DS Controls (left handed):"; + ADD_BIND("Y", "Left mouse button"); + ADD_BIND("A", "Right mouse button"); + ADD_BIND("X", "Mouse hover mode (no click)"); + ADD_BIND("B", "Skip dialog line (some games)"); + ADD_BIND("Start", "Pause/Game menu"); + ADD_BIND("Select", "DS Options menu"); + ADD_BIND("Pad Down", "Skip cutscenes"); + ADD_BIND("Pad Up", "Show/hide keyboard"); + ADD_BIND("Pad Left", "Show/hide debug console"); + ADD_BIND("Pad Right", "Swap screens"); + ADD_BIND("R + Pad", "Scroll current touch screen view"); + ADD_BIND("R + Pen", "Scroll current touch screen view"); + ADD_BIND("R + B", "Zoom in"); + ADD_BIND("R + A", "Zoom out"); + break; + } + + case 3: { + title = "Indiana Jones Fighting controls:"; + ADD_BIND("Pad Left", "Move left"); + ADD_BIND("Pad Right", "Move right"); + ADD_BIND("Pad Up", "High guard"); + ADD_BIND("Pad Down", "Guard down"); + ADD_BIND("Y", "Guard middle"); + ADD_BIND("X", "Punch high"); + ADD_BIND("A", "Punch middle"); + ADD_BIND("B", "Punch low"); + + ADD_BIND("L+R", "Hold during bootup to clear SRAM"); + ADD_BIND("", "(flash cart only)"); + break; + } + } + + + while (i < HELP_NUM_LINES) { + ADD_LINE; + } + +} + +} + + +#undef ADD_BIND +#undef ADD_TEXT +#undef ADD_LINE + diff --git a/backends/platform/ds/arm9/source/scummhelp.h b/backends/platform/ds/arm9/source/scummhelp.h new file mode 100644 index 0000000000..ef7e27d251 --- /dev/null +++ b/backends/platform/ds/arm9/source/scummhelp.h @@ -0,0 +1,64 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _SCUMMHELP_H_ +#define _SCUMMHELP_H_ + +#include "common/str.h" + +namespace DS { + +void updateStrings(byte gameId, byte version, Common::Platform platform, + int page, Common::String &title, Common::String *&key, Common::String *&dsc); + +} + +#endif +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _SCUMMHELP_H_ +#define _SCUMMHELP_H_ + +#include "common/str.h" + +namespace DS { + +void updateStrings(byte gameId, byte version, Common::Platform platform, + int page, Common::String &title, Common::String *&key, Common::String *&dsc); + +} + +#endif diff --git a/backends/platform/ds/arm9/source/touchkeyboard.cpp b/backends/platform/ds/arm9/source/touchkeyboard.cpp new file mode 100644 index 0000000000..d3e8a61077 --- /dev/null +++ b/backends/platform/ds/arm9/source/touchkeyboard.cpp @@ -0,0 +1,626 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ +#include +#include "touchkeyboard.h" +#include "keyboard_raw.h" +#include "keyboard_pal_raw.h" +#include "dsmain.h" +#include "osystem_ds.h" + +namespace DS { + +struct key_data { + char keyNum; + char x, y; + int character; + bool pressed; +}; + +#define DS_NUM_KEYS 60 +#define DS_SHIFT 0 +#define DS_BACKSPACE 8 +#define DS_RETURN 13 +#define DS_CAPSLOCK 1 + + +key_data keys[DS_NUM_KEYS] = { + // Key number x y character + + // Numbers + {28, 3, 0, '1'}, + {29, 5, 0, '2'}, + {30, 7, 0, '3'}, + {31, 9, 0, '4'}, + {32, 11, 0, '5'}, + {33, 13, 0, '6'}, + {34, 15, 0, '7'}, + {35, 17, 0, '8'}, + {36, 19, 0, '9'}, + {27, 21, 0, '0'}, + {45, 23, 0, SDLK_MINUS}, + {50, 25, 0, SDLK_EQUALS}, + {52, 27, 0, SDLK_BACKSPACE}, + + // Top row + {'Q'-'A' + 1, 4, 2, 'Q'}, + {'W'-'A' + 1, 6, 2, 'W'}, + {'E'-'A' + 1, 8, 2, 'E'}, + {'R'-'A' + 1, 10, 2, 'R'}, + {'T'-'A' + 1, 12, 2, 'T'}, + {'Y'-'A' + 1, 14, 2, 'Y'}, + {'U'-'A' + 1, 16, 2, 'U'}, + {'I'-'A' + 1, 18, 2, 'I'}, + {'O'-'A' + 1, 20, 2, 'O'}, + {'P'-'A' + 1, 22, 2, 'P'}, + {43, 24, 2, SDLK_LEFTBRACKET}, + {44, 26, 2, SDLK_RIGHTBRACKET}, + + // Middle row + {55, 3, 4, DS_CAPSLOCK}, + {'A'-'A' + 1, 5, 4, 'A'}, + {'S'-'A' + 1, 7, 4, 'S'}, + {'D'-'A' + 1, 9, 4, 'D'}, + {'F'-'A' + 1, 11, 4, 'F'}, + {'G'-'A' + 1, 13, 4, 'G'}, + {'H'-'A' + 1, 15, 4, 'H'}, + {'J'-'A' + 1, 17, 4, 'J'}, + {'K'-'A' + 1, 19, 4, 'K'}, + {'L'-'A' + 1, 21, 4, 'L'}, + {42, 23, 4, SDLK_SEMICOLON}, + {41, 25, 4, SDLK_QUOTE}, + {46, 27, 4, SDLK_RETURN}, + + // Bottom row + {51, 4, 6, DS_SHIFT}, + {'Z'-'A' + 1, 6, 6, 'Z'}, + {'X'-'A' + 1, 8, 6, 'X'}, + {'C'-'A' + 1, 10, 6, 'C'}, + {'V'-'A' + 1, 12, 6, 'V'}, + {'B'-'A' + 1, 14, 6, 'B'}, + {'N'-'A' + 1, 16, 6, 'N'}, + {'M'-'A' + 1, 18, 6, 'M'}, + {38, 20, 6, SDLK_COMMA}, + {39, 22, 6, SDLK_PERIOD}, + {40, 24, 6, SDLK_SLASH}, + + // Space bar + {47, 9, 8, SDLK_SPACE}, + {48, 11, 8, SDLK_SPACE}, + {48, 13, 8, SDLK_SPACE}, + {48, 15, 8, SDLK_SPACE}, + {48, 17, 8, SDLK_SPACE}, + {49, 19, 8, SDLK_SPACE}, + + // Cursor arrows + {52, 27, 8, SDLK_LEFT}, + {54, 29, 8, SDLK_DOWN}, + {53, 31, 8, SDLK_RIGHT}, + {51, 29, 6, SDLK_UP}, + + // Close button + {56, 30, 0, SDLK_UNKNOWN}, + +}; + +int keyboardX; +int keyboardY; + +int mapBase; +int tileBase; + +bool shiftState; +bool capsLockState; + +bool closed; + +void restoreVRAM(int tileBase, int mapBase, u16* saveSpace) { + for (int r = 0; r < 32 * 32; r++) { + ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r] = *saveSpace++; + } + + for (int r = 0; r < 4096; r++) { + ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r] = *saveSpace++; + } +} + +void drawKeyboard(int tileBase, int mapBase, u16* saveSpace) { + + + for (int r = 0; r < 32 * 32; r++) { +// *saveSpace++ = ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r]; + ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r] = 127; + } + + for (int r = 0; r < 4096; r++) { +// *saveSpace++ = ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r]; + ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r] = ((u16 *) (keyboard_raw))[r]; + } + + for (int r = 0; r < 16; r++) { + BG_PALETTE_SUB[r] = ((u16 *) (keyboard_pal_raw))[r]; + } + + for (int r = 0; r < 16; r++) { + int col = ((u16 *) (keyboard_pal_raw))[r]; + + int red = col & 0x001F; + int green = (col & 0x03E0) >> 5; + int blue = (col & 0x7C00) >> 10; + + red = (red * 8) / 16; + green = (green * 8) / 16; + blue = (blue * 8) / 16; + + BG_PALETTE_SUB[16 + r] = red | (green << 5) | (blue << 10); + } + + keyboardX = -2; + keyboardY = 2; + + mapBase = mapBase; + tileBase = tileBase; + + shiftState = false; + capsLockState = false; + + int x = keyboardX; + int y = keyboardY; + + u16* base = ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase)); + + for (int r = 0; r < DS_NUM_KEYS; r++) { + base[(y + keys[r].y) * 32 + x + keys[r].x] = keys[r].keyNum * 2; + base[(y + keys[r].y) * 32 + x + keys[r].x + 1] = keys[r].keyNum * 2 + 1; + + base[(y + keys[r].y + 1) * 32 + x + keys[r].x] = 128 + keys[r].keyNum * 2; + base[(y + keys[r].y + 1) * 32 + x + keys[r].x + 1] = 128 + keys[r].keyNum * 2 + 1; + + keys[r].pressed = false; + } + + closed = false; +} + +bool getKeyboardClosed() { + return closed; +} + +void setKeyHighlight(int key, bool highlight) { + u16* base = ((u16 *) SCREEN_BASE_BLOCK_SUB(DS::mapBase)); + + if (highlight) { + base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x] |= 0x1000; + base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x + 1] |= 0x1000; + base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x] |= 0x1000; + base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x + 1] |= 0x1000; + } else { + base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x] &= ~0x1000; + base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x + 1] &= ~0x1000; + base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x] &= ~0x1000; + base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x + 1] &= ~0x1000; + } +} + +void addKeyboardEvents() { + if (DS::getPenDown()) { + int x = IPC->touchXpx; + int y = IPC->touchYpx; + + int tx = (x >> 3) - keyboardX; + int ty = (y >> 3) - keyboardY; + +// consolePrintf("x=%d y=%d\n", tx, ty); + + for (int r = 0; r < DS_NUM_KEYS; r++) { + if (( (tx >= keys[r].x) && (tx <= keys[r].x + 1)) && + (ty >= keys[r].y) && (ty <= keys[r].y + 1)) { + OSystem_DS* system = OSystem_DS::instance(); + OSystem::Event event; + +// consolePrintf("Key: %d\n", r); + if ((keys[r].character == SDLK_UNKNOWN)) { + // Close button + DS::closed = true; + } else if ((keys[r].character >= '0') && (keys[r].character <= '9')) { + event.kbd.ascii = keys[r].character; + event.kbd.keycode = 0; + + } else if ((keys[r].character >= 'A') && (keys[r].character <= 'Z')) { + + if ((!DS::shiftState) && (!DS::capsLockState)) { + event.kbd.ascii = keys[r].character + 32; // Make key lowercase. + } else { + event.kbd.ascii = keys[r].character; + } + + event.kbd.keycode = event.kbd.ascii; + } else { + event.kbd.ascii = keys[r].character; + event.kbd.keycode = keys[r].character; + } + + + + //event.kbd.keycode = keys[r].character; + //event.kbd.ascii = keys[r].character; + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.flags = 0; + system->addEvent(event); + + event.type = OSystem::EVENT_KEYUP; + system->addEvent(event); + + switch (keys[r].character) { + case DS_SHIFT: { + DS::shiftState = !DS::shiftState; + DS::setKeyHighlight(r, DS::shiftState); + break; + } + + case DS_CAPSLOCK: { + DS::capsLockState = !DS::capsLockState; + DS::setKeyHighlight(r, DS::capsLockState); + break; + } + + default: { + DS::setKeyHighlight(r, true); + keys[r].pressed = true; + + if (DS::shiftState) { + DS::shiftState = false; + for (int t = 0; t < DS_NUM_KEYS; t++) { + if (keys[t].character == DS_SHIFT) { + DS::setKeyHighlight(t, false); + } + } + } + break; + } + } + + } + } + } + + if (DS::getPenReleased()) { + for (int r = 0; r < DS_NUM_KEYS; r++) { + if (keys[r].pressed) { + DS::setKeyHighlight(r, false); + keys[r].pressed = false; + } + } + } +} + +} + +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ +#include +#include "touchkeyboard.h" +#include "keyboard_raw.h" +#include "keyboard_pal_raw.h" +#include "dsmain.h" +#include "osystem_ds.h" + +namespace DS { + +struct key_data { + char keyNum; + char x, y; + int character; + bool pressed; +}; + +#define DS_NUM_KEYS 60 +#define DS_SHIFT 0 +#define DS_BACKSPACE 8 +#define DS_RETURN 13 +#define DS_CAPSLOCK 1 + + +key_data keys[DS_NUM_KEYS] = { + // Key number x y character + + // Numbers + {28, 3, 0, '1'}, + {29, 5, 0, '2'}, + {30, 7, 0, '3'}, + {31, 9, 0, '4'}, + {32, 11, 0, '5'}, + {33, 13, 0, '6'}, + {34, 15, 0, '7'}, + {35, 17, 0, '8'}, + {36, 19, 0, '9'}, + {27, 21, 0, '0'}, + {45, 23, 0, SDLK_MINUS}, + {50, 25, 0, SDLK_EQUALS}, + {52, 27, 0, SDLK_BACKSPACE}, + + // Top row + {'Q'-'A' + 1, 4, 2, 'Q'}, + {'W'-'A' + 1, 6, 2, 'W'}, + {'E'-'A' + 1, 8, 2, 'E'}, + {'R'-'A' + 1, 10, 2, 'R'}, + {'T'-'A' + 1, 12, 2, 'T'}, + {'Y'-'A' + 1, 14, 2, 'Y'}, + {'U'-'A' + 1, 16, 2, 'U'}, + {'I'-'A' + 1, 18, 2, 'I'}, + {'O'-'A' + 1, 20, 2, 'O'}, + {'P'-'A' + 1, 22, 2, 'P'}, + {43, 24, 2, SDLK_LEFTBRACKET}, + {44, 26, 2, SDLK_RIGHTBRACKET}, + + // Middle row + {55, 3, 4, DS_CAPSLOCK}, + {'A'-'A' + 1, 5, 4, 'A'}, + {'S'-'A' + 1, 7, 4, 'S'}, + {'D'-'A' + 1, 9, 4, 'D'}, + {'F'-'A' + 1, 11, 4, 'F'}, + {'G'-'A' + 1, 13, 4, 'G'}, + {'H'-'A' + 1, 15, 4, 'H'}, + {'J'-'A' + 1, 17, 4, 'J'}, + {'K'-'A' + 1, 19, 4, 'K'}, + {'L'-'A' + 1, 21, 4, 'L'}, + {42, 23, 4, SDLK_SEMICOLON}, + {41, 25, 4, SDLK_QUOTE}, + {46, 27, 4, SDLK_RETURN}, + + // Bottom row + {51, 4, 6, DS_SHIFT}, + {'Z'-'A' + 1, 6, 6, 'Z'}, + {'X'-'A' + 1, 8, 6, 'X'}, + {'C'-'A' + 1, 10, 6, 'C'}, + {'V'-'A' + 1, 12, 6, 'V'}, + {'B'-'A' + 1, 14, 6, 'B'}, + {'N'-'A' + 1, 16, 6, 'N'}, + {'M'-'A' + 1, 18, 6, 'M'}, + {38, 20, 6, SDLK_COMMA}, + {39, 22, 6, SDLK_PERIOD}, + {40, 24, 6, SDLK_SLASH}, + + // Space bar + {47, 9, 8, SDLK_SPACE}, + {48, 11, 8, SDLK_SPACE}, + {48, 13, 8, SDLK_SPACE}, + {48, 15, 8, SDLK_SPACE}, + {48, 17, 8, SDLK_SPACE}, + {49, 19, 8, SDLK_SPACE}, + + // Cursor arrows + {52, 27, 8, SDLK_LEFT}, + {54, 29, 8, SDLK_DOWN}, + {53, 31, 8, SDLK_RIGHT}, + {51, 29, 6, SDLK_UP}, + + // Close button + {56, 30, 0, SDLK_UNKNOWN}, + +}; + +int keyboardX; +int keyboardY; + +int mapBase; +int tileBase; + +bool shiftState; +bool capsLockState; + +bool closed; + +void restoreVRAM(int tileBase, int mapBase, u16* saveSpace) { + for (int r = 0; r < 32 * 32; r++) { + ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r] = *saveSpace++; + } + + for (int r = 0; r < 4096; r++) { + ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r] = *saveSpace++; + } +} + +void drawKeyboard(int tileBase, int mapBase, u16* saveSpace) { + + + for (int r = 0; r < 32 * 32; r++) { +// *saveSpace++ = ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r]; + ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase))[r] = 127; + } + + for (int r = 0; r < 4096; r++) { +// *saveSpace++ = ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r]; + ((u16 *) CHAR_BASE_BLOCK_SUB(tileBase))[r] = ((u16 *) (keyboard_raw))[r]; + } + + for (int r = 0; r < 16; r++) { + BG_PALETTE_SUB[r] = ((u16 *) (keyboard_pal_raw))[r]; + } + + for (int r = 0; r < 16; r++) { + int col = ((u16 *) (keyboard_pal_raw))[r]; + + int red = col & 0x001F; + int green = (col & 0x03E0) >> 5; + int blue = (col & 0x7C00) >> 10; + + red = (red * 8) / 16; + green = (green * 8) / 16; + blue = (blue * 8) / 16; + + BG_PALETTE_SUB[16 + r] = red | (green << 5) | (blue << 10); + } + + keyboardX = -2; + keyboardY = 2; + + mapBase = mapBase; + tileBase = tileBase; + + shiftState = false; + capsLockState = false; + + int x = keyboardX; + int y = keyboardY; + + u16* base = ((u16 *) SCREEN_BASE_BLOCK_SUB(mapBase)); + + for (int r = 0; r < DS_NUM_KEYS; r++) { + base[(y + keys[r].y) * 32 + x + keys[r].x] = keys[r].keyNum * 2; + base[(y + keys[r].y) * 32 + x + keys[r].x + 1] = keys[r].keyNum * 2 + 1; + + base[(y + keys[r].y + 1) * 32 + x + keys[r].x] = 128 + keys[r].keyNum * 2; + base[(y + keys[r].y + 1) * 32 + x + keys[r].x + 1] = 128 + keys[r].keyNum * 2 + 1; + + keys[r].pressed = false; + } + + closed = false; +} + +bool getKeyboardClosed() { + return closed; +} + +void setKeyHighlight(int key, bool highlight) { + u16* base = ((u16 *) SCREEN_BASE_BLOCK_SUB(DS::mapBase)); + + if (highlight) { + base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x] |= 0x1000; + base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x + 1] |= 0x1000; + base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x] |= 0x1000; + base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x + 1] |= 0x1000; + } else { + base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x] &= ~0x1000; + base[(keyboardY + keys[key].y) * 32 + keyboardX + keys[key].x + 1] &= ~0x1000; + base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x] &= ~0x1000; + base[(keyboardY + keys[key].y + 1) * 32 + keyboardX + keys[key].x + 1] &= ~0x1000; + } +} + +void addKeyboardEvents() { + if (DS::getPenDown()) { + int x = IPC->touchXpx; + int y = IPC->touchYpx; + + int tx = (x >> 3) - keyboardX; + int ty = (y >> 3) - keyboardY; + +// consolePrintf("x=%d y=%d\n", tx, ty); + + for (int r = 0; r < DS_NUM_KEYS; r++) { + if (( (tx >= keys[r].x) && (tx <= keys[r].x + 1)) && + (ty >= keys[r].y) && (ty <= keys[r].y + 1)) { + OSystem_DS* system = OSystem_DS::instance(); + OSystem::Event event; + +// consolePrintf("Key: %d\n", r); + if ((keys[r].character == SDLK_UNKNOWN)) { + // Close button + DS::closed = true; + } else if ((keys[r].character >= '0') && (keys[r].character <= '9')) { + event.kbd.ascii = keys[r].character; + event.kbd.keycode = 0; + + } else if ((keys[r].character >= 'A') && (keys[r].character <= 'Z')) { + + if ((!DS::shiftState) && (!DS::capsLockState)) { + event.kbd.ascii = keys[r].character + 32; // Make key lowercase. + } else { + event.kbd.ascii = keys[r].character; + } + + event.kbd.keycode = event.kbd.ascii; + } else { + event.kbd.ascii = keys[r].character; + event.kbd.keycode = keys[r].character; + } + + + + //event.kbd.keycode = keys[r].character; + //event.kbd.ascii = keys[r].character; + event.type = OSystem::EVENT_KEYDOWN; + event.kbd.flags = 0; + system->addEvent(event); + + event.type = OSystem::EVENT_KEYUP; + system->addEvent(event); + + switch (keys[r].character) { + case DS_SHIFT: { + DS::shiftState = !DS::shiftState; + DS::setKeyHighlight(r, DS::shiftState); + break; + } + + case DS_CAPSLOCK: { + DS::capsLockState = !DS::capsLockState; + DS::setKeyHighlight(r, DS::capsLockState); + break; + } + + default: { + DS::setKeyHighlight(r, true); + keys[r].pressed = true; + + if (DS::shiftState) { + DS::shiftState = false; + for (int t = 0; t < DS_NUM_KEYS; t++) { + if (keys[t].character == DS_SHIFT) { + DS::setKeyHighlight(t, false); + } + } + } + break; + } + } + + } + } + } + + if (DS::getPenReleased()) { + for (int r = 0; r < DS_NUM_KEYS; r++) { + if (keys[r].pressed) { + DS::setKeyHighlight(r, false); + keys[r].pressed = false; + } + } + } +} + +} + diff --git a/backends/platform/ds/arm9/source/touchkeyboard.h b/backends/platform/ds/arm9/source/touchkeyboard.h new file mode 100644 index 0000000000..5afab98d03 --- /dev/null +++ b/backends/platform/ds/arm9/source/touchkeyboard.h @@ -0,0 +1,202 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _TOUCHKEYBOARD_H_ +#define _TOUCHKEYBOARD_H_ + +namespace DS { + +enum SDLKey { + SDLK_UNKNOWN = 0, SDLK_FIRST = 0, SDLK_BACKSPACE = 8, SDLK_TAB = 9, + SDLK_CLEAR = 12, SDLK_RETURN = 13, SDLK_PAUSE = 19, SDLK_ESCAPE = 27, + SDLK_SPACE = 32, SDLK_EXCLAIM = 33, SDLK_QUOTEDBL = 34, SDLK_HASH = 35, + SDLK_DOLLAR = 36, SDLK_AMPERSAND = 38, SDLK_QUOTE = 39, SDLK_LEFTPAREN = 40, + SDLK_RIGHTPAREN = 41, SDLK_ASTERISK = 42, SDLK_PLUS = 43, SDLK_COMMA = 44, + SDLK_MINUS = 45, SDLK_PERIOD = 46, SDLK_SLASH = 47, SDLK_0 = 48, + SDLK_1 = 49, SDLK_2 = 50, SDLK_3 = 51, SDLK_4 = 52, + SDLK_5 = 53, SDLK_6 = 54, SDLK_7 = 55, SDLK_8 = 56, + SDLK_9 = 57, SDLK_COLON = 58, SDLK_SEMICOLON = 59, SDLK_LESS = 60, + SDLK_EQUALS = 61, SDLK_GREATER = 62, SDLK_QUESTION = 63, SDLK_AT = 64, + SDLK_LEFTBRACKET = 91, SDLK_BACKSLASH = 92, SDLK_RIGHTBRACKET = 93, SDLK_CARET = 94, + SDLK_UNDERSCORE = 95, SDLK_BACKQUOTE = 96, SDLK_a = 97, SDLK_b = 98, + SDLK_c = 99, SDLK_d = 100, SDLK_e = 101, SDLK_f = 102, + SDLK_g = 103, SDLK_h = 104, SDLK_i = 105, SDLK_j = 106, + SDLK_k = 107, SDLK_l = 108, SDLK_m = 109, SDLK_n = 110, + SDLK_o = 111, SDLK_p = 112, SDLK_q = 113, SDLK_r = 114, + SDLK_s = 115, SDLK_t = 116, SDLK_u = 117, SDLK_v = 118, + SDLK_w = 119, SDLK_x = 120, SDLK_y = 121, SDLK_z = 122, + SDLK_DELETE = 127, SDLK_WORLD_0 = 160, SDLK_WORLD_1 = 161, SDLK_WORLD_2 = 162, + SDLK_WORLD_3 = 163, SDLK_WORLD_4 = 164, SDLK_WORLD_5 = 165, SDLK_WORLD_6 = 166, + SDLK_WORLD_7 = 167, SDLK_WORLD_8 = 168, SDLK_WORLD_9 = 169, SDLK_WORLD_10 = 170, + SDLK_WORLD_11 = 171, SDLK_WORLD_12 = 172, SDLK_WORLD_13 = 173, SDLK_WORLD_14 = 174, + SDLK_WORLD_15 = 175, SDLK_WORLD_16 = 176, SDLK_WORLD_17 = 177, SDLK_WORLD_18 = 178, + SDLK_WORLD_19 = 179, SDLK_WORLD_20 = 180, SDLK_WORLD_21 = 181, SDLK_WORLD_22 = 182, + SDLK_WORLD_23 = 183, SDLK_WORLD_24 = 184, SDLK_WORLD_25 = 185, SDLK_WORLD_26 = 186, + SDLK_WORLD_27 = 187, SDLK_WORLD_28 = 188, SDLK_WORLD_29 = 189, SDLK_WORLD_30 = 190, + SDLK_WORLD_31 = 191, SDLK_WORLD_32 = 192, SDLK_WORLD_33 = 193, SDLK_WORLD_34 = 194, + SDLK_WORLD_35 = 195, SDLK_WORLD_36 = 196, SDLK_WORLD_37 = 197, SDLK_WORLD_38 = 198, + SDLK_WORLD_39 = 199, SDLK_WORLD_40 = 200, SDLK_WORLD_41 = 201, SDLK_WORLD_42 = 202, + SDLK_WORLD_43 = 203, SDLK_WORLD_44 = 204, SDLK_WORLD_45 = 205, SDLK_WORLD_46 = 206, + SDLK_WORLD_47 = 207, SDLK_WORLD_48 = 208, SDLK_WORLD_49 = 209, SDLK_WORLD_50 = 210, + SDLK_WORLD_51 = 211, SDLK_WORLD_52 = 212, SDLK_WORLD_53 = 213, SDLK_WORLD_54 = 214, + SDLK_WORLD_55 = 215, SDLK_WORLD_56 = 216, SDLK_WORLD_57 = 217, SDLK_WORLD_58 = 218, + SDLK_WORLD_59 = 219, SDLK_WORLD_60 = 220, SDLK_WORLD_61 = 221, SDLK_WORLD_62 = 222, + SDLK_WORLD_63 = 223, SDLK_WORLD_64 = 224, SDLK_WORLD_65 = 225, SDLK_WORLD_66 = 226, + SDLK_WORLD_67 = 227, SDLK_WORLD_68 = 228, SDLK_WORLD_69 = 229, SDLK_WORLD_70 = 230, + SDLK_WORLD_71 = 231, SDLK_WORLD_72 = 232, SDLK_WORLD_73 = 233, SDLK_WORLD_74 = 234, + SDLK_WORLD_75 = 235, SDLK_WORLD_76 = 236, SDLK_WORLD_77 = 237, SDLK_WORLD_78 = 238, + SDLK_WORLD_79 = 239, SDLK_WORLD_80 = 240, SDLK_WORLD_81 = 241, SDLK_WORLD_82 = 242, + SDLK_WORLD_83 = 243, SDLK_WORLD_84 = 244, SDLK_WORLD_85 = 245, SDLK_WORLD_86 = 246, + SDLK_WORLD_87 = 247, SDLK_WORLD_88 = 248, SDLK_WORLD_89 = 249, SDLK_WORLD_90 = 250, + SDLK_WORLD_91 = 251, SDLK_WORLD_92 = 252, SDLK_WORLD_93 = 253, SDLK_WORLD_94 = 254, + SDLK_WORLD_95 = 255, SDLK_KP0 = 256, SDLK_KP1 = 257, SDLK_KP2 = 258, + SDLK_KP3 = 259, SDLK_KP4 = 260, SDLK_KP5 = 261, SDLK_KP6 = 262, + SDLK_KP7 = 263, SDLK_KP8 = 264, SDLK_KP9 = 265, SDLK_KP_PERIOD = 266, + SDLK_KP_DIVIDE = 267, SDLK_KP_MULTIPLY = 268, SDLK_KP_MINUS = 269, SDLK_KP_PLUS = 270, + SDLK_KP_ENTER = 271, SDLK_KP_EQUALS = 272, SDLK_UP = 273, SDLK_DOWN = 274, + SDLK_RIGHT = 275, SDLK_LEFT = 276, SDLK_INSERT = 277, SDLK_HOME = 278, + SDLK_END = 279, SDLK_PAGEUP = 280, SDLK_PAGEDOWN = 281, SDLK_F1 = 282, + SDLK_F2 = 283, SDLK_F3 = 284, SDLK_F4 = 285, SDLK_F5 = 286, + SDLK_F6 = 287, SDLK_F7 = 288, SDLK_F8 = 289, SDLK_F9 = 290, + SDLK_F10 = 291, SDLK_F11 = 292, SDLK_F12 = 293, SDLK_F13 = 294, + SDLK_F14 = 295, SDLK_F15 = 296, SDLK_NUMLOCK = 300, SDLK_CAPSLOCK = 301, + SDLK_SCROLLOCK = 302, SDLK_RSHIFT = 303, SDLK_LSHIFT = 304, SDLK_RCTRL = 305, + SDLK_LCTRL = 306, SDLK_RALT = 307, SDLK_LALT = 308, SDLK_RMETA = 309, + SDLK_LMETA = 310, SDLK_LSUPER = 311, SDLK_RSUPER = 312, SDLK_MODE = 313, + SDLK_COMPOSE = 314, SDLK_HELP = 315, SDLK_PRINT = 316, SDLK_SYSREQ = 317, + SDLK_BREAK = 318, SDLK_MENU = 319, SDLK_POWER = 320, SDLK_EURO = 321, + SDLK_UNDO = 322, SDLK_LAST +}; + +enum SDLMod { + KMOD_NONE = 0x0000, KMOD_LSHIFT = 0x0001, KMOD_RSHIFT = 0x0002, KMOD_LCTRL = 0x0040, + KMOD_RCTRL = 0x0080, KMOD_LALT = 0x0100, KMOD_RALT = 0x0200, KMOD_LMETA = 0x0400, + KMOD_RMETA = 0x0800, KMOD_NUM = 0x1000, KMOD_CAPS = 0x2000, KMOD_MODE = 0x4000, + KMOD_RESERVED = 0x8000 +}; + +void drawKeyboard(int tileBase, int mapBase, u16* saveSpace); +void restoreVRAM(int tileBase, int mapBase, u16* saveSpace); +void addKeyboardEvents(); +bool getKeyboardClosed(); + +} + +#endif +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _TOUCHKEYBOARD_H_ +#define _TOUCHKEYBOARD_H_ + +namespace DS { + +enum SDLKey { + SDLK_UNKNOWN = 0, SDLK_FIRST = 0, SDLK_BACKSPACE = 8, SDLK_TAB = 9, + SDLK_CLEAR = 12, SDLK_RETURN = 13, SDLK_PAUSE = 19, SDLK_ESCAPE = 27, + SDLK_SPACE = 32, SDLK_EXCLAIM = 33, SDLK_QUOTEDBL = 34, SDLK_HASH = 35, + SDLK_DOLLAR = 36, SDLK_AMPERSAND = 38, SDLK_QUOTE = 39, SDLK_LEFTPAREN = 40, + SDLK_RIGHTPAREN = 41, SDLK_ASTERISK = 42, SDLK_PLUS = 43, SDLK_COMMA = 44, + SDLK_MINUS = 45, SDLK_PERIOD = 46, SDLK_SLASH = 47, SDLK_0 = 48, + SDLK_1 = 49, SDLK_2 = 50, SDLK_3 = 51, SDLK_4 = 52, + SDLK_5 = 53, SDLK_6 = 54, SDLK_7 = 55, SDLK_8 = 56, + SDLK_9 = 57, SDLK_COLON = 58, SDLK_SEMICOLON = 59, SDLK_LESS = 60, + SDLK_EQUALS = 61, SDLK_GREATER = 62, SDLK_QUESTION = 63, SDLK_AT = 64, + SDLK_LEFTBRACKET = 91, SDLK_BACKSLASH = 92, SDLK_RIGHTBRACKET = 93, SDLK_CARET = 94, + SDLK_UNDERSCORE = 95, SDLK_BACKQUOTE = 96, SDLK_a = 97, SDLK_b = 98, + SDLK_c = 99, SDLK_d = 100, SDLK_e = 101, SDLK_f = 102, + SDLK_g = 103, SDLK_h = 104, SDLK_i = 105, SDLK_j = 106, + SDLK_k = 107, SDLK_l = 108, SDLK_m = 109, SDLK_n = 110, + SDLK_o = 111, SDLK_p = 112, SDLK_q = 113, SDLK_r = 114, + SDLK_s = 115, SDLK_t = 116, SDLK_u = 117, SDLK_v = 118, + SDLK_w = 119, SDLK_x = 120, SDLK_y = 121, SDLK_z = 122, + SDLK_DELETE = 127, SDLK_WORLD_0 = 160, SDLK_WORLD_1 = 161, SDLK_WORLD_2 = 162, + SDLK_WORLD_3 = 163, SDLK_WORLD_4 = 164, SDLK_WORLD_5 = 165, SDLK_WORLD_6 = 166, + SDLK_WORLD_7 = 167, SDLK_WORLD_8 = 168, SDLK_WORLD_9 = 169, SDLK_WORLD_10 = 170, + SDLK_WORLD_11 = 171, SDLK_WORLD_12 = 172, SDLK_WORLD_13 = 173, SDLK_WORLD_14 = 174, + SDLK_WORLD_15 = 175, SDLK_WORLD_16 = 176, SDLK_WORLD_17 = 177, SDLK_WORLD_18 = 178, + SDLK_WORLD_19 = 179, SDLK_WORLD_20 = 180, SDLK_WORLD_21 = 181, SDLK_WORLD_22 = 182, + SDLK_WORLD_23 = 183, SDLK_WORLD_24 = 184, SDLK_WORLD_25 = 185, SDLK_WORLD_26 = 186, + SDLK_WORLD_27 = 187, SDLK_WORLD_28 = 188, SDLK_WORLD_29 = 189, SDLK_WORLD_30 = 190, + SDLK_WORLD_31 = 191, SDLK_WORLD_32 = 192, SDLK_WORLD_33 = 193, SDLK_WORLD_34 = 194, + SDLK_WORLD_35 = 195, SDLK_WORLD_36 = 196, SDLK_WORLD_37 = 197, SDLK_WORLD_38 = 198, + SDLK_WORLD_39 = 199, SDLK_WORLD_40 = 200, SDLK_WORLD_41 = 201, SDLK_WORLD_42 = 202, + SDLK_WORLD_43 = 203, SDLK_WORLD_44 = 204, SDLK_WORLD_45 = 205, SDLK_WORLD_46 = 206, + SDLK_WORLD_47 = 207, SDLK_WORLD_48 = 208, SDLK_WORLD_49 = 209, SDLK_WORLD_50 = 210, + SDLK_WORLD_51 = 211, SDLK_WORLD_52 = 212, SDLK_WORLD_53 = 213, SDLK_WORLD_54 = 214, + SDLK_WORLD_55 = 215, SDLK_WORLD_56 = 216, SDLK_WORLD_57 = 217, SDLK_WORLD_58 = 218, + SDLK_WORLD_59 = 219, SDLK_WORLD_60 = 220, SDLK_WORLD_61 = 221, SDLK_WORLD_62 = 222, + SDLK_WORLD_63 = 223, SDLK_WORLD_64 = 224, SDLK_WORLD_65 = 225, SDLK_WORLD_66 = 226, + SDLK_WORLD_67 = 227, SDLK_WORLD_68 = 228, SDLK_WORLD_69 = 229, SDLK_WORLD_70 = 230, + SDLK_WORLD_71 = 231, SDLK_WORLD_72 = 232, SDLK_WORLD_73 = 233, SDLK_WORLD_74 = 234, + SDLK_WORLD_75 = 235, SDLK_WORLD_76 = 236, SDLK_WORLD_77 = 237, SDLK_WORLD_78 = 238, + SDLK_WORLD_79 = 239, SDLK_WORLD_80 = 240, SDLK_WORLD_81 = 241, SDLK_WORLD_82 = 242, + SDLK_WORLD_83 = 243, SDLK_WORLD_84 = 244, SDLK_WORLD_85 = 245, SDLK_WORLD_86 = 246, + SDLK_WORLD_87 = 247, SDLK_WORLD_88 = 248, SDLK_WORLD_89 = 249, SDLK_WORLD_90 = 250, + SDLK_WORLD_91 = 251, SDLK_WORLD_92 = 252, SDLK_WORLD_93 = 253, SDLK_WORLD_94 = 254, + SDLK_WORLD_95 = 255, SDLK_KP0 = 256, SDLK_KP1 = 257, SDLK_KP2 = 258, + SDLK_KP3 = 259, SDLK_KP4 = 260, SDLK_KP5 = 261, SDLK_KP6 = 262, + SDLK_KP7 = 263, SDLK_KP8 = 264, SDLK_KP9 = 265, SDLK_KP_PERIOD = 266, + SDLK_KP_DIVIDE = 267, SDLK_KP_MULTIPLY = 268, SDLK_KP_MINUS = 269, SDLK_KP_PLUS = 270, + SDLK_KP_ENTER = 271, SDLK_KP_EQUALS = 272, SDLK_UP = 273, SDLK_DOWN = 274, + SDLK_RIGHT = 275, SDLK_LEFT = 276, SDLK_INSERT = 277, SDLK_HOME = 278, + SDLK_END = 279, SDLK_PAGEUP = 280, SDLK_PAGEDOWN = 281, SDLK_F1 = 282, + SDLK_F2 = 283, SDLK_F3 = 284, SDLK_F4 = 285, SDLK_F5 = 286, + SDLK_F6 = 287, SDLK_F7 = 288, SDLK_F8 = 289, SDLK_F9 = 290, + SDLK_F10 = 291, SDLK_F11 = 292, SDLK_F12 = 293, SDLK_F13 = 294, + SDLK_F14 = 295, SDLK_F15 = 296, SDLK_NUMLOCK = 300, SDLK_CAPSLOCK = 301, + SDLK_SCROLLOCK = 302, SDLK_RSHIFT = 303, SDLK_LSHIFT = 304, SDLK_RCTRL = 305, + SDLK_LCTRL = 306, SDLK_RALT = 307, SDLK_LALT = 308, SDLK_RMETA = 309, + SDLK_LMETA = 310, SDLK_LSUPER = 311, SDLK_RSUPER = 312, SDLK_MODE = 313, + SDLK_COMPOSE = 314, SDLK_HELP = 315, SDLK_PRINT = 316, SDLK_SYSREQ = 317, + SDLK_BREAK = 318, SDLK_MENU = 319, SDLK_POWER = 320, SDLK_EURO = 321, + SDLK_UNDO = 322, SDLK_LAST +}; + +enum SDLMod { + KMOD_NONE = 0x0000, KMOD_LSHIFT = 0x0001, KMOD_RSHIFT = 0x0002, KMOD_LCTRL = 0x0040, + KMOD_RCTRL = 0x0080, KMOD_LALT = 0x0100, KMOD_RALT = 0x0200, KMOD_LMETA = 0x0400, + KMOD_RMETA = 0x0800, KMOD_NUM = 0x1000, KMOD_CAPS = 0x2000, KMOD_MODE = 0x4000, + KMOD_RESERVED = 0x8000 +}; + +void drawKeyboard(int tileBase, int mapBase, u16* saveSpace); +void restoreVRAM(int tileBase, int mapBase, u16* saveSpace); +void addKeyboardEvents(); +bool getKeyboardClosed(); + +} + +#endif diff --git a/backends/platform/ds/arm9/source/zipreader.cpp b/backends/platform/ds/arm9/source/zipreader.cpp new file mode 100644 index 0000000000..b8780913ec --- /dev/null +++ b/backends/platform/ds/arm9/source/zipreader.cpp @@ -0,0 +1,442 @@ +/* Zip File Reader + * Copyright (C) 2002-2004 Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include "stdafx.h" +#include "zipreader.h" + +#define SWAP_U16(v) (((v & 0xFF) << 8) + (v & 0xFF00)) +#define SWAP_U32(v) ((SWAP_U16((v & 0XFFFF0000) >> 16) + (SWAP_U16(v & 0x0000FFFF) << 16))) + + +ZipFile::ZipFile() { + // Locate a zip file in cartridge memory space + + consolePrintf("ZIP file check..."); + + char* p = (char *) ZF_SEARCH_START; + bool found = false; + + _zipFile = NULL; + + while ((p != (char *) ZF_SEARCH_END) && (!found)) { + // Zip file header is: 0x504B0304 + + if ( (*p == 0x50) && (*(p + 1) == 0x4B) && (*(p + 2) == 0x03) && (*(p + 3) == 0x04) ) { + // Found header! + found = true; + _zipFile = p; + } + + if (!found) p += ZF_SEARCH_STRIDE; + + } + + if (_zipFile) { + consolePrintf("Ok!\n"); + } else { + consolePrintf("Not in use!\n"); + return; + } + + changeToRoot(); + restartFile(); + + if (_currentFile->compSize != (u32) getFileSize()) { + consolePrintf("Error: ZIP file contains compression!\n"); + } + + _allFilesVisible = false; +} + +bool ZipFile::isReady() { + return _zipFile != NULL; +} + +bool ZipFile::restartFile() { + _currentFile = (FileHeader *) _zipFile; + char name[128]; + getFileName(name); + + bool more = true; + + while (!currentFileInFolder() && more) { + char name[128]; + getFileName(name); + more = skipFile(); + } + + return more; +} + +bool ZipFile::currentFileInFolder() { + char name[128]; + + if (_allFilesVisible) return true; + + getFileName(name); + + if (_directory[0] == 0) { // Root directory + name[strlen(name) - 1] = 0; + return !strchr(name, '\\'); // Not in root if contains a / character before the last character + } else { +/* if (name starts with directory && it's not the directory + && (no slashes after the directory || it's the last character) + && (slash follows directory) + */ + if ((strstr(name, _directory) == name) && (strlen(name) != strlen(_directory)) + && ((strchr(name + strlen(_directory) + 1, '\\') == NULL) + || (strchr(name + strlen(_directory) + 1, '\\') == name + strlen(name) - 1)) + && (*(name + strlen(_directory)) == '\\')) { + return true; + } + } + + return false; +} + +void ZipFile::getFileName(char* name) { + strncpy(name, (char *) (_currentFile + 1), _currentFile->nameLength); + + for (int r = 0; r < (int) strlen(name); r++) { + if (name[r] == '/') name[r] = '\\'; + } + + name[_currentFile->nameLength] = 0; + + if (name[strlen(name) - 1] == '\\') { + name[strlen(name) - 1] = 0; + } +} + +bool ZipFile::skipFile() { + bool valid; + + do { + + // Move on to the next file + _currentFile = (FileHeader *) ( + ((char *) (_currentFile)) + sizeof(*_currentFile) + _currentFile->nameLength + _currentFile->fileSize + _currentFile->extraLength + ); + + // Return true if there are more files. Check this by looking for the magic number + valid = (_currentFile->magic[0] == 0x50) && + (_currentFile->magic[1] == 0x4B) && + (_currentFile->magic[2] == 0x03) && + (_currentFile->magic[3] == 0x04); + + + } while (valid && !currentFileInFolder()); + + return valid; + + // Currently doesn't handle data descriptors! +} + + + +u32 ZipFile::misaligned32(u32* v) { + char* b = (char *) v; + return (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]; +} + +u16 ZipFile::misaligned16(u16* v) { + char* b = (char *) v; + return (b[0] << 8) + b[1]; +} + +int ZipFile::getFileSize() { + return _currentFile->fileSize; +} + +bool ZipFile::isDirectory() { + return _currentFile->fileSize == 0; // This is a bit wrong, but seems to work. +} + +char* ZipFile::getFile() { + return ((char *) (_currentFile)) + sizeof(*_currentFile) + _currentFile->nameLength + _currentFile->extraLength; +} + +bool ZipFile::findFile(char* search) { + changeToRoot(); + restartFile(); + + char searchName[128]; + strcpy(searchName, search); + for (int r = 0; r < (int) strlen(searchName); r++) { + if (searchName[r] == '/') searchName[r] = '\\'; + } + + if (*(searchName + strlen(searchName) - 1) == '\\') { // Directories have a terminating slash + *(searchName + strlen(searchName) - 1) = '\0'; // which we need to dispose of. + } + + + do { + char name[128]; + getFileName(name); + if (*(name + strlen(name) - 1) == '\\') { // Directories have a terminating slash + *(name + strlen(name) - 1) = '\0'; // which we need to dispose of. + } + + + if (!stricmp(name, searchName)) { +// consolePrintf("'%s'=='%s'\n", name, searchName); + return true; // Got it! + } else { +// consolePrintf("'%s'!='%s'\n", name, searchName); + } + } while (skipFile()); + + return false; // File wasn't found +} + +void ZipFile::changeToRoot() { + _directory[0] = 0; +} + +void ZipFile::changeDirectory(char* dir) { +// consolePrintf("Current dir now '%s'\n", dir); + strcpy(_directory, dir); +} + +ZipFile::~ZipFile() { + +} +/* Zip File Reader + * Copyright (C) 2002-2004 Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include "stdafx.h" +#include "zipreader.h" + +#define SWAP_U16(v) (((v & 0xFF) << 8) + (v & 0xFF00)) +#define SWAP_U32(v) ((SWAP_U16((v & 0XFFFF0000) >> 16) + (SWAP_U16(v & 0x0000FFFF) << 16))) + + +ZipFile::ZipFile() { + // Locate a zip file in cartridge memory space + + consolePrintf("ZIP file check..."); + + char* p = (char *) ZF_SEARCH_START; + bool found = false; + + _zipFile = NULL; + + while ((p != (char *) ZF_SEARCH_END) && (!found)) { + // Zip file header is: 0x504B0304 + + if ( (*p == 0x50) && (*(p + 1) == 0x4B) && (*(p + 2) == 0x03) && (*(p + 3) == 0x04) ) { + // Found header! + found = true; + _zipFile = p; + } + + if (!found) p += ZF_SEARCH_STRIDE; + + } + + if (_zipFile) { + consolePrintf("Ok!\n"); + } else { + consolePrintf("Not in use!\n"); + return; + } + + changeToRoot(); + restartFile(); + + if (_currentFile->compSize != (u32) getFileSize()) { + consolePrintf("Error: ZIP file contains compression!\n"); + } + + _allFilesVisible = false; +} + +bool ZipFile::isReady() { + return _zipFile != NULL; +} + +bool ZipFile::restartFile() { + _currentFile = (FileHeader *) _zipFile; + char name[128]; + getFileName(name); + + bool more = true; + + while (!currentFileInFolder() && more) { + char name[128]; + getFileName(name); + more = skipFile(); + } + + return more; +} + +bool ZipFile::currentFileInFolder() { + char name[128]; + + if (_allFilesVisible) return true; + + getFileName(name); + + if (_directory[0] == 0) { // Root directory + name[strlen(name) - 1] = 0; + return !strchr(name, '\\'); // Not in root if contains a / character before the last character + } else { +/* if (name starts with directory && it's not the directory + && (no slashes after the directory || it's the last character) + && (slash follows directory) + */ + if ((strstr(name, _directory) == name) && (strlen(name) != strlen(_directory)) + && ((strchr(name + strlen(_directory) + 1, '\\') == NULL) + || (strchr(name + strlen(_directory) + 1, '\\') == name + strlen(name) - 1)) + && (*(name + strlen(_directory)) == '\\')) { + return true; + } + } + + return false; +} + +void ZipFile::getFileName(char* name) { + strncpy(name, (char *) (_currentFile + 1), _currentFile->nameLength); + + for (int r = 0; r < (int) strlen(name); r++) { + if (name[r] == '/') name[r] = '\\'; + } + + name[_currentFile->nameLength] = 0; + + if (name[strlen(name) - 1] == '\\') { + name[strlen(name) - 1] = 0; + } +} + +bool ZipFile::skipFile() { + bool valid; + + do { + + // Move on to the next file + _currentFile = (FileHeader *) ( + ((char *) (_currentFile)) + sizeof(*_currentFile) + _currentFile->nameLength + _currentFile->fileSize + _currentFile->extraLength + ); + + // Return true if there are more files. Check this by looking for the magic number + valid = (_currentFile->magic[0] == 0x50) && + (_currentFile->magic[1] == 0x4B) && + (_currentFile->magic[2] == 0x03) && + (_currentFile->magic[3] == 0x04); + + + } while (valid && !currentFileInFolder()); + + return valid; + + // Currently doesn't handle data descriptors! +} + + + +u32 ZipFile::misaligned32(u32* v) { + char* b = (char *) v; + return (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]; +} + +u16 ZipFile::misaligned16(u16* v) { + char* b = (char *) v; + return (b[0] << 8) + b[1]; +} + +int ZipFile::getFileSize() { + return _currentFile->fileSize; +} + +bool ZipFile::isDirectory() { + return _currentFile->fileSize == 0; // This is a bit wrong, but seems to work. +} + +char* ZipFile::getFile() { + return ((char *) (_currentFile)) + sizeof(*_currentFile) + _currentFile->nameLength + _currentFile->extraLength; +} + +bool ZipFile::findFile(char* search) { + changeToRoot(); + restartFile(); + + char searchName[128]; + strcpy(searchName, search); + for (int r = 0; r < (int) strlen(searchName); r++) { + if (searchName[r] == '/') searchName[r] = '\\'; + } + + if (*(searchName + strlen(searchName) - 1) == '\\') { // Directories have a terminating slash + *(searchName + strlen(searchName) - 1) = '\0'; // which we need to dispose of. + } + + + do { + char name[128]; + getFileName(name); + if (*(name + strlen(name) - 1) == '\\') { // Directories have a terminating slash + *(name + strlen(name) - 1) = '\0'; // which we need to dispose of. + } + + + if (!stricmp(name, searchName)) { +// consolePrintf("'%s'=='%s'\n", name, searchName); + return true; // Got it! + } else { +// consolePrintf("'%s'!='%s'\n", name, searchName); + } + } while (skipFile()); + + return false; // File wasn't found +} + +void ZipFile::changeToRoot() { + _directory[0] = 0; +} + +void ZipFile::changeDirectory(char* dir) { +// consolePrintf("Current dir now '%s'\n", dir); + strcpy(_directory, dir); +} + +ZipFile::~ZipFile() { + +} diff --git a/backends/platform/ds/arm9/source/zipreader.h b/backends/platform/ds/arm9/source/zipreader.h new file mode 100644 index 0000000000..4b01735111 --- /dev/null +++ b/backends/platform/ds/arm9/source/zipreader.h @@ -0,0 +1,162 @@ +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _ZIPREADER_H_ +#define _ZIPREADER_H_ +#include "stdafx.h" +#include "portdefs.h" +#define ZF_SEARCH_START 0x08000000 +#define ZF_SEARCH_END 0x09000000 +#define ZF_SEARCH_STRIDE 16 + +class ZipFile { + + struct FileHeader { + char magic[4]; // Header (0x04034B50) 0 + u16 minVersion; // Version needed to extract 4 + u16 flags; // Flags 6 + u16 comp; // Compression method 8 + u16 modTime; // Last modified file time A + u16 modDate; // Last modified file data C + u32 crc32; // CRC32 F + u32 compSize; // Compressed size + u32 fileSize; // Uncompressed file size + u16 nameLength; // Length of the filename + u16 extraLength; // Length of any extra data + } __attribute__ ((packed)); + + char* _zipFile; + char _directory[128]; + + bool _allFilesVisible; + + FileHeader* _currentFile; + +public: + ZipFile(); + ~ZipFile(); + + bool isReady(); + + // These operations set the current file + bool restartFile(); + bool skipFile(); + bool findFile(char* search); + + // These return the file's data and information + char* getFile(); + int getFileSize(); + void getFileName(char* name); + bool isDirectory(); + + // These set the current directory + void changeDirectory(char* name); + void changeToRoot(); + void setAllFilesVisible(bool state) { _allFilesVisible = state; } + + bool currentFileInFolder(); + + u16 misaligned16(u16* v); + u32 misaligned32(u32* v); + +}; + + +#endif +/* ScummVMDS - Scumm Interpreter DS Port + * Copyright (C) 2002-2004 The ScummVM project and Neil Millstone + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _ZIPREADER_H_ +#define _ZIPREADER_H_ +#include "stdafx.h" +#include "portdefs.h" +#define ZF_SEARCH_START 0x08000000 +#define ZF_SEARCH_END 0x09000000 +#define ZF_SEARCH_STRIDE 16 + +class ZipFile { + + struct FileHeader { + char magic[4]; // Header (0x04034B50) 0 + u16 minVersion; // Version needed to extract 4 + u16 flags; // Flags 6 + u16 comp; // Compression method 8 + u16 modTime; // Last modified file time A + u16 modDate; // Last modified file data C + u32 crc32; // CRC32 F + u32 compSize; // Compressed size + u32 fileSize; // Uncompressed file size + u16 nameLength; // Length of the filename + u16 extraLength; // Length of any extra data + } __attribute__ ((packed)); + + char* _zipFile; + char _directory[128]; + + bool _allFilesVisible; + + FileHeader* _currentFile; + +public: + ZipFile(); + ~ZipFile(); + + bool isReady(); + + // These operations set the current file + bool restartFile(); + bool skipFile(); + bool findFile(char* search); + + // These return the file's data and information + char* getFile(); + int getFileSize(); + void getFileName(char* name); + bool isDirectory(); + + // These set the current directory + void changeDirectory(char* name); + void changeToRoot(); + void setAllFilesVisible(bool state) { _allFilesVisible = state; } + + bool currentFileInFolder(); + + u16 misaligned16(u16* v); + u32 misaligned32(u32* v); + +}; + + +#endif -- cgit v1.2.3