Add a basic 3DS port

Was a lot easier than I expected. The software renderer is such a
godsend for quick-and-dirty ports.

Oddly enough, even on an old3DS, this actually almost runs at full
speed (hits around 40FPS in Mimiga Village).

There's a lot of work left to do before this is actually useable:
* There's no audio
* You can't save
* A proper hardware-accelerated renderer is needed
This commit is contained in:
Clownacy 2020-10-11 00:09:22 +01:00
parent 49027c4ab7
commit f5b3b2e0d0
3 changed files with 230 additions and 1 deletions

View file

@ -26,7 +26,7 @@ option(FREETYPE_FONTS "Use FreeType2 to render the DejaVu Mono (English) or Migu
set(BACKEND_RENDERER "SDLTexture" CACHE STRING "Which renderer the game should use: 'OpenGL3' for an OpenGL 3.2 renderer, 'OpenGLES2' for an OpenGL ES 2.0 renderer, 'SDLTexture' for SDL2's hardware-accelerated Texture API, 'SDLSurface' for SDL2's software-rendered Surface API, 'Wii U' for the Wii U's hardware-accelerated GX2 API, or 'Software' for a handwritten software renderer") set(BACKEND_RENDERER "SDLTexture" CACHE STRING "Which renderer the game should use: 'OpenGL3' for an OpenGL 3.2 renderer, 'OpenGLES2' for an OpenGL ES 2.0 renderer, 'SDLTexture' for SDL2's hardware-accelerated Texture API, 'SDLSurface' for SDL2's software-rendered Surface API, 'Wii U' for the Wii U's hardware-accelerated GX2 API, or 'Software' for a handwritten software renderer")
set(BACKEND_AUDIO "SDL2" CACHE STRING "Which audio backend the game should use: 'SDL2', 'miniaudio', 'WiiU-Hardware', 'WiiU-Software', or 'Null'") set(BACKEND_AUDIO "SDL2" CACHE STRING "Which audio backend the game should use: 'SDL2', 'miniaudio', 'WiiU-Hardware', 'WiiU-Software', or 'Null'")
set(BACKEND_PLATFORM "SDL2" CACHE STRING "Which platform backend the game should use: 'SDL2', 'GLFW3', 'WiiU', or 'Null'") set(BACKEND_PLATFORM "SDL2" CACHE STRING "Which platform backend the game should use: 'SDL2', 'GLFW3', 'WiiU', '3DS', or 'Null'")
option(LTO "Enable link-time optimisation" OFF) option(LTO "Enable link-time optimisation" OFF)
option(PKG_CONFIG_STATIC_LIBS "On platforms with pkg-config, static-link the dependencies (good for Windows builds, so you don't need to bundle DLL files)" OFF) option(PKG_CONFIG_STATIC_LIBS "On platforms with pkg-config, static-link the dependencies (good for Windows builds, so you don't need to bundle DLL files)" OFF)
@ -393,6 +393,11 @@ elseif(BACKEND_PLATFORM MATCHES "WiiU")
"src/Backends/Controller/WiiU.cpp" "src/Backends/Controller/WiiU.cpp"
"src/Backends/Platform/WiiU.cpp" "src/Backends/Platform/WiiU.cpp"
) )
elseif(BACKEND_PLATFORM MATCHES "3DS")
target_sources(CSE2 PRIVATE
"src/Backends/Controller/Null.cpp"
"src/Backends/Platform/3DS.cpp"
)
elseif(BACKEND_PLATFORM MATCHES "Null") elseif(BACKEND_PLATFORM MATCHES "Null")
target_sources(CSE2 PRIVATE target_sources(CSE2 PRIVATE
"src/Backends/Controller/Null.cpp" "src/Backends/Controller/Null.cpp"
@ -417,6 +422,8 @@ elseif(BACKEND_PLATFORM MATCHES "GLFW3" AND BACKEND_RENDERER MATCHES "Software")
elseif(BACKEND_PLATFORM MATCHES "WiiU" AND BACKEND_RENDERER MATCHES "WiiU") elseif(BACKEND_PLATFORM MATCHES "WiiU" AND BACKEND_RENDERER MATCHES "WiiU")
elseif(BACKEND_PLATFORM MATCHES "WiiU" AND BACKEND_RENDERER MATCHES "Software") elseif(BACKEND_PLATFORM MATCHES "WiiU" AND BACKEND_RENDERER MATCHES "Software")
target_sources(CSE2 PRIVATE "src/Backends/Rendering/Window/Software/WiiU.cpp") target_sources(CSE2 PRIVATE "src/Backends/Rendering/Window/Software/WiiU.cpp")
elseif(BACKEND_PLATFORM MATCHES "3DS" AND BACKEND_RENDERER MATCHES "Software")
target_sources(CSE2 PRIVATE "src/Backends/Rendering/Window/Software/3DS.cpp")
elseif(BACKEND_PLATFORM MATCHES "Null" AND BACKEND_RENDERER MATCHES "Software") elseif(BACKEND_PLATFORM MATCHES "Null" AND BACKEND_RENDERER MATCHES "Software")
target_sources(CSE2 PRIVATE "src/Backends/Rendering/Window/Software/Null.cpp") target_sources(CSE2 PRIVATE "src/Backends/Rendering/Window/Software/Null.cpp")
else() else()

View file

@ -0,0 +1,147 @@
#include "../Misc.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <3ds.h>
bool Backend_Init(void (*drag_and_drop_callback)(const char *path), void (*window_focus_callback)(bool focus))
{
(void)drag_and_drop_callback;
(void)window_focus_callback;
hidInit();
gfxInitDefault();
consoleInit(GFX_TOP, NULL);
Result rc = romfsInit();
if (rc == 0)
{
return true;
}
else
{
Backend_PrintError("Could not initialise romfs");
}
return false;
}
void Backend_Deinit(void)
{
romfsExit();
gfxExit();
hidExit();
}
void Backend_PostWindowCreation(void)
{
}
bool Backend_GetBasePath(std::string *string_buffer)
{
*string_buffer = "romfs:";
return true;
}
void Backend_HideMouse(void)
{
}
void Backend_SetWindowIcon(const unsigned char *rgb_pixels, size_t width, size_t height)
{
(void)rgb_pixels;
(void)width;
(void)height;
}
void Backend_SetCursor(const unsigned char *rgba_pixels, size_t width, size_t height)
{
(void)rgba_pixels;
(void)width;
(void)height;
}
void Backend_EnableDragAndDrop(void)
{
}
bool Backend_SystemTask(bool active)
{
(void)active;
return aptMainLoop();
}
void Backend_GetKeyboardState(bool *keyboard_state)
{
memset(keyboard_state, 0, sizeof(bool) * BACKEND_KEYBOARD_TOTAL);
// Read controller
hidScanInput();
u32 buttons = hidKeysHeld();
keyboard_state[BACKEND_KEYBOARD_UP] |= buttons & KEY_UP;
keyboard_state[BACKEND_KEYBOARD_DOWN] |= buttons & KEY_DOWN;
keyboard_state[BACKEND_KEYBOARD_LEFT] |= buttons & KEY_LEFT;
keyboard_state[BACKEND_KEYBOARD_RIGHT] |= buttons & KEY_RIGHT;
keyboard_state[BACKEND_KEYBOARD_Z] |= buttons & KEY_B; // Jump
keyboard_state[BACKEND_KEYBOARD_X] |= buttons & KEY_Y; // Shoot
keyboard_state[BACKEND_KEYBOARD_Q] |= buttons & (KEY_A | KEY_START); // Inventory
keyboard_state[BACKEND_KEYBOARD_W] |= buttons & (KEY_X | KEY_SELECT); // Map
keyboard_state[BACKEND_KEYBOARD_A] |= buttons & (KEY_L | KEY_ZL | KEY_CSTICK_LEFT); // Weapon left
keyboard_state[BACKEND_KEYBOARD_S] |= buttons & (KEY_R | KEY_ZR | KEY_CSTICK_RIGHT); // Weapon right
}
void Backend_ShowMessageBox(const char *title, const char *message)
{
Backend_PrintInfo("ShowMessageBox - %s - %s", title, message);
}
ATTRIBUTE_FORMAT_PRINTF(1, 2) void Backend_PrintError(const char *format, ...)
{
char message_buffer[0x100];
va_list argument_list;
va_start(argument_list, format);
vsnprintf(message_buffer, sizeof(message_buffer), format, argument_list);
va_end(argument_list);
printf("ERROR:");
printf(message_buffer);
}
ATTRIBUTE_FORMAT_PRINTF(1, 2) void Backend_PrintInfo(const char *format, ...)
{
char message_buffer[0x100];
va_list argument_list;
va_start(argument_list, format);
vsnprintf(message_buffer, sizeof(message_buffer), format, argument_list);
va_end(argument_list);
printf("INFO:");
printf(message_buffer);
}
unsigned long Backend_GetTicks(void)
{
return svcGetSystemTick() / CPU_TICKS_PER_MSEC;
}
void Backend_Delay(unsigned int ticks)
{
// svcSleepThread measures in nanoseconds
svcSleepThread(ticks * 1000000);
}

View file

@ -0,0 +1,75 @@
#include "../Software.h"
#include <stddef.h>
#include <stdlib.h>
#include <3ds.h>
static unsigned char *framebuffer;
static size_t framebuffer_pitch;
static size_t framebuffer_width;
static size_t framebuffer_height;
bool WindowBackend_Software_CreateWindow(const char *window_title, size_t screen_width, size_t screen_height, bool fullscreen)
{
(void)window_title;
(void)fullscreen;
gfxSetDoubleBuffering(GFX_TOP, true);
gfxSetScreenFormat(GFX_TOP, GSP_BGR8_OES);
framebuffer = (unsigned char*)malloc(screen_width * screen_height * 3);
if (framebuffer != NULL)
{
framebuffer_pitch = screen_width * 3;
framebuffer_width = screen_width;
framebuffer_height = screen_height;
return true;
}
return false;
}
void WindowBackend_Software_DestroyWindow(void)
{
free(framebuffer);
}
unsigned char* WindowBackend_Software_GetFramebuffer(size_t *pitch)
{
*pitch = framebuffer_pitch;
return framebuffer;
}
void WindowBackend_Software_Display(void)
{
// Absolutely disgusting
unsigned char *actual_framebuffer = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL);
unsigned char *framebuffer_pointer = framebuffer;
const size_t offset = ((400 - framebuffer_width) / 2) * 240;
for (unsigned int h = framebuffer_height - 1; h-- != 0;)
{
for (unsigned int w = 0; w < framebuffer_width * 240; w += 240)
{
actual_framebuffer[(offset + w + h) * 3 + 2] = *framebuffer_pointer++;
actual_framebuffer[(offset + w + h) * 3 + 1] = *framebuffer_pointer++;
actual_framebuffer[(offset + w + h) * 3 + 0] = *framebuffer_pointer++;
}
}
gfxFlushBuffers();
gfxSwapBuffers();
}
void WindowBackend_Software_HandleWindowResize(size_t width, size_t height)
{
(void)width;
(void)height;
}