diff --git a/.gitignore b/.gitignore index 5d022f364a..cb44a8efa1 100644 --- a/.gitignore +++ b/.gitignore @@ -87,6 +87,8 @@ libagbsyscall/*.s *.dll *.sdl *.iso +*.cia +*.elf # PSP build outputs EBOOT.PBP @@ -95,3 +97,6 @@ sa2_debug.log # third party deps /ext + +# downloaded tools +/tools/bannertool-1.2.3-linux \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index cc35f9334c..7a8863d7e3 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -24,6 +24,22 @@ "intelliSenseMode": "linux-gcc-x64", "configurationProvider": "ms-vscode.makefile-tools" }, + { + "name": "SA2 Linux 3DS", + "includePath": [ + "${workspaceFolder}/include/**", + "/opt/devkitpro/libctru/include/", + "/opt/devkitpro/portlibs/3ds/include/**", + "/opt/devkitpro/portlibs/3ds/include/SDL2/**", + "/opt/devkitpro/devkitARM/arm-none-eabi/include/**" + ], + "defines": ["PLATFORM_3DS=1", "__3DS__", "GAME=GAME_SA2", "PORTABLE"], + "compilerPath": "/opt/devkitpro/devkitARM/bin/arm-none-eabi-gcc", + "cStandard": "gnu17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-arm", + "configurationProvider": "ms-vscode.makefile-tools" + }, { "name": "Linux portable", "includePath": [ diff --git a/3ds_sa1.rsf b/3ds_sa1.rsf new file mode 100644 index 0000000000..013ca02af1 --- /dev/null +++ b/3ds_sa1.rsf @@ -0,0 +1,199 @@ +BasicInfo: + Title : "Sonic Advance" + CompanyCode : "00" + ProductCode : "CTR-P-SAGBA" + ContentType : Application + Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem / Homebrew + +#RomFs: + # Specifies the root path of the read only file system to include in the ROM. + #RootPath : "romfs" + +TitleInfo: + UniqueId : 0xC6768 + Category : Application + +CardInfo: + MediaSize : 128MB # 128MB / 256MB / 512MB / 1GB / 2GB / 4GB + MediaType : Card1 # Card1 / Card2 + CardDevice : None # NorFlash(Pick this if you use savedata) / None + + +Option: + UseOnSD : true # true if App is to be installed to SD + FreeProductCode : true # Removes limitations on ProductCode + MediaFootPadding : false # If true CCI files are created with padding + EnableCrypt : false # Enables encryption for NCCH and CIA + EnableCompress : true # Compresses exefs code + +AccessControlInfo: + #UseExtSaveData : true + #ExtSaveDataId: 0xff3ff + #UseExtendedSaveDataAccessControl: true + #AccessibleSaveDataIds: [0x101, 0x202, 0x303, 0x404, 0x505, 0x606] + +SystemControlInfo: + SaveDataSize: 128KB + RemasterVersion: 0 + StackSize: 0x40000 + +# DO NOT EDIT BELOW HERE OR PROGRAMS WILL NOT LAUNCH (most likely) + +AccessControlInfo: + FileSystemAccess: + - Debug + - DirectSdmc + - DirectSdmcWrite + + IdealProcessor : 0 + AffinityMask : 1 + + Priority : 16 + + MaxCpu : 0x9E # Default + DisableDebug : false + EnableForceDebug : false + CanWriteSharedPage : false + CanUsePrivilegedPriority : false + CanUseNonAlphabetAndNumber : false + PermitMainFunctionArgument : false + CanShareDeviceMemory : false + RunnableOnSleep : false + SpecialMemoryArrange : false + CoreVersion : 2 + DescVersion : 2 + + ReleaseKernelMajor : "02" + ReleaseKernelMinor : "33" + MemoryType : Application + HandleTableSize: 512 + + SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode + CpuSpeed : 804MHz # 268MHz(Default)/804MHz + EnableL2Cache : true # false(default)/true + CanAccessCore2 : true + + IORegisterMapping: + - 1ff50000-1ff57fff + - 1ff70000-1ff77fff + MemoryMapping: + - 1f000000-1f5fffff:r + SystemCallAccess: + ArbitrateAddress: 34 + Break: 60 + CancelTimer: 28 + ClearEvent: 25 + ClearTimer: 29 + CloseHandle: 35 + ConnectToPort: 45 + ControlMemory: 1 + CreateAddressArbiter: 33 + CreateEvent: 23 + CreateMemoryBlock: 30 + CreateMutex: 19 + CreateSemaphore: 21 + CreateThread: 8 + CreateTimer: 26 + DuplicateHandle: 39 + ExitProcess: 3 + ExitThread: 9 + GetCurrentProcessorNumber: 17 + GetHandleInfo: 41 + GetProcessId: 53 + GetProcessIdOfThread: 54 + GetProcessIdealProcessor: 6 + GetProcessInfo: 43 + GetResourceLimit: 56 + GetResourceLimitCurrentValues: 58 + GetResourceLimitLimitValues: 57 + GetSystemInfo: 42 + GetSystemTick: 40 + GetThreadContext: 59 + GetThreadId: 55 + GetThreadIdealProcessor: 15 + GetThreadInfo: 44 + GetThreadPriority: 11 + MapMemoryBlock: 31 + OutputDebugString: 61 + QueryMemory: 2 + ReleaseMutex: 20 + ReleaseSemaphore: 22 + SendSyncRequest1: 46 + SendSyncRequest2: 47 + SendSyncRequest3: 48 + SendSyncRequest4: 49 + SendSyncRequest: 50 + SetThreadPriority: 12 + SetTimer: 27 + SignalEvent: 24 + SleepThread: 10 + UnmapMemoryBlock: 32 + WaitSynchronization1: 36 + WaitSynchronizationN: 37 + InterruptNumbers: + ServiceAccessControl: + - APT:U + - $hioFIO + - $hostio0 + - $hostio1 + - ac:u + - boss:U + - cam:u + - ir:rst + - cfg:u + - dlp:FKCL + - dlp:SRVR + - dsp::DSP + - frd:u + - fs:USER + - gsp::Gpu + - hid:USER + - http:C + - mic:u + - ndm:u + - news:s + - nwm::UDS + - ptm:u + - pxi:dev + - soc:U + - gsp::Lcd + - y2r:u + - ldr:ro + - ir:USER + - ir:u + - csnd:SND + - am:u + - ns:s + +SystemControlInfo: + Dependency: + ac: 0x0004013000002402L + am: 0x0004013000001502L + boss: 0x0004013000003402L + camera: 0x0004013000001602L + cecd: 0x0004013000002602L + cfg: 0x0004013000001702L + codec: 0x0004013000001802L + csnd: 0x0004013000002702L + dlp: 0x0004013000002802L + dsp: 0x0004013000001a02L + friends: 0x0004013000003202L + gpio: 0x0004013000001b02L + gsp: 0x0004013000001c02L + hid: 0x0004013000001d02L + http: 0x0004013000002902L + i2c: 0x0004013000001e02L + ir: 0x0004013000003302L + mcu: 0x0004013000001f02L + mic: 0x0004013000002002L + ndm: 0x0004013000002b02L + news: 0x0004013000003502L + nim: 0x0004013000002c02L + nwm: 0x0004013000002d02L + pdn: 0x0004013000002102L + ps: 0x0004013000003102L + ptm: 0x0004013000002202L + ro: 0x0004013000003702L + socket: 0x0004013000002e02L + spi: 0x0004013000002302L + ssl: 0x0004013000002f02L \ No newline at end of file diff --git a/3ds_sa2.rsf b/3ds_sa2.rsf new file mode 100644 index 0000000000..0fa7d419ba --- /dev/null +++ b/3ds_sa2.rsf @@ -0,0 +1,199 @@ +BasicInfo: + Title : "Sonic Advance 2" + CompanyCode : "00" + ProductCode : "CTR-P-SATGBA" + ContentType : Application + Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem / Homebrew + +#RomFs: + # Specifies the root path of the read only file system to include in the ROM. + #RootPath : "romfs" + +TitleInfo: + UniqueId : 0xC676A + Category : Application + +CardInfo: + MediaSize : 128MB # 128MB / 256MB / 512MB / 1GB / 2GB / 4GB + MediaType : Card1 # Card1 / Card2 + CardDevice : None # NorFlash(Pick this if you use savedata) / None + + +Option: + UseOnSD : true # true if App is to be installed to SD + FreeProductCode : true # Removes limitations on ProductCode + MediaFootPadding : false # If true CCI files are created with padding + EnableCrypt : false # Enables encryption for NCCH and CIA + EnableCompress : true # Compresses exefs code + +AccessControlInfo: + #UseExtSaveData : true + #ExtSaveDataId: 0xff3ff + #UseExtendedSaveDataAccessControl: true + #AccessibleSaveDataIds: [0x101, 0x202, 0x303, 0x404, 0x505, 0x606] + +SystemControlInfo: + SaveDataSize: 128KB + RemasterVersion: 0 + StackSize: 0x40000 + +# DO NOT EDIT BELOW HERE OR PROGRAMS WILL NOT LAUNCH (most likely) + +AccessControlInfo: + FileSystemAccess: + - Debug + - DirectSdmc + - DirectSdmcWrite + + IdealProcessor : 0 + AffinityMask : 1 + + Priority : 16 + + MaxCpu : 0x9E # Default + DisableDebug : false + EnableForceDebug : false + CanWriteSharedPage : false + CanUsePrivilegedPriority : false + CanUseNonAlphabetAndNumber : false + PermitMainFunctionArgument : false + CanShareDeviceMemory : false + RunnableOnSleep : false + SpecialMemoryArrange : false + CoreVersion : 2 + DescVersion : 2 + + ReleaseKernelMajor : "02" + ReleaseKernelMinor : "33" + MemoryType : Application + HandleTableSize: 512 + + SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode + CpuSpeed : 804MHz # 268MHz(Default)/804MHz + EnableL2Cache : true # false(default)/true + CanAccessCore2 : true + + IORegisterMapping: + - 1ff50000-1ff57fff + - 1ff70000-1ff77fff + MemoryMapping: + - 1f000000-1f5fffff:r + SystemCallAccess: + ArbitrateAddress: 34 + Break: 60 + CancelTimer: 28 + ClearEvent: 25 + ClearTimer: 29 + CloseHandle: 35 + ConnectToPort: 45 + ControlMemory: 1 + CreateAddressArbiter: 33 + CreateEvent: 23 + CreateMemoryBlock: 30 + CreateMutex: 19 + CreateSemaphore: 21 + CreateThread: 8 + CreateTimer: 26 + DuplicateHandle: 39 + ExitProcess: 3 + ExitThread: 9 + GetCurrentProcessorNumber: 17 + GetHandleInfo: 41 + GetProcessId: 53 + GetProcessIdOfThread: 54 + GetProcessIdealProcessor: 6 + GetProcessInfo: 43 + GetResourceLimit: 56 + GetResourceLimitCurrentValues: 58 + GetResourceLimitLimitValues: 57 + GetSystemInfo: 42 + GetSystemTick: 40 + GetThreadContext: 59 + GetThreadId: 55 + GetThreadIdealProcessor: 15 + GetThreadInfo: 44 + GetThreadPriority: 11 + MapMemoryBlock: 31 + OutputDebugString: 61 + QueryMemory: 2 + ReleaseMutex: 20 + ReleaseSemaphore: 22 + SendSyncRequest1: 46 + SendSyncRequest2: 47 + SendSyncRequest3: 48 + SendSyncRequest4: 49 + SendSyncRequest: 50 + SetThreadPriority: 12 + SetTimer: 27 + SignalEvent: 24 + SleepThread: 10 + UnmapMemoryBlock: 32 + WaitSynchronization1: 36 + WaitSynchronizationN: 37 + InterruptNumbers: + ServiceAccessControl: + - APT:U + - $hioFIO + - $hostio0 + - $hostio1 + - ac:u + - boss:U + - cam:u + - ir:rst + - cfg:u + - dlp:FKCL + - dlp:SRVR + - dsp::DSP + - frd:u + - fs:USER + - gsp::Gpu + - hid:USER + - http:C + - mic:u + - ndm:u + - news:s + - nwm::UDS + - ptm:u + - pxi:dev + - soc:U + - gsp::Lcd + - y2r:u + - ldr:ro + - ir:USER + - ir:u + - csnd:SND + - am:u + - ns:s + +SystemControlInfo: + Dependency: + ac: 0x0004013000002402L + am: 0x0004013000001502L + boss: 0x0004013000003402L + camera: 0x0004013000001602L + cecd: 0x0004013000002602L + cfg: 0x0004013000001702L + codec: 0x0004013000001802L + csnd: 0x0004013000002702L + dlp: 0x0004013000002802L + dsp: 0x0004013000001a02L + friends: 0x0004013000003202L + gpio: 0x0004013000001b02L + gsp: 0x0004013000001c02L + hid: 0x0004013000001d02L + http: 0x0004013000002902L + i2c: 0x0004013000001e02L + ir: 0x0004013000003302L + mcu: 0x0004013000001f02L + mic: 0x0004013000002002L + ndm: 0x0004013000002b02L + news: 0x0004013000003502L + nim: 0x0004013000002c02L + nwm: 0x0004013000002d02L + pdn: 0x0004013000002102L + ps: 0x0004013000003102L + ptm: 0x0004013000002202L + ro: 0x0004013000003702L + socket: 0x0004013000002e02L + spi: 0x0004013000002302L + ssl: 0x0004013000002f02L \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md index 7ef8b028eb..47b73110fb 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -18,6 +18,9 @@ sudo apt install build-essential binutils-arm-none-eabi gcc-arm-none-eabi libpng # Optional, if you want to compile for the PS2 you'll need sudo apt install mkisofs + +# Optional, if you want to compile for the 3DS you'll need +sudo apt install ffmpeg imagemagick ``` #### On MacOS @@ -35,6 +38,14 @@ For compiling for the PSP or PS2, ensure that you install the required SDK - [PS2DEV](https://github.com/ps2dev/ps2dev) - [PSPDEV](https://github.com/pspdev/pspdev) +#### DEVKITPRO + +For compiling for the 3DS, ensure you install DEVKITPRO and the following libraries + +- [DEVKITPRO](https://devkitpro.org/wiki/Getting_Started) +- [CITRO3D](https://github.com/devkitPro/citro3d) +- [CITRO2D](https://github.com/devkitPro/citro2d) + ## Building Clone/Download the repo @@ -76,6 +87,12 @@ Tip: On Linux and MacOS this can be opened with `wine sa2.sdl_win32.exe` 1. Run `make ps2` 1. `sa2.ps2.iso` will be created +#### For 3DS + +1. Run `make 3DS_SDL2` +1. Run `make 3ds` +1. `sa2.3ds.cia` will be created + ### GBA rom 1. Clone [agbcc](https://github.com/SAT-R/agbcc) repo into another folder diff --git a/Makefile b/Makefile index 7206ac8e10..46826d6c53 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,33 @@ else ifeq ($(PLATFORM),sdl_psp) PREFIX := psp- else ifeq ($(PLATFORM),ps2) PREFIX := mips64r5900el-ps2-elf- +else ifeq ($(PLATFORM),3ds) + PREFIX := arm-none-eabi- + TOOLCHAIN := $(DEVKITARM) + include $(DEVKITARM)/3ds_rules + PORTLIBS := $(DEVKITPRO)/portlibs/3ds + ifneq (,$(TOOLCHAIN)) + ifneq ($(wildcard $(TOOLCHAIN)/bin),) + export PATH := $(TOOLCHAIN)/bin:$(PATH) + endif + endif + + ifeq ($(GAME), GAME_SA1) + APP_TITLE := Sonic Advance + APP_DESCRIPTION := Decompilation of Sonic Advance + APP_AUTHOR := SAT-R + ICON := graphics/sa1/obj_tiles/4bpp/a0805_f001.png + SCALE := 200% + FFMPEG_FLAGS := -t 1.4 + else ifeq ($(GAME), GAME_SA2) + APP_TITLE := Sonic Advance 2 + APP_DESCRIPTION := Decompilation of Sonic Advance 2 + APP_AUTHOR := SAT-R + ICON := graphics/sa2/obj_tiles/4bpp/anim_1125/f000.png + SCALE := 300% + endif +# ICON := icon.png + else # Native ifneq ($(PLATFORM),sdl) @@ -116,6 +143,10 @@ SDL_MINGW_LIB := $(SDL_MINGW_PKG)/lib SDL_MINGW_FLAGS := -I$(SDL_MINGW_INCLUDE) -D_THREAD_SAFE SDL_MINGW_LIBS := -L$(SDL_MINGW_LIB) -lSDL2main -lSDL2.dll +SDL_3DS_PKG := $(ROOT_DIR)/ext/SDL/build +SDL_3DS_INCLUDE := $(SDL_3DS_PKG)/include/SDL2 +SDL_3DS_LIB := $(SDL_3DS_PKG) + LIBABGSYSCALL_LIBS := -L$(ROOT_DIR)/libagbsyscall/build/$(PLATFORM) -lagbsyscall ### FILES ### @@ -137,6 +168,10 @@ else ifeq ($(PLATFORM),ps2) ROM := $(BUILD_NAME).$(PLATFORM).iso ELF := $(ROM:.iso=.elf) MAP := $(ROM:.iso=.map) +else ifeq ($(PLATFORM),3ds) +ROM := $(BUILD_NAME).$(PLATFORM).cia +ELF := $(ROM:.cia=.elf) +MAP := $(ROM:.cia=.map) else ROM := $(BUILD_NAME).$(PLATFORM).exe ELF := $(ROM:.exe=.elf) @@ -180,15 +215,17 @@ endif ifeq ($(PLATFORM),gba) C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/*") else ifeq ($(PLATFORM),sdl) -C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/win32/*" -not -path "*/platform/ps2/*") +C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/win32/*" -not -path "*/platform/ps2/*" -not -path "*/platform/3ds/*") else ifeq ($(PLATFORM),sdl_psp) -C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/win32/*" -not -path "*/platform/ps2/*") +C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/win32/*" -not -path "*/platform/ps2/*" -not -path "*/platform/3ds/*") else ifeq ($(PLATFORM),ps2) -C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/win32/*" -not -path "*/platform/pret_sdl/*") +C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/win32/*" -not -path "*/platform/pret_sdl/*" -not -path "*/platform/3ds/*") else ifeq ($(PLATFORM),sdl_win32) -C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/win32/*" -not -path "*/platform/ps2/*") +C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/win32/*" -not -path "*/platform/ps2/*" -not -path "*/platform/3ds/*") else ifeq ($(PLATFORM),win32) -C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/pret_sdl/*" -not -path "*/platform/ps2/*") +C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/pret_sdl/*" -not -path "*/platform/ps2/*" -not -path "*/platform/3ds/*") +else ifeq ($(PLATFORM),3ds) +C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS) -not -path "*/platform/win32/*" -not -path "*/platform/ps2/*" -not -path "*/platform/pret_sdl/*") else C_SRCS_IN := $(shell find $(C_SUBDIR) -name "*.c" $(C_SRC_IGNORE_PATHS)) endif @@ -273,6 +310,9 @@ else CPPFLAGS += -D TITLE_BAR=$(BUILD_NAME).$(PLATFORM) -D PLATFORM_GBA=0 -D PLATFORM_SDL=1 -D PLATFORM_WIN32=0 $(SDL_MINGW_FLAGS) else ifeq ($(PLATFORM),win32) CPPFLAGS += -D TITLE_BAR=$(BUILD_NAME).$(PLATFORM) -D PLATFORM_GBA=0 -D PLATFORM_SDL=0 -D PLATFORM_WIN32=1 + else ifeq ($(PLATFORM),3ds) + CC1FLAGS += -Wno-parentheses-equality -Wno-unused-value -g -mword-relocations -ffunction-sections -specs=3dsx.specs -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -mfpu=vfp + CPPFLAGS += -D PLATFORM_GBA=0 -D PLATFORM_SDL=1 -D PLATFORM_WIN32=0 -D __3DS__ -I$(SDL_3DS_INCLUDE) -I$(SDL_3DS_PKG)/include-config-release/SDL2 -I$(DEVKITPRO)/libctru/include endif ifeq ($(CPU_ARCH),i386) @@ -294,6 +334,8 @@ else CC1FLAGS += -O3 -funroll-loops -fomit-frame-pointer else ifeq ($(PLATFORM),ps2) CC1FLAGS += -O3 -fomit-frame-pointer + else ifeq ($(PLATFORM),3ds) + CC1FLAGS += -O3 -fomit-frame-pointer else CC1FLAGS += -O2 endif @@ -337,6 +379,8 @@ else CPP := $(CC1) -E else ifeq ($(PLATFORM), ps2) ASFLAGS += -msingle-float + else ifeq ($(PLATFORM), 3ds) + ASFLAGS += -g -march=armv6k -mfloat-abi=hard -mfpu=vfp endif # Allow file input through stdin on modern gcc/g++ and set it to "compile only" CC1FLAGS += -x c -S @@ -356,6 +400,8 @@ else ifeq ($(PLATFORM),sdl) MAP_FLAG := -Xlinker -Map= endif # Win32, PSP, PS2 +else ifeq ($(PLATFORM),3ds) + MAP_FLAG := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -mfpu=vfp -specs=3dsx.specs -g $(ARCH) -Xlinker -Map= else MAP_FLAG := -Xlinker -Map= endif @@ -373,6 +419,8 @@ else ifeq ($(PLATFORM),sdl_win32) LIBS := -mwin32 -lkernel32 -lwinmm -lmingw32 -lxinput $(LIBABGSYSCALL_LIBS) $(SDL_MINGW_LIBS) else ifeq ($(PLATFORM), win32) LIBS := -mwin32 -lkernel32 -lwinmm -lgdi32 -lxinput -lopengl32 $(LIBABGSYSCALL_LIBS) +else ifeq ($(PLATFORM), 3ds) + LIBS := -lSDL2main -lSDL2 -lm -lcitro2d -lcitro3d -lctru -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -mfpu=vfp -L$(SDL_3DS_LIB) -L$(DEVKITPRO)/libctru/lib $(LIBABGSYSCALL_LIBS) endif #### MAIN TARGETS #### @@ -456,6 +504,8 @@ ifneq ($(GAME_NAME),sa1) @$(MAKE) clean GAME_NAME=sa1 endif + $(RM) sa1.3ds.* + $(RM) sa2.3ds.* clean-tools: @$(foreach tooldir,$(TOOLDIRS),$(MAKE) clean -C $(tooldir);) @@ -484,6 +534,9 @@ tas_sdl: ; @$(MAKE) sdl TAS_TESTING=1 sdl_win32: @$(MAKE) PLATFORM=sdl_win32 CPU_ARCH=i386 + +3ds: + @$(MAKE) PLATFORM=3ds win32: ; @$(MAKE) PLATFORM=win32 CPU_ARCH=i386 @@ -563,6 +616,14 @@ else ifeq ($(PLATFORM),ps2) @printf "BOOT2 = cdrom0:\\$(PS2_GAME_CODE);1\nVER = 1.00\nVMODE = NTSC" > $(OBJ_DIR)/iso/SYSTEM.CNF @cp $< $(OBJ_DIR)/iso/$(PS2_GAME_CODE) @mkisofs -o $(ROM) $(OBJ_DIR)/iso/ +else ifeq ($(PLATFORM),3ds) + @arm-none-eabi-strip $(ELF) -o $(BUILD_NAME).3ds_strip.elf + @convert "$(ICON)" -filter point -resize $(SCALE) $(OBJ_DIR)/icon.png + @smdhtool --create "$(APP_TITLE)" "$(APP_DESCRIPTION)" "$(APP_AUTHOR)" "$(OBJ_DIR)/icon.png" $(OBJ_DIR)/$(BUILD_NAME).3ds.smdh + @ffmpeg -y -i sound/sa2/direct_sound_samples/voices/announcer/sonic_advance_2.aif $(FFMPEG_FLAGS) -ar 44100 -ac 2 -acodec pcm_s16le $(OBJ_DIR)/banner.wav + @./tools/bannertool-1.2.3-linux/bannertool makebanner -i banner.$(BUILD_NAME).png -a $(OBJ_DIR)/banner.wav -o $(OBJ_DIR)/$(BUILD_NAME).3ds.bnr + @makerom -f cia -rsf 3ds_$(BUILD_NAME).rsf -elf $(BUILD_NAME).3ds_strip.elf -banner $(OBJ_DIR)/$(BUILD_NAME).3ds.bnr -icon $(OBJ_DIR)/$(BUILD_NAME).3ds.smdh -o $(ROM) +# @3dsxtool $(ELF) $(ROM) else $(OBJCOPY) -O pei-x86-64 $< $@ endif @@ -694,6 +755,11 @@ $(SDL_MINGW_LIB): SDL2.dll: $(SDL_MINGW_LIB) cp $(SDL_MINGW_SDL_DLL) SDL2.dll +3DS_SDL2: + @mkdir -p ext + cd ext && git clone https://github.com/libsdl-org/SDL.git --branch SDL2 + cd ext/SDL && cmake -S. -Bbuild -DCMAKE_TOOLCHAIN_FILE="$(DEVKITPRO)/cmake/3DS.cmake" -DCMAKE_BUILD_TYPE=Release && cmake --build build + ### FORMATTER ### format: @@ -711,3 +777,6 @@ ctx.c: $(C_HEADERS) @for header in $(C_HEADERS); do echo "#include \"$$header\""; done > ctx.h gcc -P -E -dD -undef -nostdinc -I include -D GEN_CTX=1 -D PLATFORM_GBA=1 -D GAME=GAME_SA2 ctx.h | sed '/^#define __STDC/d' | sed '1s|^|#include \n|' > ctx.c @rm ctx.h + +bannertool: + cd tools && wget -qO- https://github.com/carstene1ns/3ds-bannertool/releases/download/1.2.3/bannertool-1.2.3-linux.tar.gz | bsdtar -xvf- \ No newline at end of file diff --git a/README.md b/README.md index 738e2761e0..1b684e8f44 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ It can also build: * :construction: **sa2.win32.exe** `make win32` (Win32 OpenGL port, not functional) * **sa2.ps2.iso** `make ps2` (PlayStation 2 port, requires [PS2DEV](https://github.com/ps2dev/ps2dev)) * **EBOOT.PBP** `make sdl_psp` (PlayStation Portable SDL port, requires [PSPDEV](https://github.com/pspdev/pspdev)) +* **sa2.3ds.cia** `make 3ds` (3DS Citro2D + SDL port, requires [LIBCTRU](https://github.com/devkitPro/libctru)) ## Current state (SA2) diff --git a/banner.sa1.png b/banner.sa1.png new file mode 100644 index 0000000000..d53064272f Binary files /dev/null and b/banner.sa1.png differ diff --git a/banner.sa2.png b/banner.sa2.png new file mode 100644 index 0000000000..36a73925ce Binary files /dev/null and b/banner.sa2.png differ diff --git a/include/gba/defines.h b/include/gba/defines.h index 0ae35e1198..6e0232569b 100644 --- a/include/gba/defines.h +++ b/include/gba/defines.h @@ -48,6 +48,9 @@ // Runs at 60fps with the "fast draw" #define DISPLAY_WIDTH 320 #define DISPLAY_HEIGHT 180 +#elif defined(__3DS__) +#define DISPLAY_WIDTH 400 +#define DISPLAY_HEIGHT 240 #else #define DISPLAY_WIDTH 426 #define DISPLAY_HEIGHT 240 diff --git a/libagbsyscall/Makefile b/libagbsyscall/Makefile index 6aa9de3dab..57c1d963a8 100644 --- a/libagbsyscall/Makefile +++ b/libagbsyscall/Makefile @@ -45,6 +45,19 @@ else ifeq ($(PLATFORM),sdl_psp) PSPSDK := $(PSPDEV)/psp/sdk export PATH := $(PSPDEV)/bin:$(PATH) PREFIX := psp- +else ifeq ($(PLATFORM),3ds) + PREFIX := arm-none-eabi- + TOOLCHAIN := $(DEVKITARM) + TOPDIR ?= $(CURDIR) +include $(DEVKITARM)/3ds_rules + PORTLIBS := $(DEVKITPRO)/portlibs/3ds + ifneq (,$(TOOLCHAIN)) + ifneq ($(wildcard $(TOOLCHAIN)/bin),) + export PATH := $(TOOLCHAIN)/bin:$(PATH) + endif + endif +include $(DEVKITARM)/3ds_rules + PORTLIBS := $(DEVKITPRO)/portlibs/3ds else ifeq ($(PLATFORM),ps2) PREFIX := mips64r5900el-ps2-elf- else ifneq ($(PLATFORM),sdl) @@ -102,6 +115,12 @@ ifneq ($(PLATFORM),gba) CPP := $(CC1) -E endif endif + + ifeq ($(PLATFORM), 3ds) + CC1FLAGS += -g -Wall -mword-relocations -ffunction-sections -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -mfpu=vfp -fomit-frame-pointer + #CPPFLAGS += -fno-rtti -fno-exceptions -g -Wall -O2 -mword-relocations -ffunction-sections + endif + endif # PLATFORM != gba @@ -111,6 +130,10 @@ ifeq ($(PLATFORM),gba) endif endif +ifeq ($(PLATFORM),3ds) + ASFLAGS := -g -march=armv6k -mfloat-abi=hard -mfpu=vfp +endif + ARFLAGS := rc SYSCALLS := IntrWait \ diff --git a/src/platform/3ds/3ds.c b/src/platform/3ds/3ds.c new file mode 100644 index 0000000000..16cb46d53d --- /dev/null +++ b/src/platform/3ds/3ds.c @@ -0,0 +1,380 @@ +#include +#include +#include +#include +#include +#include + +#include <3ds.h> +#include +#include +#include + +#include + +#include "global.h" +#include "core.h" +#include "lib/agb_flash/flash_internal.h" +#include "platform/shared/dma.h" +#include "platform/shared/input.h" +#include "platform/shared/video/gpsp_renderer.h" + +#if ENABLE_AUDIO +#include "platform/shared/audio/cgb_audio.h" +#endif + +uint16_t gameImage[DISPLAY_WIDTH * DISPLAY_HEIGHT]; + +C3D_RenderTarget *top; +C2D_Image screen; + +#define INITIAL_VIDEO_SCALE 1 +unsigned int videoScale = INITIAL_VIDEO_SCALE; +unsigned int preFullscreenVideoScale = INITIAL_VIDEO_SCALE; + +bool speedUp = false; +bool isRunning = true; +bool paused = false; +bool stepOneFrame = false; +// bool headless = false; + +double lastGameTime = 0; +double curGameTime = 0; +double fixedTimestep = 1.0 / 60.0; // 16.666667ms +double timeScale = 1.0; +double accumulator = 0.0; + +static FILE *sSaveFile = NULL; + +extern void AgbMain(void); +void DoSoftReset(void) {}; + +void VDraw(); + +static void ReadSaveFile(char *path); +static void StoreSaveFile(void); +static void CloseSaveFile(void); + +u16 Platform_GetKeyInput(void); + +static size_t posToTex(size_t index) +{ + const int width = 512; + const int x = index % DISPLAY_WIDTH; + const int y = index / DISPLAY_WIDTH; + + // printf("%i , %i \n", x, y); + + return ((((y >> 3) * (width >> 3) + (x >> 3)) << 5) + + (((x & 1) >> 1) | ((y & 1)) | ((x & 2)) | ((y & 2) << 1) | ((x & 4) << 1) | ((y & 4) << 2))); +} + +static u16 texMap[400 * 240 / 2]; + +int main(int argc, char **argv) +{ + gfxInitDefault(); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + C2D_Init(C2D_DEFAULT_MAX_OBJECTS); + C2D_Prepare(); + // osSetSpeedupEnable(true); + consoleInit(GFX_BOTTOM, NULL); + + // const char *headlessEnv = getenv("HEADLESS"); + + // if (headlessEnv && strcmp(headlessEnv, "true") == 0) { + // headless = true; + // } + + // const char *parentEnv = getenv("SIO_PARENT"); + + // if (parentEnv && strcmp(parentEnv, "true") == 0) { + // SIO_MULTI_CNT->id = 0; + // SIO_MULTI_CNT->si = 1; + // SIO_MULTI_CNT->sd = 1; + // SIO_MULTI_CNT->enable = false; + // } + + mkdir("/3ds/SonicAdvance", 0755); + ReadSaveFile("/3ds/SonicAdvance/sa2.sav"); + + // Prevent the multiplayer screen from being drawn ( see core.c:EngineInit() ) + REG_RCNT = 0x8000; + REG_KEYINPUT = 0x3FF; + + // if (headless) { + // #if ENABLE_AUDIO + // // Required or it makes an infinite loop + // cgb_audio_init(48000); + // #endif + // AgbMain(); + // return 1; + // } + + if (SDL_Init(SDL_INIT_AUDIO) < 0) { + fprintf(stderr, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); + return 1; + } + + top = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT); + C2D_TargetClear(top, C2D_Color32f(0.0f, 0.f, 0.0f, 1.0f)); + + screen.tex = malloc(sizeof(C3D_Tex)); + C3D_TexInit(screen.tex, 512, 256, GPU_RGBA5551); + C3D_TexSetFilter(screen.tex, GPU_NEAREST, GPU_NEAREST); + memset(screen.tex->data, 0, 512 * 256 * 2); + + for(int i = 0; i < DISPLAY_WIDTH * DISPLAY_HEIGHT/2; i++) { + texMap[i] = posToTex(i*2); + } + + Tex3DS_SubTexture *subtex = malloc(sizeof(Tex3DS_SubTexture)); + subtex->width = 512; + subtex->height = 256; + subtex->top = 1.f; + subtex->right = 1.f; + screen.subtex = subtex; + +#if ENABLE_AUDIO + SDL_AudioSpec want; + + SDL_memset(&want, 0, sizeof(want)); /* or SDL_zero(want) */ + want.freq = 48000; + want.format = AUDIO_S16; + want.channels = 2; + want.samples = (want.freq / 60); + cgb_audio_init(want.freq); + + if (SDL_OpenAudio(&want, 0) < 0) { + SDL_Log("Failed to open audio: %s", SDL_GetError()); + } else { + if (want.format != AUDIO_S16) /* we let this one thing change. */ + SDL_Log("We didn't get S16 audio format."); + SDL_PauseAudio(0); + } +#endif + + VDraw(); + AgbMain(); + + return 0; +} + +bool newFrameRequested = FALSE; + +// called every gba frame. we process sdl events and render as many times +// as vsync needs, then return when a new game frame is needed. +void VBlankIntrWait(void) +{ +#define HANDLE_VBLANK_INTRS() \ + ({ \ + REG_DISPSTAT |= INTR_FLAG_VBLANK; \ + RunDMAs(DMA_VBLANK); \ + if (REG_DISPSTAT & DISPSTAT_VBLANK_INTR) \ + gIntrTable[INTR_INDEX_VBLANK](); \ + REG_DISPSTAT &= ~INTR_FLAG_VBLANK; \ + }) + + // if (headless) { + // REG_VCOUNT = DISPLAY_HEIGHT + 1; + // HANDLE_VBLANK_INTRS(); + // return; + // } + + bool frameAvailable = TRUE; + bool frameDrawn = false; + + while (isRunning) { + + if (!paused || stepOneFrame) { + // double dt = fixedTimestep / timeScale; // TODO: Fix speedup + + // don't accumulate time if we already requested a new frame + // this frame cycle (emulates threaded sdl behavior) + // if (!newFrameRequested) { + // double deltaTime = 0; + + // curGameTime = SDL_GetPerformanceCounter(); + // if (stepOneFrame) { + // deltaTime = dt; + // } else { + // deltaTime = (double)((curGameTime - lastGameTime) / (double)SDL_GetPerformanceFrequency()); + // if (deltaTime > (dt * 5)) + // deltaTime = dt * 5; + // } + // lastGameTime = curGameTime; + + // accumulator += deltaTime; + // } else { + // newFrameRequested = FALSE; + // } + + // while (accumulator >= dt && aptMainLoop()) { + REG_KEYINPUT = KEYS_MASK ^ Platform_GetKeyInput(); + if (frameAvailable) { + VDraw(); + frameAvailable = FALSE; + frameDrawn = true; + + HANDLE_VBLANK_INTRS(); + + // accumulator -= dt; + } else { + newFrameRequested = TRUE; + return; + } + // } + + if (paused && stepOneFrame) { + stepOneFrame = false; + } + } + + // present + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + // C2D_TargetClear(top, C2D_Color32f(0.0f, 0.f, 0.0f, 1.0f)); + C2D_SceneBegin(top); + C2D_DrawImageAt(screen, 0, 0, 0, NULL, 1.f, 1.f); + C3D_FrameEnd(0); + + isRunning = aptMainLoop(); + } + + CloseSaveFile(); + + // SDL_DestroyWindow(sdlWindow); + free((void *)screen.tex); + free((void *)screen.subtex); + C2D_Fini(); + C3D_Fini(); + SDL_Quit(); + gfxExit(); + exit(0); +#undef HANDLE_VBLANK_INTRS +} + +static void ReadSaveFile(char *path) +{ + // Check whether the saveFile exists, and create it if not + sSaveFile = fopen(path, "r+b"); + if (sSaveFile == NULL) { + sSaveFile = fopen(path, "w+b"); + } + + fseek(sSaveFile, 0, SEEK_END); + int fileSize = ftell(sSaveFile); + fseek(sSaveFile, 0, SEEK_SET); + + // Only read as many bytes as fit inside the buffer + // or as many bytes as are in the file + int bytesToRead = (fileSize < sizeof(FLASH_BASE)) ? fileSize : sizeof(FLASH_BASE); + + int bytesRead = fread(FLASH_BASE, 1, bytesToRead, sSaveFile); + + // Fill the buffer if the savefile was just created or smaller than the buffer itself + for (int i = bytesRead; i < sizeof(FLASH_BASE); i++) { + FLASH_BASE[i] = 0xFF; + } +} + +static void StoreSaveFile() +{ + if (sSaveFile != NULL) { + fseek(sSaveFile, 0, SEEK_SET); + fwrite(FLASH_BASE, 1, sizeof(FLASH_BASE), sSaveFile); + } +} + +void Platform_StoreSaveFile(void) { StoreSaveFile(); } + +static void CloseSaveFile() +{ + if (sSaveFile != NULL) { + fclose(sSaveFile); + } +} + +u16 Platform_GetKeyInput(void) +{ + u32 newKeys = 0; + // if (joystick == NULL) + // return newKeys; + + hidScanInput(); + const u32 kHeld = hidKeysHeld(); + + // if (kHeld & KEY_A) + // newKeys |= A_BUTTON; + // if (kHeld & KEY_B) + // newKeys |= B_BUTTON; + // if (kHeld & KEY_Y) + // newKeys |= A_BUTTON; + // if (kHeld & KEY_SELECT) + // newKeys |= SELECT_BUTTON; + // if (kHeld & KEY_START) + // newKeys |= START_BUTTON; + // if (kHeld & KEY_R) + // newKeys |= R_BUTTON; + // if (kHeld & KEY_L) + // newKeys |= L_BUTTON; + // if (kHeld & KEY_UP) + // newKeys |= DPAD_UP; + // if (kHeld & KEY_DOWN) + // newKeys |= DPAD_DOWN; + // if (kHeld & KEY_LEFT) + // newKeys |= DPAD_LEFT; + // if (kHeld & KEY_RIGHT) + // newKeys |= DPAD_RIGHT; + + newKeys = kHeld + & 0b1111111111; // buttons that are on the GBA and 3DS are mapped the same way, so this way will save instructions from 35 to 9 + newKeys |= (kHeld >> 24) & 0xF0; // allow circle pad to act as DPAD + if (kHeld & KEY_Y) // psp did this too so might aswell + newKeys |= A_BUTTON; + return (u16)newKeys; +} + +void Platform_QueueAudio(const s16 *data, uint32_t bytesCount) +{ + // if (headless) { + // return; + // } + // Reset the audio buffer if we are 10 frames out of sync + // If this happens it suggests there was some OS level lag + // in playing audio. The queue length should remain stable at < 10 otherwise + if (SDL_GetQueuedAudioSize(1) > (bytesCount * 10)) { + SDL_ClearQueuedAudio(1); + } + + SDL_QueueAudio(1, data, bytesCount); + // printf("Queueing %d\n, QueueSize %d\n", bytesCount, SDL_GetQueuedAudioSize(1)); +} + +// when i tried to time this on a 3ds, the lookup table and calculating manually both took the same amount of ticks and time so I have no +// clue which is faster but the lookup table was faster on azahar +void VDraw() +{ + gpsp_draw_frame(gameImage); + + // for (int i = 0; i < DISPLAY_WIDTH * DISPLAY_HEIGHT; i++) { + // // const u8 red = gameImage[i] & 0x1F; + // // const u8 green = (gameImage[i] >> 5) & 0x1F; + // // const u8 blue = (gameImage[i] >> 10) & 0x1F; + // // const u8 alpha = (gameImage[i] >> 15) & 0x1; + // // ((u16 *)screen.tex->data)[posToTex[i]] = 1 | (blue << 1) | (green << 6) | (red << 11); + // } + + // for (int i = 0; i < (DISPLAY_WIDTH * DISPLAY_HEIGHT) / 2; i++) { + // const u32 pixelPair = ((u32 *)gameImage)[i]; + // ((u32 *)screen.tex->data)[posToTex(i * 2)] + // = 0x10001 | ((pixelPair >> 9) & 0x3E003E) | ((pixelPair << 1) & 0x7C007C0) | ((pixelPair << 11) & 0xF800F800); + // } + + for (int i = 0; i < (DISPLAY_WIDTH * DISPLAY_HEIGHT) / 2; i++) { + const u32 pixelPair = ((u32 *)gameImage)[i]; + ((u32 *)screen.tex->data)[texMap[i]] + = 0x10001 | ((pixelPair >> 9) & 0x3E003E) | ((pixelPair << 1) & 0x7C007C0) | ((pixelPair << 11) & 0xF800F800); + } + + REG_VCOUNT = DISPLAY_HEIGHT + 1; // prep for being in VBlank period +} \ No newline at end of file diff --git a/src/platform/3ds/README.txt b/src/platform/3ds/README.txt new file mode 100644 index 0000000000..11d4506a3e --- /dev/null +++ b/src/platform/3ds/README.txt @@ -0,0 +1,4 @@ +The source code in this folder is from: +https://github.com/Kurausukun/pokeemerald/tree/pc_port + +Thanks to camthesaxman, Kurausukun, Pidgey and NT_x86 for allowing us to use the code! diff --git a/tools/preproc/preproc.cpp b/tools/preproc/preproc.cpp index 86b71a0c23..8c7aef2a89 100644 --- a/tools/preproc/preproc.cpp +++ b/tools/preproc/preproc.cpp @@ -147,7 +147,8 @@ int main(int argc, char **argv) || (!platform.compare("sdl")) || (!platform.compare("sdl_win32")) || (!platform.compare("sdl_psp")) - || (!platform.compare("ps2"))) + || (!platform.compare("ps2")) + || (!platform.compare("3ds"))) { fullRGBA = false; } else if(!platform.compare("win32")) {