3DS: Update to new API

This commit is contained in:
Jeffrey Pfau 2016-01-02 01:49:03 -08:00
parent 631b287b3a
commit e65d12d35e
8 changed files with 110 additions and 148 deletions

View File

@ -213,8 +213,9 @@ void GBAConfigDirectory(char* out, size_t outLength) {
snprintf(out, outLength, "/%s", projectName);
mkdir(out, 0777);
#elif defined(_3DS)
UNUSED(portable);
snprintf(out, outLength, "/%s", projectName);
FSUSER_CreateDirectory(0, sdmcArchive, FS_makePath(PATH_CHAR, out));
FSUSER_CreateDirectory(sdmcArchive, fsMakePath(PATH_ASCII, out), 0);
#else
getcwd(out, outLength);
strncat(out, PATH_SEP "portable.ini", outLength - strlen(out));

View File

@ -18,7 +18,7 @@ struct VFile3DS {
struct VDirEntry3DS {
struct VDirEntry d;
FS_dirent ent;
FS_DirectoryEntry ent;
char utf8Name[256];
};
@ -49,14 +49,15 @@ static struct VDir* _vd3dOpenDir(struct VDir* vd, const char* path);
static const char* _vd3deName(struct VDirEntry* vde);
static enum VFSType _vd3deType(struct VDirEntry* vde);
struct VFile* VFileOpen3DS(FS_archive* archive, const char* path, int flags) {
struct VFile* VFileOpen3DS(FS_Archive* archive, const char* path, int flags) {
struct VFile3DS* vf3d = malloc(sizeof(struct VFile3DS));
if (!vf3d) {
return 0;
}
FS_path newPath = FS_makePath(PATH_CHAR, path);
Result res = FSUSER_OpenFile(0, &vf3d->handle, *archive, newPath, flags, FS_ATTRIBUTE_NONE);
// TODO: Use UTF-16
FS_Path newPath = fsMakePath(PATH_ASCII, path);
Result res = FSUSER_OpenFile(&vf3d->handle, *archive, newPath, flags, 0);
if (res & 0xFFFC03FF) {
free(vf3d);
return 0;
@ -175,8 +176,9 @@ struct VDir* VDirOpen(const char* path) {
return 0;
}
FS_path newPath = FS_makePath(PATH_CHAR, path);
Result res = FSUSER_OpenDirectory(0, &vd3d->handle, sdmcArchive, newPath);
// TODO: Use UTF-16
FS_Path newPath = fsMakePath(PATH_ASCII, path);
Result res = FSUSER_OpenDirectory(&vd3d->handle, sdmcArchive, newPath);
if (res & 0xFFFC03FF) {
free(vd3d);
return 0;
@ -207,8 +209,9 @@ static bool _vd3dClose(struct VDir* vd) {
static void _vd3dRewind(struct VDir* vd) {
struct VDir3DS* vd3d = (struct VDir3DS*) vd;
FSDIR_Close(vd3d->handle);
FS_path newPath = FS_makePath(PATH_CHAR, vd3d->path);
FSUSER_OpenDirectory(0, &vd3d->handle, sdmcArchive, newPath);
// TODO: Use UTF-16
FS_Path newPath = fsMakePath(PATH_ASCII, vd3d->path);
FSUSER_OpenDirectory(&vd3d->handle, sdmcArchive, newPath);
}
static struct VDirEntry* _vd3dListNext(struct VDir* vd) {
@ -264,7 +267,7 @@ static const char* _vd3deName(struct VDirEntry* vde) {
static enum VFSType _vd3deType(struct VDirEntry* vde) {
struct VDirEntry3DS* vd3de = (struct VDirEntry3DS*) vde;
if (vd3de->ent.isDirectory) {
if (vd3de->ent.attributes & FS_ATTRIBUTE_DIRECTORY) {
return VFS_DIRECTORY;
}
return VFS_FILE;

View File

@ -10,8 +10,8 @@
#include <3ds.h>
extern FS_archive sdmcArchive;
extern FS_Archive sdmcArchive;
struct VFile* VFileOpen3DS(FS_archive* archive, const char* path, int flags);
struct VFile* VFileOpen3DS(FS_Archive* archive, const char* path, int flags);
#endif

View File

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <3ds.h>
#include <3ds/gpu/gpu.h>
#include <3ds/gpu/gx.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@ -42,57 +44,12 @@ static int pendingEvents = 0;
static const struct ctrTexture* activeTexture = NULL;
static u32 _f24FromFloat(float f) {
u32 i;
memcpy(&i, &f, 4);
u32 mantissa = (i << 9) >> 9;
s32 exponent = (i << 1) >> 24;
u32 sign = (i << 0) >> 31;
// Truncate mantissa
mantissa >>= 7;
// Re-bias exponent
exponent = exponent - 127 + 63;
if (exponent < 0) {
// Underflow: flush to zero
return sign << 23;
} else if (exponent > 0x7F) {
// Overflow: saturate to infinity
return sign << 23 | 0x7F << 16;
}
return sign << 23 | exponent << 16 | mantissa;
}
static u32 _f31FromFloat(float f) {
u32 i;
memcpy(&i, &f, 4);
u32 mantissa = (i << 9) >> 9;
s32 exponent = (i << 1) >> 24;
u32 sign = (i << 0) >> 31;
// Re-bias exponent
exponent = exponent - 127 + 63;
if (exponent < 0) {
// Underflow: flush to zero
return sign << 30;
} else if (exponent > 0x7F) {
// Overflow: saturate to infinity
return sign << 30 | 0x7F << 23;
}
return sign << 30 | exponent << 23 | mantissa;
}
void ctrClearPending(int events) {
int toClear = events & pendingEvents;
if (toClear & (1 << GSPEVENT_PSC0)) {
if (toClear & (1 << GSPGPU_EVENT_PSC0)) {
gspWaitForPSC0();
}
if (toClear & (1 << GSPEVENT_PPF)) {
if (toClear & (1 << GSPGPU_EVENT_PPF)) {
gspWaitForPPF();
}
pendingEvents ^= toClear;
@ -103,39 +60,39 @@ static void _GPU_SetFramebuffer(intptr_t colorBuffer, intptr_t depthBuffer, u16
u32 buf[4];
// Unknown
GPUCMD_AddWrite(GPUREG_0111, 0x00000001);
GPUCMD_AddWrite(GPUREG_0110, 0x00000001);
GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 0x00000001);
GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_INVALIDATE, 0x00000001);
// Set depth/color buffer address and dimensions
buf[0] = depthBuffer >> 3;
buf[1] = colorBuffer >> 3;
buf[2] = (0x01) << 24 | ((h-1) & 0xFFF) << 12 | (w & 0xFFF) << 0;
GPUCMD_AddIncrementalWrites(GPUREG_DEPTHBUFFER_LOC, buf, 3);
GPUCMD_AddWrite(GPUREG_006E, buf[2]);
GPUCMD_AddWrite(GPUREG_RENDERBUF_DIM, buf[2]);
// Set depth/color buffer pixel format
GPUCMD_AddWrite(GPUREG_DEPTHBUFFER_FORMAT, 3 /* D248S */ );
GPUCMD_AddWrite(GPUREG_COLORBUFFER_FORMAT, 0 /* RGBA8 */ << 16 | 2 /* Unknown */);
GPUCMD_AddWrite(GPUREG_011B, 0); // Unknown
GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_BLOCK32, 0); // Unknown
// Enable color/depth buffers
buf[0] = colorBuffer != 0 ? 0xF : 0x0;
buf[1] = buf[0];
buf[2] = depthBuffer != 0 ? 0x2 : 0x0;
buf[3] = buf[2];
GPUCMD_AddIncrementalWrites(GPUREG_0112, buf, 4);
GPUCMD_AddIncrementalWrites(GPUREG_COLORBUFFER_READ, buf, 4);
}
static void _GPU_SetViewportEx(u16 x, u16 y, u16 w, u16 h) {
u32 buf[4];
buf[0] = _f24FromFloat(w / 2.0f);
buf[1] = _f31FromFloat(2.0f / w) << 1;
buf[2] = _f24FromFloat(h / 2.0f);
buf[3] = _f31FromFloat(2.0f / h) << 1;
GPUCMD_AddIncrementalWrites(GPUREG_0041, buf, 4);
buf[0] = f32tof24(w / 2.0f);
buf[1] = f32tof31(2.0f / w) << 1;
buf[2] = f32tof24(h / 2.0f);
buf[3] = f32tof31(2.0f / h) << 1;
GPUCMD_AddIncrementalWrites(GPUREG_VIEWPORT_WIDTH, buf, 4);
GPUCMD_AddWrite(GPUREG_0068, (y & 0xFFFF) << 16 | (x & 0xFFFF) << 0);
GPUCMD_AddWrite(GPUREG_VIEWPORT_XY, (y & 0xFFFF) << 16 | (x & 0xFFFF) << 0);
buf[0] = 0;
buf[1] = 0;
@ -259,7 +216,7 @@ void ctrGpuBeginFrame(int screen) {
fw = 320;
}
_GPU_SetFramebuffer(osConvertVirtToPhys((u32)gpuColorBuffer[screen]), 0, 240, fw);
_GPU_SetFramebuffer(osConvertVirtToPhys(gpuColorBuffer[screen]), 0, 240, fw);
}
void ctrGpuBeginDrawing(void) {
@ -282,8 +239,8 @@ void ctrGpuBeginDrawing(void) {
GPU_SetAlphaTest(false, GPU_ALWAYS, 0);
// Unknown
GPUCMD_AddMaskedWrite(GPUREG_0062, 0x1, 0);
GPUCMD_AddWrite(GPUREG_0118, 0);
GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_TEST1, 0x1, 0);
GPUCMD_AddWrite(GPUREG_EARLYDEPTH_TEST2, 0);
GPU_SetTexEnv(0,
GPU_TEVSOURCES(GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0), // RGB
@ -299,7 +256,7 @@ void ctrGpuBeginDrawing(void) {
_setDummyTexEnv(5);
// Configure vertex attribute format
u32 bufferOffsets[] = { osConvertVirtToPhys((u32)ctrVertexBuffer) - VRAM_BASE };
u32 bufferOffsets[] = { osConvertVirtToPhys(ctrVertexBuffer) - VRAM_BASE };
u64 arrayTargetAttributes[] = { 0x210 };
u8 numAttributesInArray[] = { 3 };
GPU_SetAttributeBuffers(
@ -332,29 +289,29 @@ void ctrGpuEndFrame(int screen, void* outputFramebuffer, int w, int h) {
const u32 GX_CROP_INPUT_LINES = (1 << 2);
ctrClearPending(1 << GSPEVENT_PSC0);
ctrClearPending(1 << GSPEVENT_PPF);
ctrClearPending(1 << GSPGPU_EVENT_PSC0);
ctrClearPending(1 << GSPGPU_EVENT_PPF);
GX_SetDisplayTransfer(NULL,
GX_DisplayTransfer(
colorBuffer, GX_BUFFER_DIM(240, fw),
outputFramebuffer, GX_BUFFER_DIM(h, w),
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) |
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) |
GX_CROP_INPUT_LINES);
pendingEvents |= (1 << GSPEVENT_PPF);
pendingEvents |= (1 << GSPGPU_EVENT_PPF);
}
void ctrGpuEndDrawing(void) {
ctrClearPending(1 << GSPEVENT_PPF);
ctrClearPending(1 << GSPGPU_EVENT_PPF);
gfxSwapBuffersGpu();
gspWaitForEvent(GSPEVENT_VBlank0, false);
gspWaitForEvent(GSPGPU_EVENT_VBlank0, false);
void* gpuColorBuffer0End = (char*)gpuColorBuffer[0] + 240 * 400 * 4;
void* gpuColorBuffer1End = (char*)gpuColorBuffer[1] + 240 * 320 * 4;
GX_SetMemoryFill(NULL,
GX_MemoryFill(
gpuColorBuffer[0], 0x00000000, gpuColorBuffer0End, GX_FILL_32BIT_DEPTH | GX_FILL_TRIGGER,
gpuColorBuffer[1], 0x00000000, gpuColorBuffer1End, GX_FILL_32BIT_DEPTH | GX_FILL_TRIGGER);
pendingEvents |= 1 << GSPEVENT_PSC0;
pendingEvents |= 1 << GSPGPU_EVENT_PSC0;
}
void ctrSetViewportSize(s16 w, s16 h) {
@ -382,7 +339,7 @@ void ctrActivateTexture(const struct ctrTexture* texture) {
GPU_SetTextureEnable(GPU_TEXUNIT0);
GPU_SetTexture(
GPU_TEXUNIT0, (u32*)osConvertVirtToPhys((u32)texture->data),
GPU_TEXUNIT0, (u32*)osConvertVirtToPhys(texture->data),
texture->width, texture->height,
GPU_TEXTURE_MAG_FILTER(texture->filter) | GPU_TEXTURE_MIN_FILTER(texture->filter) |
GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_BORDER) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_BORDER),
@ -444,15 +401,15 @@ void ctrFlushBatch(void) {
return;
}
ctrClearPending((1 << GSPEVENT_PSC0));
ctrClearPending((1 << GSPGPU_EVENT_PSC0));
GSPGPU_FlushDataCache(NULL, (u8*)ctrVertexBuffer, VERTEX_INDEX_BUFFER_SIZE);
GPU_DrawElements(GPU_UNKPRIM, (u32*)(osConvertVirtToPhys((u32)ctrIndexBuffer) - VRAM_BASE), ctrNumQuads * 6);
GSPGPU_FlushDataCache(ctrVertexBuffer, VERTEX_INDEX_BUFFER_SIZE);
GPU_DrawElements(GPU_GEOMETRY_PRIM, (u32*)(osConvertVirtToPhys(ctrIndexBuffer) - VRAM_BASE), ctrNumQuads * 6);
GPU_FinishDrawing();
GPUCMD_Finalize();
GSPGPU_FlushDataCache(NULL, (u8*)gpuCommandList, COMMAND_LIST_LENGTH * sizeof(u32));
GPUCMD_FlushAndRun(NULL);
GSPGPU_FlushDataCache((u8*)gpuCommandList, COMMAND_LIST_LENGTH * sizeof(u32));
GPUCMD_FlushAndRun();
gspWaitForP3D();

View File

@ -26,18 +26,23 @@
extern char* fake_heap_start;
extern char* fake_heap_end;
u32 __linear_heap;
u32 __heapBase;
static u32 __heap_size = 0x02400000;
static u32 __linear_heap_size = 0x01400000;
extern u32 __ctru_linear_heap;
extern u32 __ctru_heap;
extern u32 __ctru_heap_size;
extern u32 __ctru_linear_heap_size;
static u32 __custom_heap_size = 0x02400000;
static u32 __custom_linear_heap_size = 0x01400000;
extern void (*__system_retAddr)(void);
void __destroy_handle_list(void);
void envDestroyHandles(void);
void __appExit();
void __libc_fini_array(void);
Result __sync_fini(void) __attribute__((weak));
uint32_t* romBuffer;
size_t romBufferSize;
@ -58,36 +63,16 @@ bool allocateRomBuffer(void) {
void __system_allocateHeaps() {
u32 tmp=0;
__ctru_heap_size = __custom_heap_size;
__ctru_linear_heap_size = __custom_linear_heap_size;
// Allocate the application heap
__heapBase = 0x08000000;
svcControlMemory(&tmp, __heapBase, 0x0, __heap_size, MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
__ctru_heap = 0x08000000;
svcControlMemory(&tmp, __ctru_heap, 0x0, __ctru_heap_size, MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
// Allocate the linear heap
svcControlMemory(&__linear_heap, 0x0, 0x0, __linear_heap_size, MEMOP_ALLOC_LINEAR, MEMPERM_READ | MEMPERM_WRITE);
svcControlMemory(&__ctru_linear_heap, 0x0, 0x0, __ctru_linear_heap_size, MEMOP_ALLOC_LINEAR, MEMPERM_READ | MEMPERM_WRITE);
// Set up newlib heap
fake_heap_start = (char*)__heapBase;
fake_heap_end = fake_heap_start + __heap_size;
}
void __attribute__((noreturn)) __libctru_exit(int rc)
{
UNUSED(rc);
u32 tmp=0;
// Unmap the linear heap
svcControlMemory(&tmp, __linear_heap, 0x0, __linear_heap_size, MEMOP_FREE, 0x0);
// Unmap the application heap
svcControlMemory(&tmp, __heapBase, 0x0, __heap_size, MEMOP_FREE, 0x0);
// Close some handles
__destroy_handle_list();
// Jump to the loader if it provided a callback
if (__system_retAddr)
__system_retAddr();
// Since above did not jump, end this process
svcExitProcess();
fake_heap_start = (char*)__ctru_heap;
fake_heap_end = fake_heap_start + __ctru_heap_size;
}

View File

@ -31,8 +31,8 @@ struct GUIFont* GUIFontCreate(void) {
tex->width = 256;
tex->height = 128;
GSPGPU_FlushDataCache(NULL, (u8*)font, font_size);
GX_RequestDma(NULL, (u32*)font, tex->data, font_size);
GSPGPU_FlushDataCache(font, font_size);
GX_RequestDma((u32*) font, tex->data, font_size);
gspWaitForDMA();
return guiFont;

View File

@ -18,6 +18,7 @@
#include "ctr-gpu.h"
#include <3ds.h>
#include <3ds/gpu/gx.h>
static enum ScreenMode {
SM_PA_BOTTOM,
@ -32,7 +33,7 @@ static enum ScreenMode {
#define AUDIO_SAMPLES 0x80
#define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 24)
FS_archive sdmcArchive;
FS_Archive sdmcArchive;
static struct GBA3DSRotationSource {
struct GBARotationSource d;
@ -76,8 +77,8 @@ static void _csndPlaySound(u32 flags, u32 sampleRate, float vol, void* left, voi
flags |= SOUND_ONE_SHOT;
}
pleft = osConvertVirtToPhys((u32) left);
pright = osConvertVirtToPhys((u32) right);
pleft = osConvertVirtToPhys(left);
pright = osConvertVirtToPhys(right);
u32 timer = CSND_TIMER(sampleRate);
if (timer < 0x0042) {
@ -149,8 +150,8 @@ static void _drawEnd(void) {
static int _batteryState(void) {
u8 charge;
u8 adapter;
PTMU_GetBatteryLevel(0, &charge);
PTMU_GetBatteryChargeState(0, &adapter);
PTMU_GetBatteryLevel(&charge);
PTMU_GetBatteryChargeState(&adapter);
int state = 0;
if (adapter) {
state |= BATTERY_CHARGING;
@ -284,8 +285,8 @@ static void _drawFrame(struct GBAGUIRunner* runner, bool faded) {
void* outputBuffer = renderer.outputBuffer;
struct ctrTexture* tex = &gbaOutputTexture;
GSPGPU_FlushDataCache(NULL, outputBuffer, 256 * VIDEO_VERTICAL_PIXELS * 2);
GX_SetDisplayTransfer(NULL,
GSPGPU_FlushDataCache(outputBuffer, 256 * VIDEO_VERTICAL_PIXELS * 2);
GX_DisplayTransfer(
outputBuffer, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS),
tex->data, GX_BUFFER_DIM(256, 256),
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) |
@ -324,9 +325,9 @@ static void _drawScreenshot(struct GBAGUIRunner* runner, const uint32_t* pixels,
memset(&newPixels[y * 256 + VIDEO_HORIZONTAL_PIXELS], 0, (256 - VIDEO_HORIZONTAL_PIXELS) * sizeof(u32));
}
GSPGPU_FlushDataCache(NULL, (void*)newPixels, 256 * VIDEO_VERTICAL_PIXELS * sizeof(u32));
GX_SetDisplayTransfer(NULL,
(void*)newPixels, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS),
GSPGPU_FlushDataCache(newPixels, 256 * VIDEO_VERTICAL_PIXELS * sizeof(u32));
GX_DisplayTransfer(
(u32*) newPixels, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS),
tex->data, GX_BUFFER_DIM(256, 256),
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) |
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) |
@ -433,8 +434,8 @@ static void _postAudioBuffer(struct GBAAVStream* stream, struct GBAAudio* audio)
#elif RESAMPLE_LIBRARY == RESAMPLE_NN
GBAAudioCopy(audio, &audioLeft[audioPos], &audioRight[audioPos], AUDIO_SAMPLES);
#endif
GSPGPU_FlushDataCache(0, (void*) &audioLeft[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
GSPGPU_FlushDataCache(0, (void*) &audioRight[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
GSPGPU_FlushDataCache(&audioLeft[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
GSPGPU_FlushDataCache(&audioRight[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
audioPos = (audioPos + AUDIO_SAMPLES) % AUDIO_SAMPLE_BUFFER;
if (audioPos == AUDIO_SAMPLES * 3) {
u8 playing = 0;
@ -448,9 +449,6 @@ static void _postAudioBuffer(struct GBAAVStream* stream, struct GBAAudio* audio)
}
int main() {
ptmInit();
hasSound = !csndInit();
rotation.d.sample = _sampleRotation;
rotation.d.readTiltX = _readTiltX;
rotation.d.readTiltY = _readTiltY;
@ -464,6 +462,9 @@ int main() {
return 1;
}
ptmuInit();
hasSound = !csndInit();
if (hasSound) {
audioLeft = linearMemAlign(AUDIO_SAMPLE_BUFFER * sizeof(int16_t), 0x80);
audioRight = linearMemAlign(AUDIO_SAMPLE_BUFFER * sizeof(int16_t), 0x80);
@ -472,6 +473,7 @@ int main() {
gfxInit(GSP_BGR8_OES, GSP_BGR8_OES, false);
if (ctrInitGpu() < 0) {
gbaOutputTexture.data = 0;
goto cleanup;
}
@ -483,18 +485,22 @@ int main() {
gbaOutputTexture.data = vramAlloc(256 * 256 * 2);
void* outputTextureEnd = (u8*)gbaOutputTexture.data + 256 * 256 * 2;
if (!gbaOutputTexture.data) {
goto cleanup;
}
// Zero texture data to make sure no garbage around the border interferes with filtering
GX_SetMemoryFill(NULL,
GX_MemoryFill(
gbaOutputTexture.data, 0x0000, outputTextureEnd, GX_FILL_16BIT_DEPTH | GX_FILL_TRIGGER,
NULL, 0, NULL, 0);
gspWaitForPSC0();
sdmcArchive = (FS_archive) {
ARCH_SDMC,
(FS_path) { PATH_EMPTY, 1, (const u8*)"" },
0, 0
sdmcArchive = (FS_Archive) {
ARCHIVE_SDMC,
(FS_Path) { PATH_EMPTY, 1, "" },
0
};
FSUSER_OpenArchive(0, &sdmcArchive);
FSUSER_OpenArchive(&sdmcArchive);
struct GUIFont* font = GUIFontCreate();
@ -549,10 +555,14 @@ int main() {
GBAGUIDeinit(&runner);
cleanup:
linearFree(renderer.outputBuffer);
if (renderer.outputBuffer) {
linearFree(renderer.outputBuffer);
}
ctrDeinitGpu();
vramFree(gbaOutputTexture.data);
if (gbaOutputTexture.data) {
ctrDeinitGpu();
vramFree(gbaOutputTexture.data);
}
gfxExit();
@ -560,7 +570,8 @@ cleanup:
linearFree(audioLeft);
linearFree(audioRight);
}
csndExit();
ptmExit();
ptmuExit();
return 0;
}

View File

@ -11,6 +11,11 @@
#include <3ds.h>
#include <malloc.h>
#ifdef _3DS
// ctrulib already has a type called Thread
#define Thread CustomThread
#endif
#define THREAD_ENTRY void
typedef ThreadFunc ThreadEntry;