소스 검색

3DS Support

This time, it's official
Florian DORMONT 9 년 전
부모
커밋
700b3f941a

+ 3 - 0
.gitignore

@@ -15,3 +15,6 @@ config.mk
 
 # Imgui
 imgui.ini
+
+# 3DS
+.map

+ 1 - 1
Makefile

@@ -15,7 +15,7 @@ CFLAGS = $(CFLAGS_COMMON) -std=gnu11
 CPPFLAGS = $(CFLAGS_COMMON) -std=gnu++11
 
 LIBS = -lz
-LDFLAGS = $(CFLAGS_COMMON) -fuse-ld=gold
+LDFLAGS = -flto
 
 SRCS_C :=
 SRCS_CPP :=

+ 0 - 0
WalrusRPG.map


+ 1 - 0
external/imgui

@@ -0,0 +1 @@
+Subproject commit a0c411ffd2b8c91466abd33b10ba4cb62659c4c7

+ 1 - 0
external/imgui-backends

@@ -0,0 +1 @@
+Subproject commit d710e060d18c3764a0d855c99447c19c53d64f30

+ 0 - 1
external/rules.mk

@@ -9,4 +9,3 @@ ifneq (, $(findstring lodepng, $(DEPS)))
     INCLUDE_EXT += $(external_LOCAL_PATH)/lodepng
     SRCS_CPP += $(external_LOCAL_PATH)/lodepng/lodepng.cpp
 endif
-

+ 125 - 0
info/3ds support/citro3D_graphisms.md

@@ -0,0 +1,125 @@
+## Citro3d Graphisms dissection
+(Using [3ds-examples] as dissection material.)
+
+#### Lcd Init
+```cpp
+gfxInitDefault(); // (libctru) Inits the LCD framebuffers with default params.
+gfxSet3D(false); // Disable 3D.
+
+// Setting up the Render buffer for both screens.
+// (Citro3d)
+C3D_RenderBuf rbTop, rbBot;
+```
+### Render buffers
+#### Init
+```cpp
+// ???I suppose that's probably for the 3D stuff.
+#ifndef EXTENDED_TOPSCR_RESOLUTION
+#define TOPSCR_WIDTH 240
+#define TOPSCR_COPYFLAG 0x00001000
+#else
+#define TOPSCR_WIDTH (240*2)
+#define TOPSCR_COPYFLAG 0x01001000
+#endif
+
+// Note : supports GPU_RB_RGB565 format
+// Why 400 of height? I don't have a frigging clue
+// Actually , a screen is 400*240, so the lib inits them by height,width. Welp.
+// IIRC the screen controller is rotated, hence the inversion.
+C3D_RenderBufInit(&rbTop, TOPSCR_WIDTH, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
+C3D_RenderBufInit(&rbBot, 240, 320, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
+rbTop.clearColor = CLEAR_COLOR;
+rbBot.clearColor = CLEAR_COLOR;
+
+```
+#### Add VBO to buffer
+```cpp
+// Configure buffers
+C3D_BufInfo* bufInfo = C3D_GetBufInfo();
+BufInfo_Init(bufInfo);
+BufInfo_Add(bufInfo, myVbo, sizeof(vertex_t), 3, 0x210);
+```
+#### Clean
+```cpp
+// Clear renderbuffers
+C3D_RenderBufClear(&rbTop);
+C3D_RenderBufClear(&rbBot);
+```
+
+### Graphics initialisation
+#### Citro3D init
+```cpp
+C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); // Inits C3D. Nuff said.
+```
+
+#### Matrix binding/setting up
+```cpp
+// Ahah, I know this one! Projection and ModelView matrices.
+C3D_MtxStack projMtx, mdlvMtx;
+MtxStack_Init(&projMtx);
+// I don't have a clue what VSH_FVEC_ or VSH_ULEN_ do...
+MtxStack_Bind(&projMtx, GPU_VERTEX_SHADER, VSH_FVEC_projMtx, VSH_ULEN_projMtx);
+MtxStack_Init(&mdlvMtx);
+MtxStack_Bind(&mdlvMtx, GPU_VERTEX_SHADER, VSH_FVEC_mdlvMtx, VSH_ULEN_mdlvMtx);
+```
+Note : Found somewhere else:
+```cpp
+// Compute the projection matrix
+Mtx_OrthoTilt(&projection, 0.0, 400.0, 0.0, 240.0, 0.0, 1.0);
+```
+I suppose that bind must be for multiple screen or stuff.
+
+### Texture
+#### Init and upload
+```cpp
+C3D_Tex myTex;
+// Load the texture and bind it to the first texture unit
+C3D_TexInit(&myTex, 64, 64, GPU_RGBA8);
+// Uploads the grass binary data to the text?
+C3D_TexUpload(&myTex, grass_bin);
+```
+
+### Shaders
+#### Parse compiled shaders
+```cpp
+DVLB_s *pVsh, *pGsh;
+pVsh = DVLB_ParseFile((u32*)test_vsh_shbin, test_vsh_shbin_size);
+pGsh = DVLB_ParseFile((u32*)test_gsh_shbin, test_gsh_shbin_size);
+```
+#### Init shader
+```cpp
+shaderProgram_s shader;
+shaderProgramInit(&shader);
+shaderProgramSetVsh(&shader, &pVsh->DVLE[0]);
+// The last value is a stride value but why 3*5? Here's the solution
+// The vertex struct gputest uses pushes vertex color to index 5
+shaderProgramSetGsh(&shader, &pGsh->DVLE[0], 3*5);
+```
+
+#### Bind shader
+```cpp
+C3D_BindProgram(&shader);
+```
+
+#### Setitng shader program attributes
+```cpp
+// Prolly get the current shader program's attributes
+C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
+AttrInfo_Init(attrInfo);
+// Attributes are only linked to gputest's way to store them.
+AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // position (xyz)
+AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // texcoord (uv)
+AttrInfo_AddLoader(attrInfo, 2, GPU_FLOAT, 3); // vertex color (rgb)
+```
+
+### VBO
+#### Init and set data
+```cpp
+// static const vertex_t vertex_list[] = {...};
+// Configure VBO
+myVbo = linearAlloc(sizeof(vertex_list));
+// Looks like the VBO allows for direct access.
+memcpy(myVbo, vertex_list, sizeof(vertex_list));
+```
+
+[3ds-examples]: https://github.com/devkitPro/3ds-examples

+ 64 - 0
info/3ds support/citro3D_input.md

@@ -0,0 +1,64 @@
+## Citro3d Input dissection
+Note : it uses libctru, which provides these functions.
+Note2 : Go check hid.h, it's well documented.
+Note3 : Wonder why gputest doesn't do hidInit or hidExit.
+### Key detection
+#### Update
+```cpp
+hidScanInput();
+```
+Note : fits perfectly the input system we use on WRPG. How convenient.
+
+#### Detect
+```cpp
+u32 kDown = hidKeysDown();
+u32 kHeld = hidKeysHeld();
+kDown & KEY_START;
+```
+
+#### Keys
+
+```cpp
+enum
+{
+	KEY_A       = BIT(0),       ///< A
+	KEY_B       = BIT(1),       ///< B
+	KEY_SELECT  = BIT(2),       ///< Select
+	KEY_START   = BIT(3),       ///< Start
+	KEY_DRIGHT  = BIT(4),       ///< D-Pad Right
+	KEY_DLEFT   = BIT(5),       ///< D-Pad Left
+	KEY_DUP     = BIT(6),       ///< D-Pad Up
+	KEY_DDOWN   = BIT(7),       ///< D-Pad Down
+	KEY_R       = BIT(8),       ///< R
+	KEY_L       = BIT(9),       ///< L
+	KEY_X       = BIT(10),      ///< X
+	KEY_Y       = BIT(11),      ///< Y
+	KEY_ZL      = BIT(14),      ///< ZL (New 3DS only)
+	KEY_ZR      = BIT(15),      ///< ZR (New 3DS only)
+	KEY_TOUCH   = BIT(20),      ///< Touch (Not actually provided by HID)
+	KEY_CSTICK_RIGHT = BIT(24), ///< C-Stick Right (New 3DS only)
+	KEY_CSTICK_LEFT  = BIT(25), ///< C-Stick Left (New 3DS only)
+	KEY_CSTICK_UP    = BIT(26), ///< C-Stick Up (New 3DS only)
+	KEY_CSTICK_DOWN  = BIT(27), ///< C-Stick Down (New 3DS only)
+	KEY_CPAD_RIGHT = BIT(28),   ///< Circle Pad Right
+	KEY_CPAD_LEFT  = BIT(29),   ///< Circle Pad Left
+	KEY_CPAD_UP    = BIT(30),   ///< Circle Pad Up
+	KEY_CPAD_DOWN  = BIT(31),   ///< Circle Pad Down
+
+	// Generic catch-all directions
+	KEY_UP    = KEY_DUP    | KEY_CPAD_UP,    ///< D-Pad Up or Circle Pad Up
+	KEY_DOWN  = KEY_DDOWN  | KEY_CPAD_DOWN,  ///< D-Pad Down or Circle Pad Down
+	KEY_LEFT  = KEY_DLEFT  | KEY_CPAD_LEFT,  ///< D-Pad Left or Circle Pad Left
+	KEY_RIGHT = KEY_DRIGHT | KEY_CPAD_RIGHT, ///< D-Pad Right or Circle Pad Right
+};
+```
+
+#### Bonus : Circle Pad :
+```cpp
+/**
+ * @brief Reads the current circle pad position.
+ * @param pos Pointer to output the circle pad position to.
+ */
+void hidCircleRead(circlePosition* pos);
+```
+Neeeeat.

+ 26 - 0
info/3ds support/infos.md

@@ -0,0 +1,26 @@
+# 3DS support project documentation
+
+## Requirements
+- Devkitarm (found on [AUR][devkitarm-bin])
+- Citrus-3ds-git (found on [AUR][citrus-3ds])
+
+
+## Ideas
+- Use [citro3d]?
+  - Low-level OpenGL-like (with some flavour) API
+  - Supports stuff like VBOs.
+  - Built over [libctru].
+- Use [sf2dlib]?
+  - Opens a simple api (check for speed and GPU implementation.)
+  - Would be quite easy to plug WRPG's graphisms over it.
+- Use [cpp3ds]?
+  - Straighter-forward SFML implementation for 3DS.
+  - WOuld only have to make things like rendering offset, scaling and clipping.
+
+
+[devkitarm-bin]: https://aur.archlinux.org/packages/devkitarm-bin/
+[citrus-3ds]: https://aur.archlinux.org/packages/citrus-3ds-git/
+
+[citro3d]: https://github.com/fincs/citro3d/
+[sf2dlib]: https://github.com/xerpi/sf2dlib
+[cpp3ds]: https://github.com/cpp3ds/cpp3ds

+ 1 - 1
mkconfig

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-TARGET_LIST=("nspire" "sfml")
+TARGET_LIST=("nspire" "sfml" "3ds")
 
 create_configuration() {
 	echo "Creating $1 configuration"

+ 169 - 0
platform/3ds/Graphics.cpp

@@ -0,0 +1,169 @@
+#include "Graphics.h"
+#include "render/Pixel.h"
+#include "utility/Rect.h"
+#include "Logger.h"
+#include "utility/misc.h"
+#include "utility/minmax.h"
+#include <3ds.h>
+#include <sf2d.h>
+
+using namespace WalrusRPG; /*::Graphics*/
+using WalrusRPG::Graphics::Pixel;
+using WalrusRPG::Utils::Rect;
+
+sf2d_rendertarget *target = nullptr;
+
+inline u32 pixel2u32(const Pixel &pix)
+{
+    return RGBA8(pix.r << 3, pix.g << 2, pix.b << 3, 0xFF);
+}
+
+namespace
+{
+    constexpr int OFFSET_X = 40;
+    constexpr int OFFSET_Y = 0;
+    void set_scissor()
+    {
+        sf2d_set_scissor_test(GPU_SCISSOR_NORMAL, 80, 0, 320, 240);
+    }
+}
+
+void Graphics::init()
+{
+    // Logger::log("Graphics init");
+    sf2d_init();
+    sf2d_set_clear_color(0);
+    sf2d_set_3D(0);
+    sf2d_set_vblank_wait(1);
+    set_scissor();
+}
+
+void Graphics::deinit()
+{
+    Logger::log("Graphics deinit");
+    sf2d_free_target(target);
+    sf2d_fini();
+}
+
+void Graphics::frame_begin()
+{
+    sf2d_start_frame(GFX_TOP, GFX_LEFT);
+}
+
+void Graphics::frame_end()
+{
+    sf2d_draw_rectangle(0, 0, OFFSET_X, 240, 0xFF000000);
+    sf2d_draw_rectangle(320 + OFFSET_X, 0, OFFSET_X, 240, 0xFF000000);
+    sf2d_end_frame();
+    sf2d_swapbuffers();
+}
+
+void Graphics::put_sprite(const Texture &sheet, int x, int y, const Rect &window)
+{
+    sf2d_draw_texture_part(sheet.data, x + OFFSET_X, y + OFFSET_Y, window.x, window.y,
+                           window.width, window.height);
+}
+
+void Graphics::put_sprite_tint(const Texture &sheet, int x, int y, const Rect &window,
+                               const Pixel &color)
+{
+    sf2d_draw_texture_part_blend(sheet.data, x + OFFSET_X, y + OFFSET_Y, window.x,
+                                 window.y, window.width, window.height, pixel2u32(color));
+}
+
+void Graphics::put_sprite_clipping(const Texture &sheet, int x, int y,
+                                   const Rect &sprite_window, const Rect &clipping_window)
+{
+    sf2d_set_scissor_test(GPU_SCISSOR_NORMAL, clipping_window.x + OFFSET_X,
+                          clipping_window.y + OFFSET_Y, clipping_window.width,
+                          clipping_window.height);
+    sf2d_draw_texture_part(sheet.data, x, y, sprite_window.x, sprite_window.y,
+                           sprite_window.width, sprite_window.height);
+    set_scissor();
+}
+
+
+void Graphics::fill(const Pixel &color)
+{
+    sf2d_clear_target(target, pixel2u32(color));
+}
+
+void Graphics::put_pixel(uint16_t x, uint16_t y, const Pixel &color)
+{
+    // Why both of them at +1? If only I knew...
+    sf2d_draw_line(x, y, x+1, y+1, 1, pixel2u32(color));
+}
+
+void Graphics::put_horizontal_line(uint16_t x, uint16_t x2, uint16_t y,
+                                   const Pixel &color)
+{
+    // Because sf2dlib has issues with lines, let's port Nspire's functions.
+    if (x > x2)
+    {
+        uint16_t temp = x;
+        x = x2;
+        x2 = temp;
+    }
+    for (; x <= x2; x++)
+    {
+        put_pixel(x + OFFSET_X, y + OFFSET_Y, color);
+    }
+}
+
+void Graphics::put_vertical_line(uint16_t x, uint16_t y, uint16_t y2, const Pixel &color)
+{
+    // Because sf2dlib has issues with lines, let's port Nspire's functions.
+    if (y > y2)
+    {
+        uint16_t temp = y;
+        y = y2;
+        y2 = temp;
+    }
+    for (; y <= y2; y++)
+    {
+        put_pixel(x + OFFSET_X, y + OFFSET_Y, color);
+    }
+}
+
+void Graphics::put_line(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2,
+                        const Pixel &color)
+{
+    // Because sf2dlib has issues with lines, let's port Nspire's functions.
+    if (x == x2)
+    {
+        put_vertical_line(x + OFFSET_X, y + OFFSET_Y, y2 + OFFSET_Y, color);
+        return;
+    }
+    else if (y == y2)
+    {
+        put_horizontal_line(x + OFFSET_X, x2 + OFFSET_X, y + OFFSET_Y, color);
+        return;
+    }
+    int dx = abs(x - x2), sx = x < x2 ? 1 : -1;
+    int dy = abs(y - y2), sy = y < y2 ? 1 : -1;
+    int err = (dx > dy ? dx : -dy) / 2, e2;
+
+    while(true)
+    {
+        put_pixel(x + OFFSET_X, y + OFFSET_Y, color);
+        if (x == x2 && y == y2)
+            break;
+        e2 = err;
+        if (e2 > -dx)
+        {
+            err -= dy;
+            x += sx;
+        }
+        if (e2 < dy)
+        {
+            err += dx;
+            y += sy;
+        }
+    }
+}
+
+void Graphics::put_rectangle(const Rect &rect, const Pixel &color)
+{
+    sf2d_draw_rectangle(rect.x + OFFSET_X, rect.y + OFFSET_Y, rect.width, rect.height,
+                        pixel2u32(color));
+}

+ 71 - 0
platform/3ds/Logger.cpp

@@ -0,0 +1,71 @@
+#include "Logger.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+#include <3ds/console.h>
+
+using namespace WalrusRPG;
+
+namespace
+{
+    PrintConsole *console;
+    // TODO : Find a better name
+    /**
+     * Prints the timestamp and the message category/type.
+     */
+    void print_premessage(const char *type)
+    {
+        char date_buffer[256];
+        time_t now = time(0);
+        strftime(date_buffer, 256, "%H:%M:%S", localtime(&now));
+        printf("%s %5s : ", date_buffer, type);
+    }
+}
+
+// NOTE : I really wish there would be a better way to handle these stupid va_lists. So
+// much redundant code...
+
+void Logger::init()
+{
+    console = consoleInit(GFX_BOTTOM, NULL);
+}
+
+void Logger::log(const char *fmt, ...)
+{
+    print_premessage("  [LOG]");
+    va_list args;
+    va_start(args, fmt);
+    vprintf(fmt, args);
+    va_end(args);
+    puts("");
+}
+
+void Logger::debug(const char *fmt, ...)
+{
+    print_premessage("[DEBUG]");
+    va_list args;
+    va_start(args, fmt);
+    vprintf(fmt, args);
+    va_end(args);
+    puts("");
+}
+
+void Logger::warn(const char *fmt, ...)
+{
+    print_premessage(" [WARN]");
+    va_list args;
+    va_start(args, fmt);
+    vprintf(fmt, args);
+    va_end(args);
+    puts("");
+}
+
+void Logger::error(const char *fmt, ...)
+{
+    print_premessage("[ERROR]");
+    va_list args;
+    va_start(args, fmt);
+    vprintf(fmt, args);
+    va_end(args);
+    puts("");
+}

+ 59 - 0
platform/3ds/Quirks.cpp

@@ -0,0 +1,59 @@
+#include "Quirks.h"
+#include <cstddef>
+#include <cstring>
+#include <memory>
+#include "Logger.h"
+
+using namespace WalrusRPG;
+
+void Quirks::init(const char *argv_0)
+{
+    WalrusRPG::Logger::log("Quirks init");
+}
+
+void Quirks::deinit()
+{
+    WalrusRPG::Logger::log("Quirks deinit");
+}
+
+std::unique_ptr<char> Quirks::solve_absolute_path(const char *path)
+{
+    std::unique_ptr<char> result(new char[strlen(path) + 1]);
+    strcpy(result.get(), path);
+    return result;
+}
+
+bool Quirks::get_key(keycode_t key)
+{
+    return hidKeysDown() & key;
+}
+
+#include <stdlib.h>
+#include <unistd.h> /* for write(), also available on Windows */
+extern "C" void *emulate_cc_new(unsigned len)
+{
+    void *p = malloc(len);
+    if (p == 0)
+    {
+        /* Don't use stdio (e.g. fputs), because that may want to allocate more
+         * memory.
+         */
+        (void) !write(2, "out of memory\n", 14);
+        abort();
+    }
+    return p;
+}
+extern "C" void emulate_cc_delete(void *p)
+{
+    if (p != 0)
+        free(p);
+}
+void *operator new(unsigned len) __attribute__((alias("emulate_cc_new")));
+void *operator new[](unsigned len) __attribute__((alias("emulate_cc_new")));
+void operator delete(void *p) __attribute__((alias("emulate_cc_delete")));
+void operator delete[](void *p) __attribute__((alias("emulate_cc_delete")));
+
+extern "C" void __cxa_pure_virtual()
+{
+    // while (1);
+}

+ 36 - 0
platform/3ds/Status.cpp

@@ -0,0 +1,36 @@
+#include "Status.h"
+#include "Logger.h"
+#include "Quirks.h"
+#include "platform.h"
+#include <3ds/services/apt.h>
+
+using namespace WalrusRPG;
+
+namespace
+{
+    static bool askedToQuit;
+}
+
+void Status::init()
+{
+    Logger::log("Status init");
+    askedToQuit = false;
+}
+
+void Status::deinit()
+{
+    Logger::log("Status deinit");
+}
+
+void Status::update()
+{
+    if (!aptMainLoop())
+        askedToQuit = true;
+    if (Quirks::get_key(KEY_ZL))
+        askedToQuit = true;
+}
+
+bool Status::mustQuit()
+{
+    return askedToQuit;
+}

+ 52 - 0
platform/3ds/Texture.cpp

@@ -0,0 +1,52 @@
+#include "Texture.h"
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include "utility/misc.h"
+#include "render/Pixel.h"
+#include "lodepng.h"
+#include <3ds.h>
+#include <sf2d.h>
+
+using namespace WalrusRPG::Graphics; /*Texture*/
+using WalrusRPG::Graphics::Pixel;
+using WalrusRPG::PIAF::File;
+using WalrusRPG::Utils::Rect;
+
+#include "Logger.h"
+
+Texture::Texture(char *data) : data()
+{
+    uint16_t *data_16 = (uint16_t *) data;
+    this->data =
+        sf2d_create_texture(data_16[0], data_16[1], TEXFMT_RGB565, SF2D_PLACE_VRAM);
+    memcpy(this->data->data, &data_16[3], data_16[0] * data_16[1] * sizeof(uint16_t));
+}
+
+Texture::Texture(WalrusRPG::PIAF::File entry)
+{
+    unsigned char *pic;
+    unsigned width, height;
+
+    signed result = lodepng_decode32(&pic, &width, &height, (unsigned char *) entry.get(),
+                                     entry.file_size);
+    data =
+        sf2d_create_texture_mem_RGBA8(pic, width, height, TEXFMT_RGBA8, SF2D_PLACE_RAM);
+    free(pic);
+}
+
+Texture::~Texture()
+{
+    sf2d_free_texture(data);
+}
+
+const Rect Texture::get_dimensions()
+{
+    return {0, 0, data->width, data->height};
+}
+
+const Pixel Texture::get_pixel(unsigned x, unsigned y)
+{
+    u32 pixel = sf2d_get_pixel(data, x, y);
+    return Pixel(RGBA8_GET_R(pixel), RGBA8_GET_G(pixel), RGBA8_GET_B(pixel));
+}

+ 28 - 0
platform/3ds/Timing.cpp

@@ -0,0 +1,28 @@
+#include "Timing.h"
+#include "Logger.h"
+#include "platform.h"
+#include <cstdio>
+#include <3ds/svc.h>
+
+constexpr u64 TIMER_PRECISION = 268123480;
+constexpr u64 TIMER_SCALEDOWN = TIMER_PRECISION / TIMER_FREQ;
+
+using namespace WalrusRPG; /*Timing*/
+u64 startingTime;
+
+void Timing::init()
+{
+    Logger::log("Timing init");
+    startingTime = svcGetSystemTick();
+}
+
+void Timing::deinit()
+{
+    Logger::log("Timing deinit");
+}
+
+unsigned Timing::gettime()
+{
+    u64 t = svcGetSystemTick() - startingTime;
+    return t / TIMER_SCALEDOWN;
+}

+ 13 - 0
platform/3ds/public/platform.h

@@ -0,0 +1,13 @@
+#ifndef INCLUDE_PLATFORM_H
+#define INCLUDE_PLATFORM_H
+
+#include <cstdint>
+#include <3ds.h>
+#include <sf2d.h>
+
+#define TIMER_FREQ 10000
+
+typedef sf2d_texture *texture_data_t;
+typedef unsigned keycode_t;
+
+#endif

+ 29 - 0
platform/3ds/rules.mk

@@ -0,0 +1,29 @@
+tds_LOCAL_PATH := $(call whereami)
+include $(DEVKITARM)/3ds_rules
+
+SRCS_C += $(wildcard $(tds_LOCAL_PATH)/platform/*.c)
+SRCS_CPP += $(wildcard $(tds_LOCAL_PATH)/*.cpp)
+INCLUDE += $(tds_LOCAL_PATH)/public
+DEPS += lodepng
+
+LIBDIRS	:= $(CTRULIB) $(CURDIR)/../libsf2d $(PORTLIBS)
+INCLUDE_EXT += $(3ds_LOCAL_PATH)/public $(foreach dir,$(LIBDIRS),$(dir)/include)
+
+ARCH = -march=armv6k -mtune=mpcore -mfloat-abi=hard
+
+LIBS = -lm -lctru -lsf2d -lz
+LDFLAGS += -specs=3dsx.specs -g -march=armv6k -mtune=mpcore -mfloat-abi=hard $(ARCH) -Wl,--gc-sections,-Map,$(notdir $*.map) $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
+
+CFLAGS_COMMON += -DTARGET_3DS=1 -DARM11 -D_3DS -fno-rtti -fno-exceptions
+
+APP_TITLE		:= WalrusRPG
+APP_DESCRIPTION	:= Here we go, fellows.
+APP_AUTHOR		:= Eiyeron
+
+CC = arm-none-eabi-gcc
+CPP = arm-none-eabi-g++
+
+EXE = $(OUT)/$(NAME).3dsx
+
+run: all
+	3dslink $(EXE)

+ 2 - 1
platform/include/Logger.h

@@ -6,6 +6,7 @@ namespace WalrusRPG
     // eventually in a file (for nspire?)
     namespace Logger
     {
+        void init();
         void log(const char *fmt, ...);
         void debug(const char *fmt, ...);
         void warn(const char *fmt, ...);
@@ -13,4 +14,4 @@ namespace WalrusRPG
     }
 }
 
-#endif
+#endif

+ 4 - 0
platform/nspire/Logger.cpp

@@ -2,6 +2,10 @@
 
 using namespace WalrusRPG;
 
+void Logger::init()
+{
+}
+
 void Logger::log(const char *fmt, ...)
 {
 }

+ 1 - 0
platform/nspire/rules.mk

@@ -5,6 +5,7 @@ SRCS_CPP += $(wildcard $(nspire_LOCAL_PATH)/*.cpp)
 INCLUDE += $(nspire_LOCAL_PATH)/public
 DEPS += lodepng
 
+LDFLAGS +=  -fuse-ld=gold
 CFLAGS_COMMON += -marm -DTARGET_NSPIRE=1 -DWRPG_EXCEPTIONS=1
 YCM_EXTRA_CFLAGS := -m32 -I$(NDLESS_GIT)/ndless-sdk/include -I$(HOME)/.ndless/include
 

+ 4 - 0
platform/sfml/Logger.cpp

@@ -22,6 +22,10 @@ namespace
 // NOTE : I really wish there would be a better way to handle these stupid va_lists. So
 // much redundant code...
 
+void Logger::init()
+{
+}
+
 void Logger::log(const char *fmt, ...)
 {
     print_premessage("  [LOG]");

+ 1 - 1
platform/sfml/rules.mk

@@ -5,9 +5,9 @@ SRCS_CPP += $(wildcard $(sfml_LOCAL_PATH)/*.cpp)
 INCLUDE += $(sfml_LOCAL_PATH)/public
 
 LIBS += -lstdc++ -lsfml-window -lsfml-graphics -lsfml-system
+LDFLAGS += -fuse-ld=gold
 
 CFLAGS_COMMON += -DTARGET_SFML=1 -DWRPG_EXCEPTIONS=1
-
 CC = clang
 CPP = clang++
 

+ 1 - 1
rules.mk

@@ -33,7 +33,7 @@ $(OUT)/%.o: %.cpp | $(BUILT_SRCS)
 $(ELF): $(OBJS)
 	@mkdir -p $(dir $@)
 	@echo "CCLD: $@"
-	@+$(CC) $(LDFLAGS) $(LIBS) $^ -o $(ELF)
+	@+$(CC) $(LDFLAGS) $^ $(LIBS) -o $(ELF)
 
 clean:
 	@echo "RM: $(OUT)"

+ 7 - 2
src/engine/main.cpp

@@ -15,16 +15,18 @@ using WalrusRPG::PIAF::Archive;
 using WalrusRPG::Graphics::Texture;
 using namespace WalrusRPG::Graphics;
 
+
 int main(int argc, char *argv[])
 {
     UNUSED(argc);
+    Graphics::init();
+    Logger::init();
     Logger::log("WalrusRPG Init");
     Status::init();
-    Graphics::init();
     Timing::init();
     Quirks::init(argv[0]);
-    Text::init();
 
+    Text::init();
     Archive arc("data/wip_data.wrf");
     Texture tex(arc.get("ov.png"));
     WalrusRPG::PIAF::File f1 = arc.get("l1.bin");
@@ -59,6 +61,8 @@ int main(int argc, char *argv[])
 
     Logger::log("WalrusRPG Deinit");
     StateMachine::deinit();
+    Text::deinit();
+
     Quirks::deinit();
     Timing::deinit();
     Graphics::deinit();
@@ -66,6 +70,7 @@ int main(int argc, char *argv[])
     delete[] dungeonTest;
     delete[] dungeonTest2;
     Logger::log("WalrusRPG Exit");
+    Graphics::deinit();
 
     return 0;
 }

+ 38 - 0
src/input/Input.cpp

@@ -43,6 +43,20 @@ static InputMap key_map[] = {
     {Key::K_START, KEY_NSPIRE_DOC}, {Key::K_SELECT, KEY_NSPIRE_SCRATCHPAD},
 };
 #endif
+#ifdef TARGET_3DS
+static InputMap key_map[] = {
+    {Key::K_A, KEY_A},         {Key::K_B, KEY_B},
+    {Key::K_L, KEY_L},         {Key::K_R, KEY_R},
+
+    {Key::K_UP, KEY_UP},       {Key::K_DOWN, KEY_DOWN},
+    {Key::K_LEFT, KEY_LEFT},   {Key::K_RIGHT, KEY_RIGHT},
+
+    {Key::K_START, KEY_START}, {Key::K_SELECT, KEY_SELECT},
+};
+u32 kDown = 0;
+u32 kHeld = 0;
+u32 kUp = 0;
+#endif
 
 KeyState Input::key_get_state(Key key)
 {
@@ -62,26 +76,50 @@ KeyState Input::key_get_state(Key key)
 
 void Input::key_poll()
 {
+#ifdef TARGET_3DS
+    hidScanInput();
+    kDown = hidKeysDown();
+    kHeld = hidKeysHeld();
+    kUp = hidKeysUp();
+#else
     for (unsigned i = 0; i < K_SIZE; i++)
     {
         key_states[i].previous = key_states[i].current;
         key_states[i].current = WalrusRPG::Quirks::get_key(key_map[i].key_code);
     }
+#endif
 }
 
 bool Input::key_pressed(Key key)
 {
+#ifdef TARGET_3DS
+    return kDown & key_map[key].key_code;
+#else
     return !key_states[key].previous && key_states[key].current;
+#endif
 }
+
 bool Input::key_released(Key key)
 {
+#ifdef TARGET_3DS
+    return kUp & key_map[key].key_code;
+#else
     return key_states[key].previous && !key_states[key].current;
+#endif
 }
 bool Input::key_down(Key key)
 {
+#ifdef TARGET_3DS
+    return kHeld & key_map[key].key_code;
+#else
     return key_states[key].current;
+#endif
 }
 bool Input::key_up(Key key)
 {
+#ifdef TARGET_3DS
+    return !(~kDown & key_map[key].key_code);
+#else
     return !key_states[key].current;
+#endif
 }

+ 9 - 4
src/map/StateMap.cpp

@@ -3,6 +3,7 @@
 #include "input/Input.h"
 #include "render/Text.h"
 #include "piaf/Archive.h"
+#include "Logger.h"
 
 using WalrusRPG::States::StateMap;
 using namespace WalrusRPG;
@@ -57,12 +58,16 @@ StateMap::StateMap(int x, int y, Map &map)
 void StateMap::update(unsigned dt)
 {
     unsigned t = dt * (key_down(K_B) ? 16 : 1);
-    if (!started)
+    if (key_pressed(K_A))
     {
-        if (key_down(K_A))
-            started = true;
+        if(!started && box.state != Done)
+           started = true;
+        else if(box.state == Done)
+        {
+            started = false;
+        }
     }
-    else
+    if(started)
         box.update(t);
     camera.update(t);
 }

+ 15 - 11
src/piaf/Archive.cpp

@@ -76,6 +76,7 @@ Archive::Archive(const char *filepath)
     // Null pointer exception trigger
     if (filepath == nullptr)
     {
+        Logger::error("%s: Null path given", __FILE__);
 #ifdef WRPG_EXCEPTIONS
         throw PIAF::PIAFException("%s: Null path given", __FILE__);
 #endif
@@ -90,6 +91,7 @@ Archive::Archive(const char *filepath)
     // Again another null pointer trigger
     if (file == nullptr || file == NULL)
     {
+        Logger::error("%s: Missing file : %s", __FILE__, filepath);
 #ifdef WRPG_EXCEPTIONS
         throw PIAF::PIAFException("%s: Missing file : %s", __FILE__, filepath);
 #endif
@@ -103,6 +105,7 @@ Archive::Archive(const char *filepath)
     // File to small exception trigger
     if (filesize < 32)
     {
+        Logger::error("%s: File too small (%s): %d", __FILE__, filepath, filesize);
 #ifdef WRPG_EXCEPTIONS
         throw PIAF::PIAFException("%s: File too small (%s): %d", __FILE__, filepath,
                                   filesize);
@@ -114,16 +117,17 @@ Archive::Archive(const char *filepath)
     // Read the headers and trigger exceptions on errors
     if (fread(header_container, sizeof(char), 32, file) != 32)
     {
+        Logger::error("%s: Errorneous header : %s", __FILE__, filepath);
 #ifdef WRPG_EXCEPTIONS
         throw PIAF::PIAFException("%s: Errorneous header : %s", __FILE__, filepath);
 #endif
-    }
+  }
     // Check if the magic cookie is the same.
     // It's a first way to detect if the file is correctly an archive.
     if (strncmp(header_container, "WRPGPIAF", 8) != 0)
     {
-// TODO throw bad header
-// fprintf(stderr, "Bad header magic word\n");
+        // TODO throw bad header
+        Logger::error("%s: Magic cookie mismatch : %s", __FILE__, filepath);
 #ifdef WRPG_EXCEPTIONS
         throw PIAF::PIAFException("%s: Magic cookie mismatch : %s", __FILE__, filepath);
 #endif
@@ -133,9 +137,7 @@ Archive::Archive(const char *filepath)
     uint32_t calculated_checksum = crc32(0L, (unsigned char *) &header_container[16], 16);
     if (expected_checksum != calculated_checksum)
     {
-// TODO throw bad checksum
-// fprintf(stderr, "Bad header checksum : %x != %x\n", expected_checksum,
-// calculated_checksum);
+        Logger::error("%s: Bad checksum : %s", __FILE__, filepath);
 #ifdef WRPG_EXCEPTIONS
         throw PIAF::PIAFException("%s: Bad checksum : %s", __FILE__, filepath);
 #endif
@@ -145,8 +147,10 @@ Archive::Archive(const char *filepath)
     version = read_big_endian_value<uint32_t>(&header_container[16]);
     if (version != ARCHIVE_VERSION)
     {
-// std::exception up;
-// throw up; // haha
+        // std::exception up;
+        // throw up; // haha
+        Logger::error("%s: Wrong(%s) : %08x is not supported by %08x", __FILE__, filepath,
+                      version, ARCHIVE_VERSION);
 #ifdef WRPG_EXCEPTIONS
         throw PIAF::PIAFException("%s: Wrong(%s) : %08x is not supported by %08x",
                                   __FILE__, filepath, version, ARCHIVE_VERSION);
@@ -165,8 +169,7 @@ Archive::Archive(const char *filepath)
     uint64_t calculated_data_size = filesize - 32 - 24 * nb_files;
     if (data_size != calculated_data_size)
     {
-// fprintf(stderr, "Bad data size : expected %u, got %lld\n", data_size,
-// calculated_data_size);
+        Logger::error("Data size mismatch", __LINE__, filepath);
 #ifdef WRPG_EXCEPTIONS
         throw PIAF::PIAFException("Data size mismatch", __LINE__, filepath);
 #endif
@@ -182,6 +185,7 @@ Archive::Archive(const char *filepath)
         fseek(file, 32, SEEK_SET);
         if (fread(file_entry_data, sizeof(char), 24 * nb_files, file) < 24 * nb_files)
         {
+            Logger::error("Can't read file entry data", __LINE__, filepath);
 #ifdef WRPG_EXCEPTIONS
             throw PIAF::PIAFException("Can't read file entry data", __LINE__, filepath);
 #endif
@@ -190,7 +194,7 @@ Archive::Archive(const char *filepath)
         if (expected_filetable_checksum !=
             crc32(0L, (unsigned char *) file_entry_data, 24 * nb_files))
         {
-// fprintf(stderr, "Bad filetable checksum\n");
+            Logger::error("Bad Filetable checksum", __LINE__, filepath);
 #ifdef WRPG_EXCEPTIONS
             throw PIAF::PIAFException("Bad Filetable checksum", __LINE__, filepath);
 #endif

+ 0 - 1
src/piaf/Exceptions.cpp

@@ -13,7 +13,6 @@ PIAFException::PIAFException(const char *format, ...) : msg("")
     va_list list;
     va_start(list, format);
     vsnprintf(msg, 1024, format, list);
-    error(msg);
     va_end(list);
 }