diff options
Diffstat (limited to 'backends/platform')
57 files changed, 30928 insertions, 0 deletions
diff --git a/backends/platform/ds/arm7/Makefile b/backends/platform/ds/arm7/Makefile new file mode 100644 index 0000000000..349de22427 --- /dev/null +++ b/backends/platform/ds/arm7/Makefile @@ -0,0 +1,436 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +  +#--------------------------------------------------------------------------------- +# TARGET is the name of the output, if this ends with _mb generates a multiboot image +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET	:=	arm7 +BUILD		:=	build +SOURCES	:=	gfx source data   +INCLUDES	:=	include build +  +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH	:=	-mthumb-interwork + +# note: arm9tdmi isn't the correct CPU arch, but anything newer and LD +# *insists* it has a FPU or VFP, and it won't take no for an answer! +CFLAGS	:=	-g -Wall -O2\ + 		-mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\ +		-ffast-math \ +		$(ARCH) + +CFLAGS	+=	$(INCLUDE) -DARM7 + +CXXFLAGS	:=	$(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS	:=	-g $(ARCH) +LDFLAGS	:=	-g $(ARCH) -mno-fpu + +#--------------------------------------------------------------------------------- +# path to tools - this can be deleted if you set the path in windows +#--------------------------------------------------------------------------------- +# export PATH		:=	/d/dev/ds/devkitARM_r11/bin:/bin +  +#--------------------------------------------------------------------------------- +# PATH to ndslib - just make a system variable called NDSLIBPATH and be done with it +#--------------------------------------------------------------------------------- +# NDSLIBPATH	:=	/d/dev/ds/ndslib/ +  +#--------------------------------------------------------------------------------- +# the prefix on the compiler executables +#--------------------------------------------------------------------------------- +PREFIX			:=	arm-eabi- +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS	:= -lnds7 +  +  +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS	:=	$(DEVKITPRO)/libnds +  +  +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- +  +export OUTPUT	:=	$(CURDIR)/$(TARGET) +  +export VPATH	:=	$(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) +  +export CC		:=	$(PREFIX)gcc +export CXX		:=	$(PREFIX)g++ +export AR		:=	$(PREFIX)ar +export OBJCOPY	:=	$(PREFIX)objcopy +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +export LD		:=	$(CXX) +#export LD		:=	$(CC) +  +CFILES		:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES		:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +PCXFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.pcx))) +BINFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.bin))) +PALFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.pal))) +RAWFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.raw))) +MAPFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.map))) +  +export OFILES	:=	$(MAPFILES:.map=.o) $(RAWFILES:.raw=.o) $(PALFILES:.pal=.o) $(BINFILES:.bin=.o) $(PCXFILES:.pcx=.o)\ +					$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +  +export INCLUDE	:=	$(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) -I- -I$(CURDIR)/../commoninclude\ +					$(foreach dir,$(LIBDIRS),-I$(dir)/include) \ +					$(foreach dir,$(LIBDIRS),-I$(dir)/include/NDS)\ +					-I$(CURDIR)/$(BUILD)  +  +export LIBPATHS	:=	$(foreach dir,$(LIBDIRS),-L$(dir)/lib) +  +.PHONY: $(BUILD) clean +  +#--------------------------------------------------------------------------------- +$(BUILD): +	@[ -d $@ ] || mkdir -p $@ +	@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile +  +#--------------------------------------------------------------------------------- +clean: +	@echo clean ...$(TARGET) +	@rm -fr $(BUILD) *.bin +  +  +#--------------------------------------------------------------------------------- +else +  +DEPENDS	:=	$(OFILES:.o=.d) +  +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- + +$(OUTPUT).bin	:	$(OUTPUT).elf +  +$(OUTPUT).elf	:	$(OFILES) +  + + +#--------------------------------------------------------------------------------- +%.bin: %.elf +	@echo built ... $(notdir $@) +	@$(OBJCOPY) -O binary  $(TARGET).elf $@ +  +#--------------------------------------------------------------------------------- +%.elf: +	echo ELF +	@$(LD)  $(LDFLAGS) -specs=ds_arm7.specs $(OFILES) $(LIBPATHS) $(LIBS) -o $(TARGET).elf +  +  +  +#--------------------------------------------------------------------------------- +# Compile Targets for C/C++ +#--------------------------------------------------------------------------------- +  +#--------------------------------------------------------------------------------- +%.o : %.cpp +	@echo $(notdir $<) +	@echo $(CXX) -MM $(CXXFLAGS) -o $*.d $< +	@$(CXX) -MM $(CXXFLAGS) -o $*.d $< +	@$(CXX) $(CXXFLAGS) -c $< -o$@ +  +#--------------------------------------------------------------------------------- +%.o : %.c +	@echo $(notdir $<) +	@$(CC) -MM $(CFLAGS) -o $*.d $< +	@$(CC)  $(CFLAGS) -c $< -o$@ +  +#--------------------------------------------------------------------------------- +%.o : %.s +	@echo $(notdir $<) +	@$(CC) -MM $(CFLAGS) -o $*.d $< +	@$(CC)  $(ASFLAGS) -c $< -o$@ +  +define bin2o +	cp $(<) $(*).tmp +	$(OBJCOPY) -I binary -O elf32-littlearm -B arm \ +	--rename-section .data=.rodata \ +	--redefine-sym _binary_$*_tmp_start=$*\ +	--redefine-sym _binary_$*_tmp_end=$*_end\ +	--redefine-sym _binary_$*_tmp_size=$*_size\ +	$(*).tmp $(@) +	echo "extern const u8" $(*)"[];" > $(*).h +	echo "extern const u32" $(*)_size[]";" >> $(*).h +	rm $(*).tmp +endef +  +#--------------------------------------------------------------------------------- +%.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) + +  +-include $(DEPENDS) +  +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +  +#--------------------------------------------------------------------------------- +# TARGET is the name of the output, if this ends with _mb generates a multiboot image +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET	:=	arm7 +BUILD		:=	build +SOURCES	:=	gfx source data   +INCLUDES	:=	include build +  +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH	:=	-mthumb-interwork + +# note: arm9tdmi isn't the correct CPU arch, but anything newer and LD +# *insists* it has a FPU or VFP, and it won't take no for an answer! +CFLAGS	:=	-g -Wall -O2\ + 		-mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\ +		-ffast-math \ +		$(ARCH) + +CFLAGS	+=	$(INCLUDE) -DARM7 + +CXXFLAGS	:=	$(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS	:=	-g $(ARCH) +LDFLAGS	:=	-g $(ARCH) -mno-fpu + +#--------------------------------------------------------------------------------- +# path to tools - this can be deleted if you set the path in windows +#--------------------------------------------------------------------------------- +# export PATH		:=	/d/dev/ds/devkitARM_r11/bin:/bin +  +#--------------------------------------------------------------------------------- +# PATH to ndslib - just make a system variable called NDSLIBPATH and be done with it +#--------------------------------------------------------------------------------- +# NDSLIBPATH	:=	/d/dev/ds/ndslib/ +  +#--------------------------------------------------------------------------------- +# the prefix on the compiler executables +#--------------------------------------------------------------------------------- +PREFIX			:=	arm-eabi- +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS	:= -lnds7 +  +  +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS	:=	$(DEVKITPRO)/libnds +  +  +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- +  +export OUTPUT	:=	$(CURDIR)/$(TARGET) +  +export VPATH	:=	$(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) +  +export CC		:=	$(PREFIX)gcc +export CXX		:=	$(PREFIX)g++ +export AR		:=	$(PREFIX)ar +export OBJCOPY	:=	$(PREFIX)objcopy +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +export LD		:=	$(CXX) +#export LD		:=	$(CC) +  +CFILES		:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES		:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +PCXFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.pcx))) +BINFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.bin))) +PALFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.pal))) +RAWFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.raw))) +MAPFILES	:=	$(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.map))) +  +export OFILES	:=	$(MAPFILES:.map=.o) $(RAWFILES:.raw=.o) $(PALFILES:.pal=.o) $(BINFILES:.bin=.o) $(PCXFILES:.pcx=.o)\ +					$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +  +export INCLUDE	:=	$(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) -I- -I$(CURDIR)/../commoninclude\ +					$(foreach dir,$(LIBDIRS),-I$(dir)/include) \ +					$(foreach dir,$(LIBDIRS),-I$(dir)/include/NDS)\ +					-I$(CURDIR)/$(BUILD)  +  +export LIBPATHS	:=	$(foreach dir,$(LIBDIRS),-L$(dir)/lib) +  +.PHONY: $(BUILD) clean +  +#--------------------------------------------------------------------------------- +$(BUILD): +	@[ -d $@ ] || mkdir -p $@ +	@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile +  +#--------------------------------------------------------------------------------- +clean: +	@echo clean ...$(TARGET) +	@rm -fr $(BUILD) *.bin +  +  +#--------------------------------------------------------------------------------- +else +  +DEPENDS	:=	$(OFILES:.o=.d) +  +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- + +$(OUTPUT).bin	:	$(OUTPUT).elf +  +$(OUTPUT).elf	:	$(OFILES) +  + + +#--------------------------------------------------------------------------------- +%.bin: %.elf +	@echo built ... $(notdir $@) +	@$(OBJCOPY) -O binary  $(TARGET).elf $@ +  +#--------------------------------------------------------------------------------- +%.elf: +	echo ELF +	@$(LD)  $(LDFLAGS) -specs=ds_arm7.specs $(OFILES) $(LIBPATHS) $(LIBS) -o $(TARGET).elf +  +  +  +#--------------------------------------------------------------------------------- +# Compile Targets for C/C++ +#--------------------------------------------------------------------------------- +  +#--------------------------------------------------------------------------------- +%.o : %.cpp +	@echo $(notdir $<) +	@echo $(CXX) -MM $(CXXFLAGS) -o $*.d $< +	@$(CXX) -MM $(CXXFLAGS) -o $*.d $< +	@$(CXX) $(CXXFLAGS) -c $< -o$@ +  +#--------------------------------------------------------------------------------- +%.o : %.c +	@echo $(notdir $<) +	@$(CC) -MM $(CFLAGS) -o $*.d $< +	@$(CC)  $(CFLAGS) -c $< -o$@ +  +#--------------------------------------------------------------------------------- +%.o : %.s +	@echo $(notdir $<) +	@$(CC) -MM $(CFLAGS) -o $*.d $< +	@$(CC)  $(ASFLAGS) -c $< -o$@ +  +define bin2o +	cp $(<) $(*).tmp +	$(OBJCOPY) -I binary -O elf32-littlearm -B arm \ +	--rename-section .data=.rodata \ +	--redefine-sym _binary_$*_tmp_start=$*\ +	--redefine-sym _binary_$*_tmp_end=$*_end\ +	--redefine-sym _binary_$*_tmp_size=$*_size\ +	$(*).tmp $(@) +	echo "extern const u8" $(*)"[];" > $(*).h +	echo "extern const u32" $(*)_size[]";" >> $(*).h +	rm $(*).tmp +endef +  +#--------------------------------------------------------------------------------- +%.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) + +  +-include $(DEPENDS) +  +#--------------------------------------------------------------------------------------- +endif +#---------------------------------------------------------------------------------------
\ No newline at end of file diff --git a/backends/platform/ds/arm7/source/main.cpp b/backends/platform/ds/arm7/source/main.cpp new file mode 100644 index 0000000000..f5518be647 --- /dev/null +++ b/backends/platform/ds/arm7/source/main.cpp @@ -0,0 +1,1130 @@ +/* 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. + * + * $Header: /cvsroot/scummvm/scummvm/backends/fs/fs.cpp,v 1.3.2.1 2004/12/18 02:33:52 fingolfin Exp $ + */ + +////////////////////////////////////////////////////////////////////// +// Simple ARM7 stub (sends RTC, TSC, and X/Y data to the ARM 9) +// -- joat +// -- modified by Darkain and others +////////////////////////////////////////////////////////////////////// + +  +#include <nds.h> +  +#include <bios.h> +#include <arm7/touch.h> +#include <arm7/clock.h> +#include <stdlib.h> +#include <string.h> + +#include <nds/scummvm_ipc.h> +////////////////////////////////////////////////////////////////////// + + +#define TOUCH_CAL_X1 (*(vs16*)0x027FFCD8) +#define TOUCH_CAL_Y1 (*(vs16*)0x027FFCDA) +#define TOUCH_CAL_X2 (*(vs16*)0x027FFCDE) +#define TOUCH_CAL_Y2 (*(vs16*)0x027FFCE0) +#define SCREEN_WIDTH    256 +#define SCREEN_HEIGHT   192 +s32 TOUCH_WIDTH  = TOUCH_CAL_X2 - TOUCH_CAL_X1; +s32 TOUCH_HEIGHT = TOUCH_CAL_Y2 - TOUCH_CAL_Y1; +s32 TOUCH_OFFSET_X = ( ((SCREEN_WIDTH -60) * TOUCH_CAL_X1) / TOUCH_WIDTH  ) - 28; +s32 TOUCH_OFFSET_Y = ( ((SCREEN_HEIGHT-60) * TOUCH_CAL_Y1) / TOUCH_HEIGHT ) - 28; + +vu8* soundData; + +vu8* soundBuffer; +vu8* arm9Buffer; +bool soundFilled[4]; + +int playingSection; + +bool needSleep = false; +int temp; + +int adpcmBufferNum = 0; + +// those are pixel positions of the two points you click when calibrating +#define TOUCH_CNTRL_X1   (*(vu8*)0x027FFCDC) +#define TOUCH_CNTRL_Y1   (*(vu8*)0x027FFCDD) +#define TOUCH_CNTRL_X2   (*(vu8*)0x027FFCE2) +#define TOUCH_CNTRL_Y2   (*(vu8*)0x027FFCE3) + + +////////////////////////////////////////////////////////////////////// + +/* +void startSound(int sampleRate, const void* data, uint32 bytes, u8 channel=0, u8 vol=0x7F,  u8 pan=63, u8 format=0) { +  SCHANNEL_TIMER(channel)  = SOUND_FREQ(sampleRate); +  SCHANNEL_SOURCE(channel) = (uint32)data; +  SCHANNEL_LENGTH(channel) = bytes; +  SCHANNEL_CR(channel)     = SOUND_ENABLE | SOUND_ONE_SHOT | SOUND_VOL(vol) | SOUND_PAN(pan) | (format==1?SOUND_8BIT:SOUND_16BIT); +} + + +s8 getFreeSoundChannel() { +  for (int i=0; i<16; i++) { +    if ( (SCHANNEL_CR(i) & SOUND_ENABLE) == 0 ) return i; +  } +  return -1; +} +*/ + + +s8 getFreeSoundChannel() { +//  return 0; +  for (int i=0; i<16; i++) { +    if ( (SCHANNEL_CR(i) & SCHANNEL_ENABLE) == 0 ) return i; +  } +  return -1; +} + +void startSound(int sampleRate, const void* data, uint32 bytes, u8 channel=0, u8 vol=0x7F,  u8 pan=63, u8 format=0) { +  REG_IME = IME_DISABLE; + +  channel = getFreeSoundChannel(); +/*  if (format == 2) { +	channel = 1; +  } else { +	channel = 0; +  }*/ +   +  if (channel > 1) channel = 1; +   +  bytes &= ~7;		// Multiple of 4 bytes! +//  bytes += 4; +   +  SCHANNEL_CR(channel) = 0; +  SCHANNEL_TIMER(channel)  = SOUND_FREQ(sampleRate); +  SCHANNEL_SOURCE(channel) = ((uint32) (data)); +  SCHANNEL_LENGTH(channel) = ((bytes & 0x7FFFFFFF) >> 2); +  SCHANNEL_REPEAT_POINT(channel) = 0; + +  SCHANNEL_CR(channel + 2) = 0; +  SCHANNEL_TIMER(channel + 2)  = SOUND_FREQ(sampleRate); +  SCHANNEL_SOURCE(channel + 2) = ((uint32) (data)); +  SCHANNEL_LENGTH(channel + 2) = ((bytes & 0x7FFFFFFF) >> 2); +  SCHANNEL_REPEAT_POINT(channel + 2) = 0; +   +  uint32 flags = SCHANNEL_ENABLE | SOUND_VOL(vol) | SOUND_PAN(pan); + +  switch (format) { +	case 1: { +		flags |= SOUND_8BIT; +		flags |= SOUND_REPEAT;// | (1 << 15); +		break; +	} +	 +	case 0: { +		flags |= SOUND_16BIT; +		flags |= SOUND_REPEAT;// | (1 << 15); +		break; +	} +	 +	case 2: { +		flags |= SOUND_FORMAT_ADPCM; +		flags |= SOUND_ONE_SHOT;// | (1 << 15); +		 +		SCHANNEL_SOURCE(channel) = (unsigned int) IPC->adpcm.buffer[0]; +		//bytes += 32; +		SCHANNEL_LENGTH(channel) = (((bytes + 4) & 0x7FFFFFFF) >> 2); +		 +		SCHANNEL_CR(channel + 1) = 0; +		SCHANNEL_SOURCE(channel + 1) = (unsigned int) IPC->adpcm.buffer[0]; +		SCHANNEL_LENGTH(channel + 1) = (((bytes + 4) & 0x7FFFFFFF) >> 2); +		SCHANNEL_TIMER(channel + 1) = SOUND_FREQ(sampleRate); +		SCHANNEL_REPEAT_POINT(channel + 1) = 0; +		SCHANNEL_CR(channel + 1) = flags; +		temp = bytes; +		adpcmBufferNum = 0; +		break; +	} +  } +	 +   +//  if (bytes & 0x80000000) { +//    flags |= SOUND_REPEAT; +//  } else { +//  } + +   +    +   +  soundData = (vu8* ) data; + +  SCHANNEL_CR(channel)     = flags; +  SCHANNEL_CR(channel + 2)     = flags; + + + +  if (channel == 0) { +	for (volatile int i = 0; i < 16384 * 2; i++) { +		// Delay loop - this makes everything stay in sync! +	} +		 +	TIMER0_CR = 0; +	TIMER0_DATA = SOUND_FREQ(sampleRate) * 2; +	TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1; +		 +	TIMER1_CR = 0; +	TIMER1_DATA = 65536 - ((bytes & 0x7FFFFFFF) >> 3);		// Trigger four times during the length of the buffer +	TIMER1_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE; + +    playingSection = 0; +  } else { +	for (volatile int i = 0; i < 16384 * 2; i++) { +		// Delay loop - this makes everything stay in sync! +	} +		 +	TIMER2_CR = 0; +	TIMER2_DATA = SOUND_FREQ(sampleRate) * 2; +	TIMER2_CR = TIMER_ENABLE | TIMER_DIV_1; +		 +	TIMER3_CR = 0; +	TIMER3_DATA = 65536 - ((bytes & 0x7FFFFFFF) >> 3);		// Trigger four times during the length of the buffer +	TIMER3_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE; +	 +	for (int r = 0; r < 4; r++) { +//		IPC->streamFillNeeded[r] = true; +	} +	 +	IPC->streamPlayingSection = 0; +  } + + + +//  IPC->fillSoundFirstHalf = true; +//  IPC->fillSoundSecondHalf = true; +//  soundFirstHalf = true; +   +  REG_IME = IME_ENABLE; +} + +void stopSound(int chan) { + SCHANNEL_CR(chan) = 0; +} + +void DummyHandler() { +	REG_IF = REG_IF; +} + +uint16 powerManagerWrite(uint32 command, u32 data, bool enable) { + +  uint16 result; +  SerialWaitBusy(); + +  // Write the command and wait for it to complete +  REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz | (1 << 11); +  REG_SPIDATA = command | 0x80; +  SerialWaitBusy(); + +  // Write the second command and clock in the data +  REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz; +  REG_SPIDATA = 0; +  SerialWaitBusy(); +   +  result = REG_SPIDATA & 0xFF; + + + +  // Write the command and wait for it to complete +  REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz | (1 << 11); +  REG_SPIDATA = command; +  SerialWaitBusy(); + +  // Write the second command and clock in the data +  REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz; +  REG_SPIDATA = enable? (result | data): (result & ~data); +  SerialWaitBusy(); +} + +/* +void performSleep() { + +  powerManagerWrite(0, 0x30, true); + +  // Here, I set up a dummy interrupt handler, then trigger all interrupts. +  // These are just aknowledged by the handler without doing anything else. +  // Why?  Because without it the sleep mode will only happen once, and then +  // never again.  I got the idea from reading the MoonShell source. +  IME = 0; +  u32 irq = (u32) IRQ_HANDLER; +  IRQ_HANDLER = DummyHandler; +  IF = ~0; +  IME = 1; +   +   +  // Now save which interrupts are enabled, then set only the screens unfolding +  // interrupt to be enabled, so that the first interrupt that happens is the +  // one I want. +  int saveInts = IE; +   +   +   +  IE = IRQ_TIMER0;		// Screens unfolding interrupt + +  // Now call the sleep function in the bios +  bool b; +  do { +    TIMER0_CR = 0; +	TIMER0_DATA = TIMER_FREQ(20); +	TIMER0_CR = TIMER_ENABLE | TIMER_DIV_64; +	 +	swiDelay(100); +   +	swiSleep(); + +	swiDelay(100); +	 +	powerManagerWrite(0, 0x30, b = !b);	 +  } while (!(TIMER0_CR & TIMER_ENABLE)); +   +  TIMER0_CR = 0; + +  // We're back from sleep, now restore the interrupt state and IRQ handler   +  IRQ_HANDLER = (void (*)()) irq; +  IE = saveInts; +  IF = ~0; +  IME = 1; + +   +   +  powerManagerWrite(0, 0x30, false); +} +*/ + + +void performSleep() { +  powerManagerWrite(0, 0x30, true); + +  IPC->performArm9SleepMode = true;	// Tell ARM9 to sleep +   +//  u32 irq = (u32) IRQ_HANDLER; +//  IRQ_HANDLER = DummyHandler; +//  POWER_CR &= ~POWER_SOUND; +   +//  int saveInts = REG_IE; +//  REG_IE = (1 << 22) | IRQ_VBLANK;		// Lid open +//  *((u32*) (0x0380FFF8)) = *((u32*) (0x0380FFF8)) | (REG_IE & REG_IF); +//  VBLANK_INTR_WAIT_FLAGS = IRQ_VBLANK; + +   +  int r = 0; +  while ((REG_KEYXY & (1 << 7))) {		// Wait for lid to open +	swiDelay(1000000); +	r++; +  } +   +//  IRQ_HANDLER = (void (*)()) irq; +  IPC->performArm9SleepMode = false;	// Tell ARM9 to wake up +//  REG_IE = saveInts; +   +//  POWER_CR |= POWER_SOUND; +  +  powerManagerWrite(0, 0x30, false); +} + +////////////////////////////////////////////////////////////////////// + + +void InterruptHandler(void) { +  static int heartbeat = 0; + +  if (REG_IF & IRQ_TIMER1) { +	 +	IPC->fillNeeded[playingSection] = true; +	soundFilled[playingSection] = false; +	   +	if (playingSection == 3) { +//		IME = IME_DISABLED; +		 +	//	while (SCHANNEL_CR(0) & SCHANNEL_ENABLE) { +	//	} +//		SCHANNEL_CR(0) &= ~SCHANNEL_ENABLE;	 +		 +//		SCHANNEL_CR(0) |= SCHANNEL_ENABLE; +//		TIMER1_CR = 0; +//		TIMER1_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE; +		 +		playingSection = 0; +		 +//		IME = IME_ENABLED; +	} else { +		playingSection++; +	} +	 +	IPC->playingSection = playingSection; +	 +/*	for (int r = 0; r < 4; r++) { +		//if ((!soundFilled[r]) && (!IPC->fillNeeded[playingSection])) { +			memcpy((void *) (soundBuffer + (r * 1024)), (void *) (arm9Buffer + (r * 1024)), 1024); + +			vu16* p = (vu16 *) (soundBuffer); +			//for (int t = 0; t < 2048; t++) { +		//		*(p + t) = (t & 1)? 0xF000: 0x0000; +			//} +			soundFilled[r] = true; +		//} +	}*/ +	 + +    REG_IF = IRQ_TIMER1; +  } +   + +  if (REG_IF & IRQ_TIMER3) { +	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 +		 +	IPC->streamFillNeeded[IPC->streamPlayingSection] = true; +	   +	if (IPC->streamPlayingSection == 3) { +		IPC->streamPlayingSection = 0; +	} else { +		IPC->streamPlayingSection++; +	} +	 +    REG_IF = IRQ_TIMER3; +	 +	IPC->adpcm.semaphore = false; +  } + + +//  IPC->performArm9SleepMode = false; + +	// precalculate some values +//  static int16 TOUCH_WIDTH  = TOUCH_CAL_X2 - TOUCH_CAL_X1; +//  static int16 TOUCH_HEIGHT = TOUCH_CAL_Y2 - TOUCH_CAL_Y1; +//  static int16 CNTRL_WIDTH  = TOUCH_CNTRL_X2 - (TOUCH_CNTRL_X1 - 8); +//  static int16 CNTRL_HEIGHT = TOUCH_CNTRL_Y2 - (TOUCH_CNTRL_Y1 - 8); +   +   +   +  +  +  if (REG_IF & IRQ_VBLANK) { +    uint16 but=0, x=0, y=0, xpx=0, ypx=0, z1=0, z2=0, batt=0, aux=0; +    int t1=0, t2=0; +    uint32 temp=0; +    uint8 ct[sizeof(IPC->curtime)]; + +     +    // Update the heartbeat +    heartbeat++; +  +    // Read the X/Y buttons and the /PENIRQ line +    but = REG_KEYXY; +    if (!(but & 0x40)) { +      // Read the touch screen +	  touchPosition p = touchReadXY(); + +//      x = touchRead(TSC_MEASURE_X); + //     y = touchRead(TSC_MEASURE_Y); +	   +	  x = p.x; +	  y = p.y; +	   +	  xpx = p.px; +	  ypx = p.py; +	   +//      xpx = ( ((SCREEN_WIDTH -60) * x) / TOUCH_WIDTH  ) - TOUCH_OFFSET_X; +  //    ypx = ( ((SCREEN_HEIGHT-60) * y) / TOUCH_HEIGHT ) - TOUCH_OFFSET_Y; +	   +//	  xpx = (IPC->touchX - (int16) TOUCH_CAL_X1) * CNTRL_WIDTH  / TOUCH_WIDTH  + (int16) (TOUCH_CNTRL_X1 - 8); +	//  ypx = (IPC->touchY - (int16) TOUCH_CAL_Y1) * CNTRL_HEIGHT / TOUCH_HEIGHT + (int16) (TOUCH_CNTRL_Y1 - 8);  + +	   +      z1 = touchRead(TSC_MEASURE_Z1); +      z2 = touchRead(TSC_MEASURE_Z2); +    } +	 +    if (but & (1 << 7)) {		// Check if screen is folded +	  needSleep = true; +	} +	 + +    batt = touchRead(TSC_MEASURE_BATTERY); +    aux  = touchRead(TSC_MEASURE_AUX); + +    // Read the time +    rtcGetTime((uint8 *)ct); +    BCDToInteger((uint8 *)&(ct[1]), 7); +  +    // Read the temperature +    temp = touchReadTemperature(&t1, &t2); +  +    // Update the IPC struct +    IPC->heartbeat = heartbeat; +    IPC->buttons   = but; +    IPC->touchX    = x; +    IPC->touchY    = y; +    IPC->touchXpx  = xpx; +    IPC->touchYpx  = ypx; +    IPC->touchZ1   = z1; +    IPC->touchZ2   = z2; +    IPC->battery   = batt; +    IPC->aux       = aux; + +    for(u32 i=0; i<sizeof(ct); i++) { +      IPC->curtime[i] = ct[i]; +    } + +    IPC->temperature = temp; +    IPC->tdiode1 = t1; +    IPC->tdiode2 = t2; + +    + +	//sound code  :) +    TransferSound *snd = IPC->soundData; +    IPC->soundData = 0; +    if (snd) { +      for (int i=0; i<snd->count; i++) { +        s8 chan = getFreeSoundChannel(); +		if (snd->data[i].rate > 0) { +			if (chan >= 0) { +			  startSound(snd->data[i].rate, snd->data[i].data, snd->data[i].len, chan, snd->data[i].vol, snd->data[i].pan, snd->data[i].format); +			} +		} else { +			stopSound(-snd->data[i].rate); +		} +      } +    }  + +    REG_IF = IRQ_VBLANK; +  } +  +} +  + +////////////////////////////////////////////////////////////////////// +  + +int main(int argc, char ** argv) { +  // Reset the clock if needed +  rtcReset(); + +  //enable sound +  powerON(POWER_SOUND); +  SOUND_CR = SOUND_ENABLE | SOUND_VOL(0x7F); +  IPC->soundData = 0; +  IPC->reset = false; +   +   +  for (int r = 0; r < 8; r++) { +	IPC->adpcm.arm7Buffer[r] = (u8 *) malloc(512); +  } +   +  for (int r = 0; r < 4; r++) { +	soundFilled[r] = false; +  } + +  +  // Set up the interrupt handler +  REG_IME = 0; +  IRQ_HANDLER = &InterruptHandler; +  REG_IE = IRQ_VBLANK | IRQ_TIMER1 | IRQ_TIMER3; +  REG_IF = ~0; +  DISP_SR = DISP_VBLANK_IRQ; +  REG_IME = 1; +   +   + +  // Keep the ARM7 out of main RAM +  while (1) { +	if (needSleep) { +		performSleep(); +		needSleep = false; +	} +//	if (IPC->reset) { +//		swiSoftReset(); +//	} +  } +  return 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. + * + * $Header: /cvsroot/scummvm/scummvm/backends/fs/fs.cpp,v 1.3.2.1 2004/12/18 02:33:52 fingolfin Exp $ + */ + +////////////////////////////////////////////////////////////////////// +// Simple ARM7 stub (sends RTC, TSC, and X/Y data to the ARM 9) +// -- joat +// -- modified by Darkain and others +////////////////////////////////////////////////////////////////////// + +  +#include <nds.h> +  +#include <bios.h> +#include <arm7/touch.h> +#include <arm7/clock.h> +#include <stdlib.h> +#include <string.h> + +#include <nds/scummvm_ipc.h> +////////////////////////////////////////////////////////////////////// + + +#define TOUCH_CAL_X1 (*(vs16*)0x027FFCD8) +#define TOUCH_CAL_Y1 (*(vs16*)0x027FFCDA) +#define TOUCH_CAL_X2 (*(vs16*)0x027FFCDE) +#define TOUCH_CAL_Y2 (*(vs16*)0x027FFCE0) +#define SCREEN_WIDTH    256 +#define SCREEN_HEIGHT   192 +s32 TOUCH_WIDTH  = TOUCH_CAL_X2 - TOUCH_CAL_X1; +s32 TOUCH_HEIGHT = TOUCH_CAL_Y2 - TOUCH_CAL_Y1; +s32 TOUCH_OFFSET_X = ( ((SCREEN_WIDTH -60) * TOUCH_CAL_X1) / TOUCH_WIDTH  ) - 28; +s32 TOUCH_OFFSET_Y = ( ((SCREEN_HEIGHT-60) * TOUCH_CAL_Y1) / TOUCH_HEIGHT ) - 28; + +vu8* soundData; + +vu8* soundBuffer; +vu8* arm9Buffer; +bool soundFilled[4]; + +int playingSection; + +bool needSleep = false; +int temp; + +int adpcmBufferNum = 0; + +// those are pixel positions of the two points you click when calibrating +#define TOUCH_CNTRL_X1   (*(vu8*)0x027FFCDC) +#define TOUCH_CNTRL_Y1   (*(vu8*)0x027FFCDD) +#define TOUCH_CNTRL_X2   (*(vu8*)0x027FFCE2) +#define TOUCH_CNTRL_Y2   (*(vu8*)0x027FFCE3) + + +////////////////////////////////////////////////////////////////////// + +/* +void startSound(int sampleRate, const void* data, uint32 bytes, u8 channel=0, u8 vol=0x7F,  u8 pan=63, u8 format=0) { +  SCHANNEL_TIMER(channel)  = SOUND_FREQ(sampleRate); +  SCHANNEL_SOURCE(channel) = (uint32)data; +  SCHANNEL_LENGTH(channel) = bytes; +  SCHANNEL_CR(channel)     = SOUND_ENABLE | SOUND_ONE_SHOT | SOUND_VOL(vol) | SOUND_PAN(pan) | (format==1?SOUND_8BIT:SOUND_16BIT); +} + + +s8 getFreeSoundChannel() { +  for (int i=0; i<16; i++) { +    if ( (SCHANNEL_CR(i) & SOUND_ENABLE) == 0 ) return i; +  } +  return -1; +} +*/ + + +s8 getFreeSoundChannel() { +//  return 0; +  for (int i=0; i<16; i++) { +    if ( (SCHANNEL_CR(i) & SCHANNEL_ENABLE) == 0 ) return i; +  } +  return -1; +} + +void startSound(int sampleRate, const void* data, uint32 bytes, u8 channel=0, u8 vol=0x7F,  u8 pan=63, u8 format=0) { +  REG_IME = IME_DISABLE; + +  channel = getFreeSoundChannel(); +/*  if (format == 2) { +	channel = 1; +  } else { +	channel = 0; +  }*/ +   +  if (channel > 1) channel = 1; +   +  bytes &= ~7;		// Multiple of 4 bytes! +//  bytes += 4; +   +  SCHANNEL_CR(channel) = 0; +  SCHANNEL_TIMER(channel)  = SOUND_FREQ(sampleRate); +  SCHANNEL_SOURCE(channel) = ((uint32) (data)); +  SCHANNEL_LENGTH(channel) = ((bytes & 0x7FFFFFFF) >> 2); +  SCHANNEL_REPEAT_POINT(channel) = 0; + +  SCHANNEL_CR(channel + 2) = 0; +  SCHANNEL_TIMER(channel + 2)  = SOUND_FREQ(sampleRate); +  SCHANNEL_SOURCE(channel + 2) = ((uint32) (data)); +  SCHANNEL_LENGTH(channel + 2) = ((bytes & 0x7FFFFFFF) >> 2); +  SCHANNEL_REPEAT_POINT(channel + 2) = 0; +   +  uint32 flags = SCHANNEL_ENABLE | SOUND_VOL(vol) | SOUND_PAN(pan); + +  switch (format) { +	case 1: { +		flags |= SOUND_8BIT; +		flags |= SOUND_REPEAT;// | (1 << 15); +		break; +	} +	 +	case 0: { +		flags |= SOUND_16BIT; +		flags |= SOUND_REPEAT;// | (1 << 15); +		break; +	} +	 +	case 2: { +		flags |= SOUND_FORMAT_ADPCM; +		flags |= SOUND_ONE_SHOT;// | (1 << 15); +		 +		SCHANNEL_SOURCE(channel) = (unsigned int) IPC->adpcm.buffer[0]; +		//bytes += 32; +		SCHANNEL_LENGTH(channel) = (((bytes + 4) & 0x7FFFFFFF) >> 2); +		 +		SCHANNEL_CR(channel + 1) = 0; +		SCHANNEL_SOURCE(channel + 1) = (unsigned int) IPC->adpcm.buffer[0]; +		SCHANNEL_LENGTH(channel + 1) = (((bytes + 4) & 0x7FFFFFFF) >> 2); +		SCHANNEL_TIMER(channel + 1) = SOUND_FREQ(sampleRate); +		SCHANNEL_REPEAT_POINT(channel + 1) = 0; +		SCHANNEL_CR(channel + 1) = flags; +		temp = bytes; +		adpcmBufferNum = 0; +		break; +	} +  } +	 +   +//  if (bytes & 0x80000000) { +//    flags |= SOUND_REPEAT; +//  } else { +//  } + +   +    +   +  soundData = (vu8* ) data; + +  SCHANNEL_CR(channel)     = flags; +  SCHANNEL_CR(channel + 2)     = flags; + + + +  if (channel == 0) { +	for (volatile int i = 0; i < 16384 * 2; i++) { +		// Delay loop - this makes everything stay in sync! +	} +		 +	TIMER0_CR = 0; +	TIMER0_DATA = SOUND_FREQ(sampleRate) * 2; +	TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1; +		 +	TIMER1_CR = 0; +	TIMER1_DATA = 65536 - ((bytes & 0x7FFFFFFF) >> 3);		// Trigger four times during the length of the buffer +	TIMER1_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE; + +    playingSection = 0; +  } else { +	for (volatile int i = 0; i < 16384 * 2; i++) { +		// Delay loop - this makes everything stay in sync! +	} +		 +	TIMER2_CR = 0; +	TIMER2_DATA = SOUND_FREQ(sampleRate) * 2; +	TIMER2_CR = TIMER_ENABLE | TIMER_DIV_1; +		 +	TIMER3_CR = 0; +	TIMER3_DATA = 65536 - ((bytes & 0x7FFFFFFF) >> 3);		// Trigger four times during the length of the buffer +	TIMER3_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE; +	 +	for (int r = 0; r < 4; r++) { +//		IPC->streamFillNeeded[r] = true; +	} +	 +	IPC->streamPlayingSection = 0; +  } + + + +//  IPC->fillSoundFirstHalf = true; +//  IPC->fillSoundSecondHalf = true; +//  soundFirstHalf = true; +   +  REG_IME = IME_ENABLE; +} + +void stopSound(int chan) { + SCHANNEL_CR(chan) = 0; +} + +void DummyHandler() { +	REG_IF = REG_IF; +} + +uint16 powerManagerWrite(uint32 command, u32 data, bool enable) { + +  uint16 result; +  SerialWaitBusy(); + +  // Write the command and wait for it to complete +  REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz | (1 << 11); +  REG_SPIDATA = command | 0x80; +  SerialWaitBusy(); + +  // Write the second command and clock in the data +  REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz; +  REG_SPIDATA = 0; +  SerialWaitBusy(); +   +  result = REG_SPIDATA & 0xFF; + + + +  // Write the command and wait for it to complete +  REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz | (1 << 11); +  REG_SPIDATA = command; +  SerialWaitBusy(); + +  // Write the second command and clock in the data +  REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz; +  REG_SPIDATA = enable? (result | data): (result & ~data); +  SerialWaitBusy(); +} + +/* +void performSleep() { + +  powerManagerWrite(0, 0x30, true); + +  // Here, I set up a dummy interrupt handler, then trigger all interrupts. +  // These are just aknowledged by the handler without doing anything else. +  // Why?  Because without it the sleep mode will only happen once, and then +  // never again.  I got the idea from reading the MoonShell source. +  IME = 0; +  u32 irq = (u32) IRQ_HANDLER; +  IRQ_HANDLER = DummyHandler; +  IF = ~0; +  IME = 1; +   +   +  // Now save which interrupts are enabled, then set only the screens unfolding +  // interrupt to be enabled, so that the first interrupt that happens is the +  // one I want. +  int saveInts = IE; +   +   +   +  IE = IRQ_TIMER0;		// Screens unfolding interrupt + +  // Now call the sleep function in the bios +  bool b; +  do { +    TIMER0_CR = 0; +	TIMER0_DATA = TIMER_FREQ(20); +	TIMER0_CR = TIMER_ENABLE | TIMER_DIV_64; +	 +	swiDelay(100); +   +	swiSleep(); + +	swiDelay(100); +	 +	powerManagerWrite(0, 0x30, b = !b);	 +  } while (!(TIMER0_CR & TIMER_ENABLE)); +   +  TIMER0_CR = 0; + +  // We're back from sleep, now restore the interrupt state and IRQ handler   +  IRQ_HANDLER = (void (*)()) irq; +  IE = saveInts; +  IF = ~0; +  IME = 1; + +   +   +  powerManagerWrite(0, 0x30, false); +} +*/ + + +void performSleep() { +  powerManagerWrite(0, 0x30, true); + +  IPC->performArm9SleepMode = true;	// Tell ARM9 to sleep +   +//  u32 irq = (u32) IRQ_HANDLER; +//  IRQ_HANDLER = DummyHandler; +//  POWER_CR &= ~POWER_SOUND; +   +//  int saveInts = REG_IE; +//  REG_IE = (1 << 22) | IRQ_VBLANK;		// Lid open +//  *((u32*) (0x0380FFF8)) = *((u32*) (0x0380FFF8)) | (REG_IE & REG_IF); +//  VBLANK_INTR_WAIT_FLAGS = IRQ_VBLANK; + +   +  int r = 0; +  while ((REG_KEYXY & (1 << 7))) {		// Wait for lid to open +	swiDelay(1000000); +	r++; +  } +   +//  IRQ_HANDLER = (void (*)()) irq; +  IPC->performArm9SleepMode = false;	// Tell ARM9 to wake up +//  REG_IE = saveInts; +   +//  POWER_CR |= POWER_SOUND; +  +  powerManagerWrite(0, 0x30, false); +} + +////////////////////////////////////////////////////////////////////// + + +void InterruptHandler(void) { +  static int heartbeat = 0; + +  if (REG_IF & IRQ_TIMER1) { +	 +	IPC->fillNeeded[playingSection] = true; +	soundFilled[playingSection] = false; +	   +	if (playingSection == 3) { +//		IME = IME_DISABLED; +		 +	//	while (SCHANNEL_CR(0) & SCHANNEL_ENABLE) { +	//	} +//		SCHANNEL_CR(0) &= ~SCHANNEL_ENABLE;	 +		 +//		SCHANNEL_CR(0) |= SCHANNEL_ENABLE; +//		TIMER1_CR = 0; +//		TIMER1_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE; +		 +		playingSection = 0; +		 +//		IME = IME_ENABLED; +	} else { +		playingSection++; +	} +	 +	IPC->playingSection = playingSection; +	 +/*	for (int r = 0; r < 4; r++) { +		//if ((!soundFilled[r]) && (!IPC->fillNeeded[playingSection])) { +			memcpy((void *) (soundBuffer + (r * 1024)), (void *) (arm9Buffer + (r * 1024)), 1024); + +			vu16* p = (vu16 *) (soundBuffer); +			//for (int t = 0; t < 2048; t++) { +		//		*(p + t) = (t & 1)? 0xF000: 0x0000; +			//} +			soundFilled[r] = true; +		//} +	}*/ +	 + +    REG_IF = IRQ_TIMER1; +  } +   + +  if (REG_IF & IRQ_TIMER3) { +	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 +		 +	IPC->streamFillNeeded[IPC->streamPlayingSection] = true; +	   +	if (IPC->streamPlayingSection == 3) { +		IPC->streamPlayingSection = 0; +	} else { +		IPC->streamPlayingSection++; +	} +	 +    REG_IF = IRQ_TIMER3; +	 +	IPC->adpcm.semaphore = false; +  } + + +//  IPC->performArm9SleepMode = false; + +	// precalculate some values +//  static int16 TOUCH_WIDTH  = TOUCH_CAL_X2 - TOUCH_CAL_X1; +//  static int16 TOUCH_HEIGHT = TOUCH_CAL_Y2 - TOUCH_CAL_Y1; +//  static int16 CNTRL_WIDTH  = TOUCH_CNTRL_X2 - (TOUCH_CNTRL_X1 - 8); +//  static int16 CNTRL_HEIGHT = TOUCH_CNTRL_Y2 - (TOUCH_CNTRL_Y1 - 8); +   +   +   +  +  +  if (REG_IF & IRQ_VBLANK) { +    uint16 but=0, x=0, y=0, xpx=0, ypx=0, z1=0, z2=0, batt=0, aux=0; +    int t1=0, t2=0; +    uint32 temp=0; +    uint8 ct[sizeof(IPC->curtime)]; + +     +    // Update the heartbeat +    heartbeat++; +  +    // Read the X/Y buttons and the /PENIRQ line +    but = REG_KEYXY; +    if (!(but & 0x40)) { +      // Read the touch screen +	  touchPosition p = touchReadXY(); + +//      x = touchRead(TSC_MEASURE_X); + //     y = touchRead(TSC_MEASURE_Y); +	   +	  x = p.x; +	  y = p.y; +	   +	  xpx = p.px; +	  ypx = p.py; +	   +//      xpx = ( ((SCREEN_WIDTH -60) * x) / TOUCH_WIDTH  ) - TOUCH_OFFSET_X; +  //    ypx = ( ((SCREEN_HEIGHT-60) * y) / TOUCH_HEIGHT ) - TOUCH_OFFSET_Y; +	   +//	  xpx = (IPC->touchX - (int16) TOUCH_CAL_X1) * CNTRL_WIDTH  / TOUCH_WIDTH  + (int16) (TOUCH_CNTRL_X1 - 8); +	//  ypx = (IPC->touchY - (int16) TOUCH_CAL_Y1) * CNTRL_HEIGHT / TOUCH_HEIGHT + (int16) (TOUCH_CNTRL_Y1 - 8);  + +	   +      z1 = touchRead(TSC_MEASURE_Z1); +      z2 = touchRead(TSC_MEASURE_Z2); +    } +	 +    if (but & (1 << 7)) {		// Check if screen is folded +	  needSleep = true; +	} +	 + +    batt = touchRead(TSC_MEASURE_BATTERY); +    aux  = touchRead(TSC_MEASURE_AUX); + +    // Read the time +    rtcGetTime((uint8 *)ct); +    BCDToInteger((uint8 *)&(ct[1]), 7); +  +    // Read the temperature +    temp = touchReadTemperature(&t1, &t2); +  +    // Update the IPC struct +    IPC->heartbeat = heartbeat; +    IPC->buttons   = but; +    IPC->touchX    = x; +    IPC->touchY    = y; +    IPC->touchXpx  = xpx; +    IPC->touchYpx  = ypx; +    IPC->touchZ1   = z1; +    IPC->touchZ2   = z2; +    IPC->battery   = batt; +    IPC->aux       = aux; + +    for(u32 i=0; i<sizeof(ct); i++) { +      IPC->curtime[i] = ct[i]; +    } + +    IPC->temperature = temp; +    IPC->tdiode1 = t1; +    IPC->tdiode2 = t2; + +    + +	//sound code  :) +    TransferSound *snd = IPC->soundData; +    IPC->soundData = 0; +    if (snd) { +      for (int i=0; i<snd->count; i++) { +        s8 chan = getFreeSoundChannel(); +		if (snd->data[i].rate > 0) { +			if (chan >= 0) { +			  startSound(snd->data[i].rate, snd->data[i].data, snd->data[i].len, chan, snd->data[i].vol, snd->data[i].pan, snd->data[i].format); +			} +		} else { +			stopSound(-snd->data[i].rate); +		} +      } +    }  + +    REG_IF = IRQ_VBLANK; +  } +  +} +  + +////////////////////////////////////////////////////////////////////// +  + +int main(int argc, char ** argv) { +  // Reset the clock if needed +  rtcReset(); + +  //enable sound +  powerON(POWER_SOUND); +  SOUND_CR = SOUND_ENABLE | SOUND_VOL(0x7F); +  IPC->soundData = 0; +  IPC->reset = false; +   +   +  for (int r = 0; r < 8; r++) { +	IPC->adpcm.arm7Buffer[r] = (u8 *) malloc(512); +  } +   +  for (int r = 0; r < 4; r++) { +	soundFilled[r] = false; +  } + +  +  // Set up the interrupt handler +  REG_IME = 0; +  IRQ_HANDLER = &InterruptHandler; +  REG_IE = IRQ_VBLANK | IRQ_TIMER1 | IRQ_TIMER3; +  REG_IF = ~0; +  DISP_SR = DISP_VBLANK_IRQ; +  REG_IME = 1; +   +   + +  // Keep the ARM7 out of main RAM +  while (1) { +	if (needSleep) { +		performSleep(); +		needSleep = false; +	} +//	if (IPC->reset) { +//		swiSoftReset(); +//	} +  } +  return 0; +} + +  +////////////////////////////////////////////////////////////////////// + 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 $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_raw_end[];" > `(echo $(<F) | tr . _)`.h +#	echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_raw[];" >> `(echo $(<F) | tr . _)`.h +#	echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_raw_size";" >> `(echo $(<F) | tr . _)`.h +#endef + + +define bin2o +	bin2s $< | $(AS) -mthumb -mthumb-interwork -o $(@) +	echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(<F) | tr . _)`.h +	echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(<F) | tr . _)`.h +	echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(<F) | tr . _)`.h +endef + + +#define bin2o +#	@echo $(*) +#	cp $(<) $(*).tmp +#	$(OBJCOPY) -I binary -O elf32-littlearm -B arm \ +#	--rename-section .data=.rodata \ +#	--redefine-sym _binary_$(subst .,_,$(subst /,_,$(*)))_tmp_start=$(notdir $*)\ +#	--redefine-sym _binary_$(subst .,_,$(subst /,_,$(*)))_tmp_end=$(notdir $*)_end\ +#	$(*).tmp $(@) +#	echo "extern const u8" $(notdir $*)"[] __attribute__ ((aligned (4)));" > $(*).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 $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_raw_end[];" > `(echo $(<F) | tr . _)`.h +#	echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_raw[];" >> `(echo $(<F) | tr . _)`.h +#	echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_raw_size";" >> `(echo $(<F) | tr . _)`.h +#endef + + +define bin2o +	bin2s $< | $(AS) -mthumb -mthumb-interwork -o $(@) +	echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(<F) | tr . _)`.h +	echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(<F) | tr . _)`.h +	echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(<F) | tr . _)`.h +endef + + +#define bin2o +#	@echo $(*) +#	cp $(<) $(*).tmp +#	$(OBJCOPY) -I binary -O elf32-littlearm -B arm \ +#	--rename-section .data=.rodata \ +#	--redefine-sym _binary_$(subst .,_,$(subst /,_,$(*)))_tmp_start=$(notdir $*)\ +#	--redefine-sym _binary_$(subst .,_,$(subst /,_,$(*)))_tmp_end=$(notdir $*)_end\ +#	$(*).tmp $(@) +#	echo "extern const u8" $(notdir $*)"[] __attribute__ ((aligned (4)));" > $(*).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 <nds.h> + +#include <ARM9/console.h> //basic print funcionality + +#include <stdlib.h> +#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<w+2; i++) { +		SPRITE_GFX_SUB[off+i] = border; +		SPRITE_GFX_SUB[off+(31)*64+i] = border; +	} +	for (uint i=1; i<31; i++) { +		SPRITE_GFX_SUB[off+(i*64)] = border; +		SPRITE_GFX_SUB[off+(i*64)+(w+1)] = border; +	} +	 +	int offset = (32 - h) >> 1; + +	for (uint y=0; y<h; y++) { +		for (uint x=0; x<w; x++) { +			int color = icon[y*w+x]; +			if (color == keycolor) { +				SPRITE_GFX_SUB[off+(y+1+offset)*64+(x+1)] = 0x8000; // black background +			} else { +				SPRITE_GFX_SUB[off+(y+1+offset)*64+(x+1)] = BG_PALETTE[color] | 0x8000; +			} +		} +	} + +	sprites[1].attribute[0] = ATTR0_BMP | 150; +	sprites[1].attribute[1] = ATTR1_SIZE_64 | pos; +	sprites[1].attribute[2] = ATTR2_ALPHA(1) | 48; +} + + + + +void displayMode16Bit() { + +	u16 buffer[32 * 32 * 2]; + + +	if (displayModeIs8Bit) { +		saveGameBackBuffer(); +		for (int r = 0; r < 32 * 32; r++) { +			buffer[r] = ((u16 *) SCREEN_BASE_BLOCK(0))[r]; +		} +	} + + +	videoSetMode(MODE_5_2D | /*DISPLAY_BG0_ACTIVE |*/ DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP);  +	videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE |/* DISPLAY_BG1_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); +	vramSetBankB(VRAM_B_MAIN_BG); +	vramSetBankC(VRAM_C_MAIN_BG); +	vramSetBankD(VRAM_D_MAIN_BG); +	vramSetBankH(VRAM_H_SUB_BG); + +	BG3_CR = BG_BMP16_512x256; +	highBuffer = false; +	 +	BG3_XDX = (int) (1.25f * 256); +    BG3_XDY = 0; +    BG3_YDX = 0; +    BG3_YDY = (int) ((200.0f / 192.0f) * 256); + +	memset(BG_GFX, 0, 512 * 256 * 2); +	 +	savedPalEntry255 = PALETTE_SUB[255]; +	PALETTE_SUB[255] = RGB15(31,31,31);//by default font will be rendered with color 255 + +	// Do text stuff +	SUB_BG0_CR = BG_MAP_BASE(4) | BG_TILE_BASE(0); +	SUB_BG0_Y0 = 0; + +	consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(4), (u16*)CHAR_BASE_BLOCK_SUB(0), 16); + +	if (displayModeIs8Bit) { +		//dmaCopyHalfWords(3, (u16 *) SCREEN_BASE_BLOCK_SUB(0), buffer, 32 * 32 * 2); +		for (int r = 0; r < 32 * 32; r++) { +			((u16 *) SCREEN_BASE_BLOCK_SUB(4))[r] = buffer[r]; +		} +	} + +	consolePrintSet(0, 23); +	consolePrintf("\n"); +	 +	// Show keyboard +	SUB_BG1_CR = BG_TILE_BASE(1) | BG_MAP_BASE(12); +	//drawKeyboard(1, 12); +	 +	POWER_CR &= ~POWER_SWAP_LCDS; + + + +	displayModeIs8Bit = false; +} + + +void displayMode16BitFlipBuffer() { +	if (!displayModeIs8Bit) { +		u16* back = get16BitBackBuffer(); +	 +//		highBuffer = !highBuffer; +//		BG3_CR = BG_BMP16_512x256 |	BG_BMP_RAM(highBuffer? 1: 0); +		 +		for (int r = 0; r < 512 * 256; r++) { +			*(BG_GFX + r) = *(back + r); +		} +	} +} + +void setShakePos(int shakePos) { +	shakePos = shakePos; +} + + +u16* get16BitBackBuffer() { +	return BG_GFX + 0x20000; +} + +u16* get8BitBackBuffer() { +	return BG_GFX + 0x10000;		// 16bit qty! +} + +void setSoundProc(OSystem::SoundProc proc, void* param) { +//	consolePrintf("Set sound callback"); +	soundCallback = proc; +	soundParam = param; +} + +// The sound system in ScummVM seems to always return stereo interleaved samples. +// Here, I'm treating an 11Khz stereo stream as a 22Khz mono stream, which works sorta ok, but is +// a horrible bodge.  Any advice on how to change the engine to output mono would be greatly +// appreciated. +void doSoundCallback() { +	if ((soundCallback)) { +		lastCallbackFrame = frameCount; +		 +		for (int r = IPC->playingSection; 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 <nds.h> + +#include <ARM9/console.h> //basic print funcionality + +#include <stdlib.h> +#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<w+2; i++) { +		SPRITE_GFX_SUB[off+i] = border; +		SPRITE_GFX_SUB[off+(31)*64+i] = border; +	} +	for (uint i=1; i<31; i++) { +		SPRITE_GFX_SUB[off+(i*64)] = border; +		SPRITE_GFX_SUB[off+(i*64)+(w+1)] = border; +	} +	 +	int offset = (32 - h) >> 1; + +	for (uint y=0; y<h; y++) { +		for (uint x=0; x<w; x++) { +			int color = icon[y*w+x]; +			if (color == keycolor) { +				SPRITE_GFX_SUB[off+(y+1+offset)*64+(x+1)] = 0x8000; // black background +			} else { +				SPRITE_GFX_SUB[off+(y+1+offset)*64+(x+1)] = BG_PALETTE[color] | 0x8000; +			} +		} +	} + +	sprites[1].attribute[0] = ATTR0_BMP | 150; +	sprites[1].attribute[1] = ATTR1_SIZE_64 | pos; +	sprites[1].attribute[2] = ATTR2_ALPHA(1) | 48; +} + + + + +void displayMode16Bit() { + +	u16 buffer[32 * 32 * 2]; + + +	if (displayModeIs8Bit) { +		saveGameBackBuffer(); +		for (int r = 0; r < 32 * 32; r++) { +			buffer[r] = ((u16 *) SCREEN_BASE_BLOCK(0))[r]; +		} +	} + + +	videoSetMode(MODE_5_2D | /*DISPLAY_BG0_ACTIVE |*/ DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP);  +	videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE |/* DISPLAY_BG1_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); +	vramSetBankB(VRAM_B_MAIN_BG); +	vramSetBankC(VRAM_C_MAIN_BG); +	vramSetBankD(VRAM_D_MAIN_BG); +	vramSetBankH(VRAM_H_SUB_BG); + +	BG3_CR = BG_BMP16_512x256; +	highBuffer = false; +	 +	BG3_XDX = (int) (1.25f * 256); +    BG3_XDY = 0; +    BG3_YDX = 0; +    BG3_YDY = (int) ((200.0f / 192.0f) * 256); + +	memset(BG_GFX, 0, 512 * 256 * 2); +	 +	savedPalEntry255 = PALETTE_SUB[255]; +	PALETTE_SUB[255] = RGB15(31,31,31);//by default font will be rendered with color 255 + +	// Do text stuff +	SUB_BG0_CR = BG_MAP_BASE(4) | BG_TILE_BASE(0); +	SUB_BG0_Y0 = 0; + +	consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(4), (u16*)CHAR_BASE_BLOCK_SUB(0), 16); + +	if (displayModeIs8Bit) { +		//dmaCopyHalfWords(3, (u16 *) SCREEN_BASE_BLOCK_SUB(0), buffer, 32 * 32 * 2); +		for (int r = 0; r < 32 * 32; r++) { +			((u16 *) SCREEN_BASE_BLOCK_SUB(4))[r] = buffer[r]; +		} +	} + +	consolePrintSet(0, 23); +	consolePrintf("\n"); +	 +	// Show keyboard +	SUB_BG1_CR = BG_TILE_BASE(1) | BG_MAP_BASE(12); +	//drawKeyboard(1, 12); +	 +	POWER_CR &= ~POWER_SWAP_LCDS; + + + +	displayModeIs8Bit = false; +} + + +void displayMode16BitFlipBuffer() { +	if (!displayModeIs8Bit) { +		u16* back = get16BitBackBuffer(); +	 +//		highBuffer = !highBuffer; +//		BG3_CR = BG_BMP16_512x256 |	BG_BMP_RAM(highBuffer? 1: 0); +		 +		for (int r = 0; r < 512 * 256; r++) { +			*(BG_GFX + r) = *(back + r); +		} +	} +} + +void setShakePos(int shakePos) { +	shakePos = shakePos; +} + + +u16* get16BitBackBuffer() { +	return BG_GFX + 0x20000; +} + +u16* get8BitBackBuffer() { +	return BG_GFX + 0x10000;		// 16bit qty! +} + +void setSoundProc(OSystem::SoundProc proc, void* param) { +//	consolePrintf("Set sound callback"); +	soundCallback = proc; +	soundParam = param; +} + +// The sound system in ScummVM seems to always return stereo interleaved samples. +// Here, I'm treating an 11Khz stereo stream as a 22Khz mono stream, which works sorta ok, but is +// a horrible bodge.  Any advice on how to change the engine to output mono would be greatly +// appreciated. +void doSoundCallback() { +	if ((soundCallback)) { +		lastCallbackFrame = frameCount; +		 +		for (int r = IPC->playingSection; 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 <nds.h> +#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 <nds.h> +#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 <nds.h> +#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 <string.h> + +#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; i<inumSecs; i++)	{ +		if( disc_CacheReadSector( &p[i*512], sector + i ) == false ) +			return false; +	} +	return true; +#else +	if (active_interface) return active_interface->fn_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; i<inumSecs; i++)	{ +		if( disc_CacheWriteSector( &p[i*512], sector + i ) == false ) +			return false; +	} +	return true; +#else +	if (active_interface) return active_interface->fn_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 <nds.h> +#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 <string.h> + +#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; i<inumSecs; i++)	{ +		if( disc_CacheReadSector( &p[i*512], sector + i ) == false ) +			return false; +	} +	return true; +#else +	if (active_interface) return active_interface->fn_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; i<inumSecs; i++)	{ +		if( disc_CacheWriteSector( &p[i*512], sector + i ) == false ) +			return false; +	} +	return true; +#else +	if (active_interface) return active_interface->fn_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 <nds/jtypes.h> +#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 <nds/jtypes.h> +#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 <string.h> +#ifdef NDS + #include <nds/ipc.h>	// 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 <string.h> +#ifdef NDS + #include <nds/ipc.h>	// 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 <nds/jtypes.h> +#else + #include "gba_types.h" +#endif + +#ifdef FILE_TIME_SUPPORT + #include <time.h> +#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 <nds/jtypes.h> +#else + #include "gba_types.h" +#endif + +#ifdef FILE_TIME_SUPPORT + #include <time.h> +#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 <at> gmx <dot> de and do not forget to also +drop chishm <at> hotmail <dot> 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 <at> gmx <dot> de and do not forget to also +drop chishm <at> hotmail <dot> 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 <at> gmx <dot> de and do not forget to also +  drop chishm <at> hotmail <dot> 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 <at> gmx <dot> de and do not forget to also +  drop chishm <at> hotmail <dot> 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 <string.h> + +//--------------------------------------------------------------- +// DMA +#ifdef _CF_USE_DMA + #ifndef NDS +  #include "gba_dma.h" + #else +  #include <nds/dma.h> +  #ifdef ARM9 +   #include <nds/arm9/cache.h> +  #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 <string.h> + +//--------------------------------------------------------------- +// DMA +#ifdef _CF_USE_DMA + #ifndef NDS +  #include "gba_dma.h" + #else +  #include <nds/dma.h> +  #ifdef ARM9 +   #include <nds/arm9/cache.h> +  #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 <nds/dma.h> +  #ifdef ARM9 +   #include <nds/arm9/cache.h> +  #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 <nds/dma.h> +  #ifdef ARM9 +   #include <nds/arm9/cache.h> +  #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;i++) +	{ +		sta=M3_REG_STS; +		if((sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2)) +		{ +			return true; +			//break; +		} +	} +	return false; + +//	return ( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) ); +//	return true; +} + + +/*----------------------------------------------------------------- +M3SD_ClearStatus +Tries to make the CF card go back to idle mode +bool return OUT:  true if a CF card is idle +-----------------------------------------------------------------*/ +bool M3SD_ClearStatus (void)  +{ + +//	int i=SDDIR; +	int i; +	u16 sta; + +	i = 0; +	M3_REG_STS = CF_STS_INSERTED1; +	while (i < CARD_TIMEOUT) +	{ +		sta=M3_REG_STS; +		if(  (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2)  )break; +		i++; +	} +	if (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<numSecs;i++) +	{ +		if(M3SD_read1sector(i + sector , 512*i + (u32) buffer )==false) +		{ +			r=false; +			break; +		} +	} +	return r; + +} + + + +/*----------------------------------------------------------------- +M3SD_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 M3SD_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + +	bool r=true; +	int i; +	for(i=0;i<numSecs;i++) +	{ +		if(M3SD_write1sector(i + sector , 512*i + (u32) buffer )==false) +		{ +			r=false; +			break; +		} +	} +	return r; + +//	return false; + + +} + + +/*----------------------------------------------------------------- +M3_Unlock +Returns true if M3 was unlocked, false if failed +Added by MightyMax +-----------------------------------------------------------------*/ +bool M3SD_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 +	vu16 sta; +	sta=M3_REG_STS; +	sta=M3_REG_STS; +	if(  (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2)  )return true; + +	return false; +} + +bool M3SD_Shutdown(void) { +	return M3SD_ClearStatus() ; +} ; + +bool M3SD_StartUp(void) { +	return M3SD_Unlock() ; +} ; + + +IO_INTERFACE io_m3sd = { +	DEVICE_TYPE_M3SD, +	FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE, +	(FN_MEDIUM_STARTUP)&M3SD_StartUp, +	(FN_MEDIUM_ISINSERTED)&M3SD_IsInserted, +	(FN_MEDIUM_READSECTORS)&M3SD_ReadSectors, +	(FN_MEDIUM_WRITESECTORS)&M3SD_WriteSectors, +	(FN_MEDIUM_CLEARSTATUS)&M3SD_ClearStatus, +	(FN_MEDIUM_SHUTDOWN)&M3SD_Shutdown +} ; + + +LPIO_INTERFACE M3SD_GetInterface(void) { +	return &io_m3sd ; +} ; + +#endif // SUPPORT_M3CF +/* +	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;i++) +	{ +		sta=M3_REG_STS; +		if((sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2)) +		{ +			return true; +			//break; +		} +	} +	return false; + +//	return ( (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2) ); +//	return true; +} + + +/*----------------------------------------------------------------- +M3SD_ClearStatus +Tries to make the CF card go back to idle mode +bool return OUT:  true if a CF card is idle +-----------------------------------------------------------------*/ +bool M3SD_ClearStatus (void)  +{ + +//	int i=SDDIR; +	int i; +	u16 sta; + +	i = 0; +	M3_REG_STS = CF_STS_INSERTED1; +	while (i < CARD_TIMEOUT) +	{ +		sta=M3_REG_STS; +		if(  (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2)  )break; +		i++; +	} +	if (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<numSecs;i++) +	{ +		if(M3SD_read1sector(i + sector , 512*i + (u32) buffer )==false) +		{ +			r=false; +			break; +		} +	} +	return r; + +} + + + +/*----------------------------------------------------------------- +M3SD_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 M3SD_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + +	bool r=true; +	int i; +	for(i=0;i<numSecs;i++) +	{ +		if(M3SD_write1sector(i + sector , 512*i + (u32) buffer )==false) +		{ +			r=false; +			break; +		} +	} +	return r; + +//	return false; + + +} + + +/*----------------------------------------------------------------- +M3_Unlock +Returns true if M3 was unlocked, false if failed +Added by MightyMax +-----------------------------------------------------------------*/ +bool M3SD_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 +	vu16 sta; +	sta=M3_REG_STS; +	sta=M3_REG_STS; +	if(  (sta == CF_STS_INSERTED1)||(sta == CF_STS_INSERTED2)  )return true; + +	return false; +} + +bool M3SD_Shutdown(void) { +	return M3SD_ClearStatus() ; +} ; + +bool M3SD_StartUp(void) { +	return M3SD_Unlock() ; +} ; + + +IO_INTERFACE io_m3sd = { +	DEVICE_TYPE_M3SD, +	FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE, +	(FN_MEDIUM_STARTUP)&M3SD_StartUp, +	(FN_MEDIUM_ISINSERTED)&M3SD_IsInserted, +	(FN_MEDIUM_READSECTORS)&M3SD_ReadSectors, +	(FN_MEDIUM_WRITESECTORS)&M3SD_WriteSectors, +	(FN_MEDIUM_CLEARSTATUS)&M3SD_ClearStatus, +	(FN_MEDIUM_SHUTDOWN)&M3SD_Shutdown +} ; + + +LPIO_INTERFACE M3SD_GetInterface(void) { +	return &io_m3sd ; +} ; + +#endif // SUPPORT_M3CF diff --git a/backends/platform/ds/arm9/source/fat/io_m3sd.h b/backends/platform/ds/arm9/source/fat/io_m3sd.h new file mode 100644 index 0000000000..bd6b2644cc --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_m3sd.h @@ -0,0 +1,50 @@ +/* +	io_m3sd.h  by SaTa. + +	Hardware Routines for reading an SD card +	using the M3 SD + +	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_M3SD_H +#define IO_M3SD_H + +// 'M3SD' +#define DEVICE_TYPE_M3SD 0x4453334D + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE M3SD_GetInterface(void) ; + +#endif	// define IO_M3SD_H +/* +	io_m3sd.h  by SaTa. + +	Hardware Routines for reading an SD card +	using the M3 SD + +	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_M3SD_H +#define IO_M3SD_H + +// 'M3SD' +#define DEVICE_TYPE_M3SD 0x4453334D + +#include "disc_io.h" + +// export interface +extern LPIO_INTERFACE M3SD_GetInterface(void) ; + +#endif	// define IO_M3SD_H diff --git a/backends/platform/ds/arm9/source/fat/io_m3sd_asm.s b/backends/platform/ds/arm9/source/fat/io_m3sd_asm.s new file mode 100644 index 0000000000..18bbee75ce --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_m3sd_asm.s @@ -0,0 +1,392 @@ +	.TEXT +@	AREA M3SD, CODE, READONLY +@	ENTRY +@----------------------------------- +@	EXPORT SD_crc16 +@	EXPORT SD_data_write + +@CSYNC	EQU	0x9800000 +@SDDIR	EQU 0x8800000 +@SDCON	EQU	0x9800000 +@SDODA	EQU 0x9000000 +.equ CSYNC,0x9800000 +.equ SDDIR,0x8800000 +.equ SDCON,0x9800000 +.equ SDODA,0x9000000 + +	.ALIGN +	.CODE 32 + +clkout: +	stmfd   r13!,{r0-r1} +	ldr     r1,=SDDIR +	mov     r0,#0x4 +	strh    r0,[r1] +	mov     r0,r0 +	mov     r0,r0 +	mov     r0,#0xc +	strh    r0,[r1] +	ldmfd	r13!,{r0-r1} +	bx      r14 + +clkin: +	stmfd   r13!,{r0-r1} +	ldr     r1,=SDDIR +	mov     r0,#0x0 +	strh    r0,[r1] +	mov     r0,r0 +	mov     r0,r0 +	mov     r0,#0x8 +	strh    r0,[r1] +	ldmfd	r13!,{r0-r1} +	bx      r14 + +wait_ready: +	stmfd   r13!,{r0-r2} +	mov     r2,#32 +	mov	    r1,#SDODA +sd_write_loop2: +	mov	    r0,#0xff @end bit +	strh    r0,[r1] +	bl      clkout +	subs    r2, r2, #1 +	bne     sd_write_loop2 + +sd_write_busy: +	bl      clkin +	ldrh    r0,[r1]    +	tst	    r0,#0x100 +	beq	    sd_write_busy +	ldmfd	r13!,{r0-r1} +	bx      r14 + +@------void SD_crc16(u16* buff,u16 num,u16* crc16buff) + +    .GLOBAL SD_crc16 + +SD_crc16: +	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----------------------------------- + +@-----------------viod SD_data_write(u16 *buff,u16* crc16buff)------------------- +    .GLOBAL SD_data_write +SD_data_write: +	stmfd   r13!,{r4-r5,r14} +	mov     r5,#512 +	mov	    r2,#SDODA +sd_data_write_busy: +	bl      clkin +	ldrh    r3,[r2]    +	tst	    r3,#0x100 +	beq	    sd_data_write_busy + +	mov	    r3,#0 @star bit +	strh    r3,[r2] +	bl      clkout + +sd_data_write_loop: +	ldrh    r4,[r0],#2 +	mov     r3,r4,lsr#4 +	strh    r3,[r2] +	bl      clkout +	mov     r3,r4 +	strh    r3,[r2] +	bl      clkout +	mov     r3,r4,lsr#12 +	strh    r3,[r2] +	bl      clkout +	mov     r3,r4,lsr#8 +	strh    r3,[r2] +	bl      clkout + +	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     r5,#32 +sd_data_write_loop2: +	mov	    r3,#0xff @end bit +	strh    r3,[r2] +	bl      clkout +	subs    r5, r5, #1 +	bne     sd_data_write_loop2 +	 +sd_data_write_busy2: +	bl      clkin +	ldrh    r3,[r2]    +	tst	    r3,#0x100 +	beq	    sd_data_write_busy2 + +	ldmfd   r13!,{r4-r5,r15} +@-----------------end------------------- + +	.TEXT +@	AREA M3SD, CODE, READONLY +@	ENTRY +@----------------------------------- +@	EXPORT SD_crc16 +@	EXPORT SD_data_write + +@CSYNC	EQU	0x9800000 +@SDDIR	EQU 0x8800000 +@SDCON	EQU	0x9800000 +@SDODA	EQU 0x9000000 +.equ CSYNC,0x9800000 +.equ SDDIR,0x8800000 +.equ SDCON,0x9800000 +.equ SDODA,0x9000000 + +	.ALIGN +	.CODE 32 + +clkout: +	stmfd   r13!,{r0-r1} +	ldr     r1,=SDDIR +	mov     r0,#0x4 +	strh    r0,[r1] +	mov     r0,r0 +	mov     r0,r0 +	mov     r0,#0xc +	strh    r0,[r1] +	ldmfd	r13!,{r0-r1} +	bx      r14 + +clkin: +	stmfd   r13!,{r0-r1} +	ldr     r1,=SDDIR +	mov     r0,#0x0 +	strh    r0,[r1] +	mov     r0,r0 +	mov     r0,r0 +	mov     r0,#0x8 +	strh    r0,[r1] +	ldmfd	r13!,{r0-r1} +	bx      r14 + +wait_ready: +	stmfd   r13!,{r0-r2} +	mov     r2,#32 +	mov	    r1,#SDODA +sd_write_loop2: +	mov	    r0,#0xff @end bit +	strh    r0,[r1] +	bl      clkout +	subs    r2, r2, #1 +	bne     sd_write_loop2 + +sd_write_busy: +	bl      clkin +	ldrh    r0,[r1]    +	tst	    r0,#0x100 +	beq	    sd_write_busy +	ldmfd	r13!,{r0-r1} +	bx      r14 + +@------void SD_crc16(u16* buff,u16 num,u16* crc16buff) + +    .GLOBAL SD_crc16 + +SD_crc16: +	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----------------------------------- + +@-----------------viod SD_data_write(u16 *buff,u16* crc16buff)------------------- +    .GLOBAL SD_data_write +SD_data_write: +	stmfd   r13!,{r4-r5,r14} +	mov     r5,#512 +	mov	    r2,#SDODA +sd_data_write_busy: +	bl      clkin +	ldrh    r3,[r2]    +	tst	    r3,#0x100 +	beq	    sd_data_write_busy + +	mov	    r3,#0 @star bit +	strh    r3,[r2] +	bl      clkout + +sd_data_write_loop: +	ldrh    r4,[r0],#2 +	mov     r3,r4,lsr#4 +	strh    r3,[r2] +	bl      clkout +	mov     r3,r4 +	strh    r3,[r2] +	bl      clkout +	mov     r3,r4,lsr#12 +	strh    r3,[r2] +	bl      clkout +	mov     r3,r4,lsr#8 +	strh    r3,[r2] +	bl      clkout + +	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     r5,#32 +sd_data_write_loop2: +	mov	    r3,#0xff @end bit +	strh    r3,[r2] +	bl      clkout +	subs    r5, r5, #1 +	bne     sd_data_write_loop2 +	 +sd_data_write_busy2: +	bl      clkin +	ldrh    r3,[r2]    +	tst	    r3,#0x100 +	beq	    sd_data_write_busy2 + +	ldmfd   r13!,{r4-r5,r15} +@-----------------end------------------- + diff --git a/backends/platform/ds/arm9/source/fat/io_mpcf.c b/backends/platform/ds/arm9/source/fat/io_mpcf.c new file mode 100644 index 0000000000..c55eef73eb --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_mpcf.c @@ -0,0 +1,714 @@ +/* +	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 <nds/dma.h> +  #ifdef ARM9 +   #include <nds/arm9/cache.h> +  #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 <nds/dma.h> +  #ifdef ARM9 +   #include <nds/arm9/cache.h> +  #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 <nds/card.h> + +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 <nds/card.h> + +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<i ; j++) +@	{ +@		sd_data_read_s((u32)buff+j*512); +@	} +@	SDCommand(12,0,0);  +@	get_resp(); +@	send_clk(0x10); +@ +@} +ReadSector: +	stmfd   r13!,{r4-r6,r14} + +	mov	r4,r0 +	mov	r5,r2 + +	mov	r2,r1,lsl #9 +	mov	r0,#18 +	mov	r1,#0 +	bl	SDCommand +	mov	r6,#0 +beginforj_ReadSector: +	cmp	r6,r5 +	bge	endforj_ReadSector +	mov	r0,r4 +	add	r0,r0,r6,lsl #9  +	bl	sd_data_read_s +	add	r6,r6,#1 +	b	beginforj_ReadSector +endforj_ReadSector: +	mov	r0,#12 +	mov	r1,#0 +	mov	r2,#0 +	bl	SDCommand +	bl	get_resp +	mov	r0,#0x10 +	bl	send_clk +	mov	r0,#1 +	ldmfd   r13!,{r4-r6,r15} +@	bx	r14 +@----------end ReadSector------------ + +@-----------void get_resp(void)------------------- + + +		.ALIGN +		.GLOBAL	 get_resp @ r0:Srcp r1:num ok +		.CODE 32 +get_resp: + +	stmfd   r13!,{r14} +	mov	r1,#6 +	bl	sd_com_read_s +	ldmfd   r13!,{r15} +@-----------end get_resp------------------- + + +@---------------void WriteSector(u16 *buff,u32 sector,u8 writenum)--------------------- +		.ALIGN +		.GLOBAL	 WriteSector @ r0:Srcp r1:num ok +		.CODE 32 + +@void WriteSector(u16 *buff,u32 sector,u8 writenum) +@{ +@	register u16 i,j; +@	u16 crc16[5]; +@	i=writenum; +@ +@	sectno<<=9; +@ +@	SDCommand(25,0,sector);  +@	get_resp(); +@	send_clk(0x10);  +@ +@	for (j=0;j<i ; j++) +@	{ +@		sd_crc16_s((u32)(u32)buff+j*512,512,(u32)crc16); +@		sd_data_write_s((u32)buff+j*512,(u32)crc16); +@		send_clk(0x10);  +@	} +@	SDCommand(12,0,0);  +@	get_resp(); +@	send_clk(0x10); +@	while((*(u16*)sd_dataadd &0x0100)==0); +@ +@} +@ +WriteSector: +	stmfd   r13!,{r4-r6,r14} + +	sub	r13,r13,#20 + +	mov	r4,r0 +	mov	r5,r2 + +	mov	r2,r1,lsl #9 +	mov	r0,#25 +	mov	r1,#0 +	bl	SDCommand +	bl	get_resp +	mov	r0,#0x10 +	bl	send_clk +	mov	r6,#0 + +beginforj_WriteSector: +	cmp	r6,r5 +	bge	endforj_WriteSector +	mov	r0,r4 +	add	r0,r0,r6,lsl #9 +	mov	r1,#512 +	add	r2,r13,#4 +	bl	sd_crc16_s +	mov	r0,r4 +	add	r0,r0,r6,lsl #9  +	add	r1,r13,#4 +	bl	sd_data_write_s +	mov	r0,#0x10 +	bl	send_clk +	add	r6,r6,#1 +	b	beginforj_WriteSector +endforj_WriteSector: +	mov	r0,#12 +	mov	r1,#0 +	mov	r2,#0 +	bl	SDCommand +	bl	get_resp +	mov	r0,#0x10 +	bl	send_clk +	ldr	r0,=sd_dataadd +beginwhile_WriteSector: +	ldrh	r1,[r0] +	tst	r1,#0x0100 +	beq	beginwhile_WriteSector +	mov	r0,#1 +	add	r13,r13,#20 +	ldmfd   r13!,{r4-r6,r15} +@---------------end WriteSector-------------------- + +@----------------void InitSCMode(void)--------------- +		.ALIGN +		.GLOBAL	 InitSCMode +		.CODE 32 +InitSCMode: +	mvn     r0,#0x0F6000000  +	sub     r0,r0,#0x01 +	mov     r1,#0x0A500 +	add     r1,r1,#0x5A +	strh    r1,[r0] +	strh    r1,[r0] +	mov	r2,#3 +	strh    r2,[r0] +	strh    r2,[r0] +	bx	r14 +@----------------end InitSCMode --------------- + +@----------------bool MemoryCard_IsInserted(void)--------------- +		.ALIGN +		.GLOBAL	 MemoryCard_IsInserted +		.CODE 32 + +MemoryCard_IsInserted: +	ldr	r0,=sd_comadd +	ldrh	r1,[r0] +	tst	r1,#0x300 +	moveq	r0,#1 +	movne	r0,#0 +	bx	r14 +@----------------end MemoryCard_IsInserted--------------- + +    .END + + + + + + + + + + + +    .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<i ; j++) +@	{ +@		sd_data_read_s((u32)buff+j*512); +@	} +@	SDCommand(12,0,0);  +@	get_resp(); +@	send_clk(0x10); +@ +@} +ReadSector: +	stmfd   r13!,{r4-r6,r14} + +	mov	r4,r0 +	mov	r5,r2 + +	mov	r2,r1,lsl #9 +	mov	r0,#18 +	mov	r1,#0 +	bl	SDCommand +	mov	r6,#0 +beginforj_ReadSector: +	cmp	r6,r5 +	bge	endforj_ReadSector +	mov	r0,r4 +	add	r0,r0,r6,lsl #9  +	bl	sd_data_read_s +	add	r6,r6,#1 +	b	beginforj_ReadSector +endforj_ReadSector: +	mov	r0,#12 +	mov	r1,#0 +	mov	r2,#0 +	bl	SDCommand +	bl	get_resp +	mov	r0,#0x10 +	bl	send_clk +	mov	r0,#1 +	ldmfd   r13!,{r4-r6,r15} +@	bx	r14 +@----------end ReadSector------------ + +@-----------void get_resp(void)------------------- + + +		.ALIGN +		.GLOBAL	 get_resp @ r0:Srcp r1:num ok +		.CODE 32 +get_resp: + +	stmfd   r13!,{r14} +	mov	r1,#6 +	bl	sd_com_read_s +	ldmfd   r13!,{r15} +@-----------end get_resp------------------- + + +@---------------void WriteSector(u16 *buff,u32 sector,u8 writenum)--------------------- +		.ALIGN +		.GLOBAL	 WriteSector @ r0:Srcp r1:num ok +		.CODE 32 + +@void WriteSector(u16 *buff,u32 sector,u8 writenum) +@{ +@	register u16 i,j; +@	u16 crc16[5]; +@	i=writenum; +@ +@	sectno<<=9; +@ +@	SDCommand(25,0,sector);  +@	get_resp(); +@	send_clk(0x10);  +@ +@	for (j=0;j<i ; j++) +@	{ +@		sd_crc16_s((u32)(u32)buff+j*512,512,(u32)crc16); +@		sd_data_write_s((u32)buff+j*512,(u32)crc16); +@		send_clk(0x10);  +@	} +@	SDCommand(12,0,0);  +@	get_resp(); +@	send_clk(0x10); +@	while((*(u16*)sd_dataadd &0x0100)==0); +@ +@} +@ +WriteSector: +	stmfd   r13!,{r4-r6,r14} + +	sub	r13,r13,#20 + +	mov	r4,r0 +	mov	r5,r2 + +	mov	r2,r1,lsl #9 +	mov	r0,#25 +	mov	r1,#0 +	bl	SDCommand +	bl	get_resp +	mov	r0,#0x10 +	bl	send_clk +	mov	r6,#0 + +beginforj_WriteSector: +	cmp	r6,r5 +	bge	endforj_WriteSector +	mov	r0,r4 +	add	r0,r0,r6,lsl #9 +	mov	r1,#512 +	add	r2,r13,#4 +	bl	sd_crc16_s +	mov	r0,r4 +	add	r0,r0,r6,lsl #9  +	add	r1,r13,#4 +	bl	sd_data_write_s +	mov	r0,#0x10 +	bl	send_clk +	add	r6,r6,#1 +	b	beginforj_WriteSector +endforj_WriteSector: +	mov	r0,#12 +	mov	r1,#0 +	mov	r2,#0 +	bl	SDCommand +	bl	get_resp +	mov	r0,#0x10 +	bl	send_clk +	ldr	r0,=sd_dataadd +beginwhile_WriteSector: +	ldrh	r1,[r0] +	tst	r1,#0x0100 +	beq	beginwhile_WriteSector +	mov	r0,#1 +	add	r13,r13,#20 +	ldmfd   r13!,{r4-r6,r15} +@---------------end WriteSector-------------------- + +@----------------void InitSCMode(void)--------------- +		.ALIGN +		.GLOBAL	 InitSCMode +		.CODE 32 +InitSCMode: +	mvn     r0,#0x0F6000000  +	sub     r0,r0,#0x01 +	mov     r1,#0x0A500 +	add     r1,r1,#0x5A +	strh    r1,[r0] +	strh    r1,[r0] +	mov	r2,#3 +	strh    r2,[r0] +	strh    r2,[r0] +	bx	r14 +@----------------end InitSCMode --------------- + +@----------------bool MemoryCard_IsInserted(void)--------------- +		.ALIGN +		.GLOBAL	 MemoryCard_IsInserted +		.CODE 32 + +MemoryCard_IsInserted: +	ldr	r0,=sd_comadd +	ldrh	r1,[r0] +	tst	r1,#0x300 +	moveq	r0,#1 +	movne	r0,#0 +	bx	r14 +@----------------end MemoryCard_IsInserted--------------- + +    .END + + + + + + + + + + + diff --git a/backends/platform/ds/arm9/source/gbampsave.cpp b/backends/platform/ds/arm9/source/gbampsave.cpp new file mode 100644 index 0000000000..bb5e44e722 --- /dev/null +++ b/backends/platform/ds/arm9/source/gbampsave.cpp @@ -0,0 +1,372 @@ +/* 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("/"); +} +/* 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 <string.h> +#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 <string.h> +#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 <NDS.h> +#include "scummconsole.h" + +#include <stdarg.h> + +#include <default_font_bin.h> + +///////////////////////////////////////// +//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 <NDS.h> +#include "scummconsole.h" + +#include <stdarg.h> + +#include <default_font_bin.h> + +///////////////////////////////////////// +//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 <NDS.h> +#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 <NDS.h> +#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 diff --git a/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h b/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h new file mode 100644 index 0000000000..10457c2848 --- /dev/null +++ b/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h @@ -0,0 +1,186 @@ + +#ifndef SCUMMVM_IPC_INCLUDE +#define SCUMMVM_IPC_INCLUDE + +////////////////////////////////////////////////////////////////////// + +#include <nds/jtypes.h> +#include <nds/ipc.h> + +////////////////////////////////////////////////////////////////////// + +  +typedef struct _adpcmBuffer { +	u8* buffer[8]; +	bool filled[8]; +	u8* arm7Buffer[8]; +	bool arm7Dirty[8]; +	bool semaphore; +} adpcmBuffer; +  +////////////////////////////////////////////////////////////////////// + +typedef struct scummvmTransferRegion { +  uint32 heartbeat;          // counts frames +  +   int16 touchX,   touchY;   // TSC X, Y +   int16 touchXpx, touchYpx; // TSC X, Y pixel values +   int16 touchZ1,  touchZ2;  // TSC x-panel measurements +  uint16 tdiode1,  tdiode2;  // TSC temperature diodes +  uint32 temperature;        // TSC computed temperature +  +  uint16 buttons;            // X, Y, /PENIRQ buttons +  +  union { +    uint8 curtime[8];        // current time response from RTC +  +    struct { +      u8 rtc_command; +      u8 rtc_year;           //add 2000 to get 4 digit year +      u8 rtc_month;          //1 to 12 +      u8 rtc_day;            //1 to (days in month) +  +      u8 rtc_incr; +      u8 rtc_hours;          //0 to 11 for AM, 52 to 63 for PM +      u8 rtc_minutes;        //0 to 59 +      u8 rtc_seconds;        //0 to 59 +    }; +  }; +  +  uint16 battery;            // battery life ??  hopefully.  :) +  uint16 aux;                // i have no idea... +  +  pTransferSound soundData; +   +  adpcmBuffer adpcm; +   +  +  // Don't rely on these below, will change or be removed in the future +  vuint32 mailAddr; +  vuint32 mailData; +  vuint8 mailRead; +  vuint8 mailBusy; +  vuint32 mailSize; +   +  bool performArm9SleepMode; +   +  u32 test; +  int tweak; +  bool tweakChanged; +   +//  bool fillSoundFirstHalf; +//  bool fillSoundSecondHalf; + +  // These are used for ScummVMs sound output +  bool fillNeeded[4]; +  int playingSection; +   +  bool reset; +   +  // Streaming sound +  bool streamFillNeeded[4]; +  int streamPlayingSection; +} scummTransferRegion, * pscummTransferRegion; + +////////////////////////////////////////////////////////////////////// + +#undef IPC +#define IPC ((scummTransferRegion volatile *)(0x027FF000)) + + +#endif + + + +#ifndef SCUMMVM_IPC_INCLUDE +#define SCUMMVM_IPC_INCLUDE + +////////////////////////////////////////////////////////////////////// + +#include <nds/jtypes.h> +#include <nds/ipc.h> + +////////////////////////////////////////////////////////////////////// + +  +typedef struct _adpcmBuffer { +	u8* buffer[8]; +	bool filled[8]; +	u8* arm7Buffer[8]; +	bool arm7Dirty[8]; +	bool semaphore; +} adpcmBuffer; +  +////////////////////////////////////////////////////////////////////// + +typedef struct scummvmTransferRegion { +  uint32 heartbeat;          // counts frames +  +   int16 touchX,   touchY;   // TSC X, Y +   int16 touchXpx, touchYpx; // TSC X, Y pixel values +   int16 touchZ1,  touchZ2;  // TSC x-panel measurements +  uint16 tdiode1,  tdiode2;  // TSC temperature diodes +  uint32 temperature;        // TSC computed temperature +  +  uint16 buttons;            // X, Y, /PENIRQ buttons +  +  union { +    uint8 curtime[8];        // current time response from RTC +  +    struct { +      u8 rtc_command; +      u8 rtc_year;           //add 2000 to get 4 digit year +      u8 rtc_month;          //1 to 12 +      u8 rtc_day;            //1 to (days in month) +  +      u8 rtc_incr; +      u8 rtc_hours;          //0 to 11 for AM, 52 to 63 for PM +      u8 rtc_minutes;        //0 to 59 +      u8 rtc_seconds;        //0 to 59 +    }; +  }; +  +  uint16 battery;            // battery life ??  hopefully.  :) +  uint16 aux;                // i have no idea... +  +  pTransferSound soundData; +   +  adpcmBuffer adpcm; +   +  +  // Don't rely on these below, will change or be removed in the future +  vuint32 mailAddr; +  vuint32 mailData; +  vuint8 mailRead; +  vuint8 mailBusy; +  vuint32 mailSize; +   +  bool performArm9SleepMode; +   +  u32 test; +  int tweak; +  bool tweakChanged; +   +//  bool fillSoundFirstHalf; +//  bool fillSoundSecondHalf; + +  // These are used for ScummVMs sound output +  bool fillNeeded[4]; +  int playingSection; +   +  bool reset; +   +  // Streaming sound +  bool streamFillNeeded[4]; +  int streamPlayingSection; +} scummTransferRegion, * pscummTransferRegion; + +////////////////////////////////////////////////////////////////////// + +#undef IPC +#define IPC ((scummTransferRegion volatile *)(0x027FF000)) + + +#endif + + diff --git a/backends/platform/ds/makefile b/backends/platform/ds/makefile new file mode 100644 index 0000000000..1a7b38c449 --- /dev/null +++ b/backends/platform/ds/makefile @@ -0,0 +1,42 @@ +#SUBDIRS:= `ls | egrep -v '^(CVS|tools)$$'` + + + + +export PATH	:=	$(DEVKITARM)/bin:$(PATH) + +export portdir = $(CURDIR)/arm9 +export srcdir = $(CURDIR)/../../.. + + +SUBDIRS := arm7 arm9 + +all: +	@for i in $(SUBDIRS); do if test -d $$i; then make -C $$i; fi; done; +clean: +	@for i in $(SUBDIRS); do if test -d $$i; then make -C $$i clean; fi; done; + +export: +	@for i in $(SUBDIRS); do if test -d $$i; then make -C $$i export; fi; done; + +#SUBDIRS:= `ls | egrep -v '^(CVS|tools)$$'` + + + + +export PATH	:=	$(DEVKITARM)/bin:$(PATH) + +export portdir = $(CURDIR)/arm9 +export srcdir = $(CURDIR)/../../.. + + +SUBDIRS := arm7 arm9 + +all: +	@for i in $(SUBDIRS); do if test -d $$i; then make -C $$i; fi; done; +clean: +	@for i in $(SUBDIRS); do if test -d $$i; then make -C $$i clean; fi; done; + +export: +	@for i in $(SUBDIRS); do if test -d $$i; then make -C $$i export; fi; done; +  | 
