mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-11-09 05:02:33 -05:00
Compare commits
No commits in common. "3b7dafb117b635dd17fca1c009e551f814e93df8" and "511a38763becac579af719c997162a6e171e455e" have entirely different histories.
3b7dafb117
...
511a38763b
76 changed files with 23 additions and 7549 deletions
|
|
@ -305,6 +305,4 @@ keymaps/keymap-echor1.c
|
|||
keymaps/keymap-surfansf28.c
|
||||
#elif CONFIG_KEYPAD == RG_NANO_PAD
|
||||
keymaps/keymap-rgnano.c
|
||||
#elif CONFIG_KEYPAD == CTRU_PAD
|
||||
keymaps/keymap-ctru.c
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,302 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id: keymap-ctru.c 28704 2025-07-09 11:28:53Z gama $
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio Garrido
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/* Button Code Definitions for Civic Type R (ctru) target */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "action.h"
|
||||
#include "button.h"
|
||||
#include "settings.h"
|
||||
|
||||
/*
|
||||
* The format of the list is as follows
|
||||
* { Action Code, Button code, Prereq button code }
|
||||
* if there's no need to check the previous button's value, use BUTTON_NONE
|
||||
* Insert LAST_ITEM_IN_LIST at the end of each mapping
|
||||
*/
|
||||
|
||||
static const struct button_mapping button_context_standard[] = {
|
||||
{ ACTION_STD_PREV, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
|
||||
{ ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
{ ACTION_STD_CANCEL, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_STD_CANCEL, BUTTON_BACK|BUTTON_REL, BUTTON_BACK },
|
||||
|
||||
{ ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
|
||||
{ ACTION_STD_OK, BUTTON_RIGHT, BUTTON_NONE },
|
||||
|
||||
{ ACTION_STD_QUICKSCREEN, BUTTON_MENU|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
|
||||
{ ACTION_STD_MENU, BUTTON_MENU|BUTTON_REL, BUTTON_MENU },
|
||||
{ ACTION_STD_CONTEXT, BUTTON_MENU|BUTTON_REL, BUTTON_NONE },
|
||||
|
||||
{ ACTION_STD_KEYLOCK, BUTTON_USER|BUTTON_POWER, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST
|
||||
}; /* button_context_standard */
|
||||
|
||||
|
||||
static const struct button_mapping button_context_mainmenu[] = {
|
||||
{ ACTION_TREE_WPS, BUTTON_MENU|BUTTON_REL, BUTTON_MENU },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_TREE),
|
||||
}; /* button_context_mainmenu */
|
||||
|
||||
static const struct button_mapping button_context_wps[] = {
|
||||
{ ACTION_WPS_PLAY, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
|
||||
{ ACTION_WPS_STOP, BUTTON_POWER|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
{ ACTION_WPS_BROWSE, BUTTON_BACK|BUTTON_REL, BUTTON_BACK },
|
||||
{ ACTION_WPS_MENU, BUTTON_MENU|BUTTON_REL, BUTTON_MENU },
|
||||
|
||||
|
||||
{ ACTION_WPS_HOTKEY, BUTTON_USER|BUTTON_REL, BUTTON_USER },
|
||||
{ ACTION_WPS_PITCHSCREEN, BUTTON_USER|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
{ ACTION_STD_KEYLOCK, BUTTON_USER|BUTTON_POWER, BUTTON_NONE },
|
||||
|
||||
|
||||
{ ACTION_WPS_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
|
||||
{ ACTION_WPS_QUICKSCREEN, BUTTON_MENU|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
{ ACTION_WPS_SKIPPREV, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT },
|
||||
{ ACTION_WPS_SEEKBACK, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_WPS_STOPSEEK, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT|BUTTON_REPEAT },
|
||||
|
||||
{ ACTION_WPS_SKIPNEXT, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT },
|
||||
{ ACTION_WPS_SEEKFWD, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_WPS_STOPSEEK, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT|BUTTON_REPEAT },
|
||||
|
||||
{ ACTION_WPS_VOLUP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_WPS_VOLUP, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_WPS_VOLDOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_WPS_VOLDOWN, BUTTON_DOWN, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST
|
||||
}; /* button_context_wps */
|
||||
|
||||
static const struct button_mapping button_context_list[] = {
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_list */
|
||||
|
||||
static const struct button_mapping button_context_tree[] = {
|
||||
{ ACTION_TREE_WPS, BUTTON_USER|BUTTON_REPEAT, BUTTON_USER },
|
||||
{ ACTION_TREE_STOP, BUTTON_POWER|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_TREE_HOTKEY, BUTTON_USER|BUTTON_REL, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST)
|
||||
}; /* button_context_tree */
|
||||
|
||||
static const struct button_mapping button_context_settings[] = {
|
||||
{ ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_DECREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_STD_PREV, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_TREE)
|
||||
}; /* button_context_settings */
|
||||
|
||||
static const struct button_mapping button_context_settings_right_is_inc[] = {
|
||||
{ ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_settings_right_is_inc */
|
||||
|
||||
static const struct button_mapping button_context_yesno[] = {
|
||||
{ ACTION_YESNO_ACCEPT, BUTTON_SELECT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_settings_yesno */
|
||||
|
||||
static const struct button_mapping button_context_colorchooser[] = { //check
|
||||
{ ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
|
||||
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM|CONTEXT_SETTINGS),
|
||||
}; /* button_context_colorchooser */
|
||||
|
||||
static const struct button_mapping button_context_eq[] = {
|
||||
{ ACTION_STD_CANCEL, BUTTON_MENU|BUTTON_REL, BUTTON_MENU },
|
||||
{ ACTION_SETTINGS_INC, BUTTON_RIGHT , BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_DEC, BUTTON_LEFT , BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT , BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM|CONTEXT_SETTINGS),
|
||||
}; /* button_context_eq */
|
||||
|
||||
/** Bookmark Screen **/
|
||||
static const struct button_mapping button_context_bmark[] = {
|
||||
{ ACTION_BMS_DELETE, BUTTON_POWER, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST),
|
||||
}; /* button_context_bmark */
|
||||
|
||||
static const struct button_mapping button_context_time[] = {
|
||||
{ ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_DECREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_STD_PREV, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
|
||||
}; /* button_context_time */
|
||||
|
||||
static const struct button_mapping button_context_quickscreen[] = {
|
||||
{ ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_QS_DOWN, BUTTON_DOWN, BUTTON_NONE },
|
||||
{ ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_QS_LEFT, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_QS_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_STD_CANCEL, BUTTON_MENU, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_quickscreen */
|
||||
|
||||
static const struct button_mapping button_context_pitchscreen[] = {
|
||||
{ ACTION_PS_INC_SMALL, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_PS_INC_BIG, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_DEC_SMALL, BUTTON_DOWN, BUTTON_NONE },
|
||||
{ ACTION_PS_DEC_BIG, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_NUDGE_LEFT, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_TOGGLE_MODE, BUTTON_USER, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_MENU|BUTTON_REL, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_BACK|BUTTON_REL, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
||||
static const struct button_mapping button_context_keyboard[] = {
|
||||
{ ACTION_KBD_UP, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_KBD_UP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_KBD_DOWN, BUTTON_DOWN, BUTTON_NONE },
|
||||
{ ACTION_KBD_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_KBD_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_KBD_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
{ ACTION_KBD_SELECT, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_KBD_ABORT, BUTTON_BACK|BUTTON_REL, BUTTON_BACK },
|
||||
{ ACTION_KBD_DONE, BUTTON_MENU|BUTTON_REL, BUTTON_MENU },
|
||||
{ ACTION_KBD_BACKSPACE, BUTTON_USER, BUTTON_NONE },
|
||||
{ ACTION_KBD_PAGE_FLIP, BUTTON_POWER, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_keyboard */
|
||||
|
||||
static const struct button_mapping button_context_radio[] = {
|
||||
{ ACTION_FM_MENU, BUTTON_SELECT | BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_FM_PRESET, BUTTON_MENU | BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_FM_STOP, BUTTON_POWER | BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_FM_MODE, BUTTON_MENU | BUTTON_REL, BUTTON_MENU },
|
||||
{ ACTION_FM_EXIT, BUTTON_BACK | BUTTON_REL, BUTTON_BACK },
|
||||
{ ACTION_FM_PLAY, BUTTON_SELECT | BUTTON_REL, BUTTON_SELECT },
|
||||
{ ACTION_FM_NEXT_PRESET, BUTTON_USER | BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_FM_PREV_PRESET, BUTTON_USER | BUTTON_LEFT, BUTTON_NONE },
|
||||
|
||||
/* Volume */
|
||||
{ ACTION_SETTINGS_INC, BUTTON_UP | BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_INCREPEAT, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_DEC, BUTTON_DOWN | BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_SETTINGS_DECREPEAT, BUTTON_DOWN, BUTTON_NONE },
|
||||
/* Tuning */
|
||||
{ ACTION_STD_PREV, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_STD_PREVREPEAT, BUTTON_LEFT | BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_STD_NEXTREPEAT, BUTTON_RIGHT | BUTTON_REPEAT, BUTTON_NONE },
|
||||
}; /* button_context_radio */
|
||||
|
||||
const struct button_mapping* target_get_context_mapping(int context)
|
||||
{
|
||||
switch (context & ~CONTEXT_LOCKED)
|
||||
{
|
||||
case CONTEXT_STD:
|
||||
return button_context_standard;
|
||||
case CONTEXT_WPS:
|
||||
return button_context_wps;
|
||||
|
||||
case CONTEXT_LIST:
|
||||
return button_context_list;
|
||||
case CONTEXT_MAINMENU:
|
||||
return button_context_mainmenu;
|
||||
|
||||
case CONTEXT_TREE:
|
||||
return button_context_tree;
|
||||
|
||||
case CONTEXT_SETTINGS:
|
||||
return button_context_settings;
|
||||
|
||||
case CONTEXT_CUSTOM|CONTEXT_SETTINGS:
|
||||
case CONTEXT_SETTINGS_RECTRIGGER:
|
||||
return button_context_settings_right_is_inc;
|
||||
|
||||
case CONTEXT_SETTINGS_COLOURCHOOSER:
|
||||
return button_context_colorchooser;
|
||||
case CONTEXT_SETTINGS_EQ:
|
||||
return button_context_eq;
|
||||
|
||||
case CONTEXT_SETTINGS_TIME:
|
||||
return button_context_time;
|
||||
|
||||
case CONTEXT_YESNOSCREEN:
|
||||
return button_context_yesno;
|
||||
case CONTEXT_FM:
|
||||
return button_context_radio;
|
||||
case CONTEXT_BOOKMARKSCREEN:
|
||||
return button_context_bmark;
|
||||
case CONTEXT_QUICKSCREEN:
|
||||
return button_context_quickscreen;
|
||||
case CONTEXT_PITCHSCREEN:
|
||||
return button_context_pitchscreen;
|
||||
case CONTEXT_KEYBOARD:
|
||||
return button_context_keyboard;
|
||||
}
|
||||
return button_context_standard;
|
||||
}
|
||||
|
|
@ -565,11 +565,7 @@ void resume_directory(const char *dir)
|
|||
|
||||
/* Returns the current working directory and also writes cwd to buf if
|
||||
non-NULL. In case of error, returns NULL. */
|
||||
#ifdef CTRU
|
||||
char *__wrap_getcwd(char *buf, getcwd_size_t size)
|
||||
#else
|
||||
char *getcwd(char *buf, getcwd_size_t size)
|
||||
#endif
|
||||
{
|
||||
if (!buf)
|
||||
return tc.currdir;
|
||||
|
|
|
|||
|
|
@ -128,12 +128,7 @@ void tree_unlock_cache(struct tree_context *t);
|
|||
#else
|
||||
#define getcwd_size_t size_t
|
||||
#endif
|
||||
#ifdef CTRU
|
||||
/* devkitarm already defines getcwd */
|
||||
char *__wrap_getcwd(char *buf, getcwd_size_t size);
|
||||
#else
|
||||
char *getcwd(char *buf, getcwd_size_t size);
|
||||
#endif
|
||||
void reload_directory(void);
|
||||
bool check_rockboxdir(void);
|
||||
struct tree_context* tree_get_context(void);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ target/hosted/rtc.c
|
|||
|
||||
#if (CONFIG_PLATFORM & PLATFORM_ANDROID) == 0 && \
|
||||
!defined(DX50) && !defined(DX90) && \
|
||||
(defined(DEBUG) || defined(SIMULATOR) || defined(CTRU)) /* sim should define DEBUG instead */
|
||||
(defined(DEBUG) || defined(SIMULATOR)) /* sim should define DEBUG instead */
|
||||
target/hosted/debug-hosted.c
|
||||
#endif
|
||||
|
||||
|
|
@ -94,14 +94,12 @@ target/hosted/sdl/app/button-application.c
|
|||
#ifdef WIN32
|
||||
target/hosted/filesystem-win32.c
|
||||
#else /* !WIN32 */
|
||||
#ifndef CTRU
|
||||
target/hosted/filesystem-unix.c
|
||||
#endif /* CTRU */
|
||||
#endif /* WIN32 */
|
||||
#endif /* APPLICATION */
|
||||
#endif /* HAVE_SDL */
|
||||
|
||||
#if defined(APPLICATION) && !defined(CTRU)
|
||||
#ifdef APPLICATION
|
||||
target/hosted/filesystem-app.c
|
||||
#endif /* APPLICATION */
|
||||
|
||||
|
|
@ -583,8 +581,6 @@ target/hosted/maemo/pcm-gstreamer.c
|
|||
target/hosted/sdl/pcm-sdl.c
|
||||
#endif /* (CONFIG_PLATFORM & PLATFORM_MAEMO) */
|
||||
|
||||
#elif defined(CTRU)
|
||||
drivers/audio/ctru.c
|
||||
#endif
|
||||
#endif /* (CONFIG_PLATFORM & PLATFORM_NATIVE) */
|
||||
|
||||
|
|
@ -2075,28 +2071,6 @@ target/hosted/ibasso/dx90/button-dx90.c
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if (CONFIG_PLATFORM & PLATFORM_CTRU)
|
||||
asm/arm/lcd-as-memframe.S
|
||||
target/hosted/ctru/backlight-ctru.c
|
||||
target/hosted/ctru/button-ctru.c
|
||||
target/hosted/ctru/kernel-ctru.c
|
||||
target/hosted/ctru/thread-ctru.c
|
||||
target/hosted/ctru/lcd-bitmap.c
|
||||
target/hosted/ctru/luminance-ctru.c
|
||||
target/hosted/ctru/system-ctru.c
|
||||
target/hosted/ctru/filesystem-ctru.c
|
||||
target/hosted/ctru/lc-ctru.c
|
||||
target/hosted/ctru/lc-program-resolver.c
|
||||
target/hosted/ctru/powermgmt-ctru.c
|
||||
target/hosted/ctru/timer-ctru.c
|
||||
target/hosted/ctru/pcm-ctru.c
|
||||
target/hosted/ctru/lib/sys_file.c
|
||||
target/hosted/ctru/lib/sys_dir.c
|
||||
target/hosted/ctru/lib/sys_thread.c
|
||||
target/hosted/ctru/lib/sys_timer.c
|
||||
target/hosted/ctru/lib/bfile/bfile.c
|
||||
#endif
|
||||
|
||||
#else /* defined(SIMULATOR) */
|
||||
|
||||
#ifdef WIN32
|
||||
|
|
@ -2145,9 +2119,9 @@ kernel/queue.c
|
|||
#ifdef HAVE_SEMAPHORE_OBJECTS
|
||||
kernel/semaphore.c
|
||||
#endif
|
||||
#ifdef HAVE_SDL_THREADS
|
||||
#if defined(HAVE_SDL_THREADS)
|
||||
target/hosted/sdl/thread-sdl.c
|
||||
#elif !defined(CTRU)
|
||||
#else
|
||||
kernel/thread.c
|
||||
#endif
|
||||
kernel/thread-common.c
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright © 2010 Thomas Martitz
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include "config.h"
|
||||
#include "sound.h"
|
||||
#include "pcm_sampr.h"
|
||||
#ifdef HAVE_SW_VOLUME_CONTROL
|
||||
#include "pcm_sw_volume.h"
|
||||
#include "fixedpoint.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Audio Hardware api. Make some of them do nothing as we cannot properly
|
||||
* simulate with SDL. if we used DSP we would run code that doesn't actually
|
||||
* run on the target
|
||||
**/
|
||||
|
||||
void audiohw_set_volume(int vol_l, int vol_r)
|
||||
{
|
||||
(void)vol_l; (void)vol_r;
|
||||
}
|
||||
|
||||
#if defined(AUDIOHW_HAVE_BALANCE)
|
||||
void audiohw_set_balance(int value) { (void)value; }
|
||||
#endif
|
||||
#ifndef HAVE_SW_TONE_CONTROLS
|
||||
#if defined(AUDIOHW_HAVE_BASS)
|
||||
void audiohw_set_bass(int value) { (void)value; }
|
||||
#endif
|
||||
#if defined(AUDIOHW_HAVE_TREBLE)
|
||||
void audiohw_set_treble(int value) { (void)value; }
|
||||
#endif
|
||||
#endif /* HAVE_SW_TONE_CONTROLS */
|
||||
#if defined(AUDIOHW_HAVE_BASS_CUTOFF)
|
||||
void audiohw_set_bass_cutoff(int value) { (void)value; }
|
||||
#endif
|
||||
#if defined(AUDIOHW_HAVE_TREBLE_CUTOFF)
|
||||
void audiohw_set_treble_cutoff(int value){ (void)value; }
|
||||
#endif
|
||||
/* EQ-based tone controls */
|
||||
#if defined(AUDIOHW_HAVE_EQ)
|
||||
void audiohw_set_eq_band_gain(unsigned int band, int value)
|
||||
{ (void)band; (void)value; }
|
||||
#endif
|
||||
#if defined(AUDIOHW_HAVE_EQ_FREQUENCY)
|
||||
void audiohw_set_eq_band_frequency(unsigned int band, int value)
|
||||
{ (void)band; (void)value; }
|
||||
#endif
|
||||
#if defined(AUDIOHW_HAVE_EQ_WIDTH)
|
||||
void audiohw_set_eq_band_width(unsigned int band, int value)
|
||||
{ (void)band; (void)value; }
|
||||
#endif
|
||||
#if defined(AUDIOHW_HAVE_DEPTH_3D)
|
||||
void audiohw_set_depth_3d(int value)
|
||||
{ (void)value; }
|
||||
#endif
|
||||
#if defined(AUDIOHW_HAVE_LINEOUT)
|
||||
void audiohw_set_lineout_volume(int vol_l, int vol_r)
|
||||
{ (void)vol_l; (void)vol_r; }
|
||||
#endif
|
||||
#if defined(AUDIOHW_HAVE_FILTER_ROLL_OFF)
|
||||
void audiohw_set_filter_roll_off(int value)
|
||||
{ (void)value; }
|
||||
#endif
|
||||
#if defined(AUDIOHW_HAVE_POWER_MODE)
|
||||
void audiohw_set_power_mode(int value)
|
||||
{ (void)value; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SAMPR_TYPES
|
||||
unsigned int pcm_sampr_to_hw_sampr(unsigned int samplerate,
|
||||
unsigned int type)
|
||||
{ return samplerate; (void)type; }
|
||||
#endif /* CONFIG_SAMPR_TYPES */
|
||||
|
|
@ -224,7 +224,7 @@ struct sound_settings_info
|
|||
#elif defined(HAVE_ES9218)
|
||||
#include "es9218.h"
|
||||
#elif ((CONFIG_PLATFORM & (PLATFORM_ANDROID | PLATFORM_MAEMO \
|
||||
| PLATFORM_PANDORA | PLATFORM_SDL | PLATFORM_CTRU)) | defined(RG_NANO))
|
||||
| PLATFORM_PANDORA | PLATFORM_SDL )) | defined(RG_NANO))
|
||||
#include "hosted_codec.h"
|
||||
#elif defined(DX50)
|
||||
#include "codec-dx50.h"
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@
|
|||
#define RK27XX 2700
|
||||
#define X1000 1000
|
||||
#define STM32H743 32743
|
||||
#define N10480H 10480
|
||||
|
||||
/* platforms
|
||||
* bit fields to allow PLATFORM_HOSTED to be OR'ed e.g. with a
|
||||
|
|
@ -99,7 +98,6 @@
|
|||
#define PLATFORM_MAEMO5 (1<<5)
|
||||
#define PLATFORM_MAEMO (PLATFORM_MAEMO4|PLATFORM_MAEMO5)
|
||||
#define PLATFORM_PANDORA (1<<6)
|
||||
#define PLATFORM_CTRU (1<<7)
|
||||
|
||||
/* CONFIG_KEYPAD */
|
||||
#define IRIVER_H100_PAD 4
|
||||
|
|
@ -169,7 +167,6 @@
|
|||
#define ECHO_R1_PAD 75
|
||||
#define SURFANS_F28_PAD 76
|
||||
#define RG_NANO_PAD 77
|
||||
#define CTRU_PAD 78
|
||||
|
||||
/* CONFIG_REMOTE_KEYPAD */
|
||||
#define H100_REMOTE 1
|
||||
|
|
@ -626,8 +623,6 @@ Lyre prototype 1 */
|
|||
#include "config/surfansf28.h"
|
||||
#elif defined(RG_NANO)
|
||||
#include "config/rgnano.h"
|
||||
#elif defined(CTRU)
|
||||
#include "config/ctru.h"
|
||||
#else
|
||||
#error "unknown hardware platform!"
|
||||
#endif
|
||||
|
|
@ -655,7 +650,7 @@ Lyre prototype 1 */
|
|||
# define CONFIG_BUFLIB_BACKEND BUFLIB_BACKEND_MEMPOOL
|
||||
#endif
|
||||
|
||||
#if defined(APPLICATION)
|
||||
#ifdef APPLICATION
|
||||
#ifndef CONFIG_CPU
|
||||
#define CONFIG_CPU 0
|
||||
#endif
|
||||
|
|
@ -1038,8 +1033,7 @@ Lyre prototype 1 */
|
|||
|
||||
#if defined(ASSEMBLER_THREADS) \
|
||||
|| defined(HAVE_WIN32_FIBER_THREADS) \
|
||||
|| defined(HAVE_SIGALTSTACK_THREADS) \
|
||||
|| defined(CTRU)
|
||||
|| defined(HAVE_SIGALTSTACK_THREADS)
|
||||
#define HAVE_PRIORITY_SCHEDULING
|
||||
#endif
|
||||
|
||||
|
|
@ -1110,7 +1104,7 @@ Lyre prototype 1 */
|
|||
* Older versions of GCC emit assembly in divided syntax with no option
|
||||
* to enable unified syntax.
|
||||
*/
|
||||
#if (__GNUC__ < 8) && defined(CPU_ARM_CLASSIC) || defined(CTRU)
|
||||
#if (__GNUC__ < 8) && defined(CPU_ARM_CLASSIC)
|
||||
#define BEGIN_ARM_ASM_SYNTAX_UNIFIED ".syntax unified\n"
|
||||
#define END_ARM_ASM_SYNTAX_UNIFIED ".syntax divided\n"
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* This config file is for the N3DS hosted application
|
||||
*/
|
||||
|
||||
/* We don't run on hardware directly */
|
||||
#define CONFIG_PLATFORM (PLATFORM_HOSTED|PLATFORM_CTRU)
|
||||
#define HAVE_FPU
|
||||
|
||||
/* For Rolo and boot loader */
|
||||
#define MODEL_NUMBER 100s
|
||||
#define MODEL_NAME "CTRU"
|
||||
|
||||
#define USB_NONE
|
||||
|
||||
#define CONFIG_CPU N10480H
|
||||
|
||||
#define CPU_FREQ 268000000
|
||||
|
||||
/* Define this if you have adjustable CPU frequency */
|
||||
/* #define HAVE_ADJUSTABLE_CPU_FREQ */
|
||||
|
||||
/* define this if you have a colour LCD */
|
||||
#define HAVE_LCD_COLOR
|
||||
|
||||
/* define this if you want album art for this target */
|
||||
#define HAVE_ALBUMART
|
||||
|
||||
/* define this to enable bitmap scaling */
|
||||
#define HAVE_BMP_SCALING
|
||||
|
||||
/* define this to enable JPEG decoding */
|
||||
#define HAVE_JPEG
|
||||
|
||||
/* define this if you have access to the quickscreen */
|
||||
#define HAVE_QUICKSCREEN
|
||||
|
||||
/* define this if you would like tagcache to build on this target */
|
||||
#define HAVE_TAGCACHE
|
||||
|
||||
/* LCD dimensions */
|
||||
#define LCD_WIDTH 320
|
||||
#define LCD_HEIGHT 240
|
||||
|
||||
#define LCD_DEPTH 16
|
||||
#define LCD_PIXELFORMAT RGB565
|
||||
|
||||
#define LCD_OPTIMIZED_UPDATE
|
||||
#define LCD_OPTIMIZED_UPDATE_RECT
|
||||
#define LCD_OPTIMIZED_BLIT_YUV
|
||||
|
||||
/* define this to indicate your device's keypad */
|
||||
#define HAVE_TOUCHSCREEN
|
||||
#define HAVE_BUTTON_DATA
|
||||
|
||||
/* define this if you have a real-time clock */
|
||||
#define CONFIG_RTC APPLICATION
|
||||
|
||||
/* Power management */
|
||||
#define CONFIG_BATTERY_MEASURE PERCENTAGE_MEASURE
|
||||
#define CONFIG_CHARGING CHARGING_MONITOR
|
||||
#define HAVE_SW_POWEROFF
|
||||
|
||||
/* The number of bytes reserved for loadable codecs */
|
||||
#define CODEC_SIZE 0x100000
|
||||
|
||||
/* The number of bytes reserved for loadable plugins */
|
||||
#define PLUGIN_BUFFER_SIZE 0x80000
|
||||
|
||||
#define AB_REPEAT_ENABLE
|
||||
|
||||
|
||||
/* #define HAVE_SCROLLWHEEL */
|
||||
#define CONFIG_KEYPAD CTRU_PAD
|
||||
|
||||
#define HAVE_CTRU_AUDIO
|
||||
#define HAVE_HEADPHONE_DETECTION
|
||||
/* #define HAVE_SW_VOLUME_CONTROL */
|
||||
/* #define PCM_SW_VOLUME_UNBUFFERED */
|
||||
/* #define PCM_SW_VOLUME_FRACBITS (16) */
|
||||
|
||||
/* Define this for LCD backlight available */
|
||||
#define HAVE_BACKLIGHT
|
||||
#define HAVE_BACKLIGHT_BRIGHTNESS
|
||||
|
||||
/* Main LCD backlight brightness range and defaults */
|
||||
#define MIN_BRIGHTNESS_SETTING 16
|
||||
#define MAX_BRIGHTNESS_SETTING 142
|
||||
#define BRIGHTNESS_STEP 5
|
||||
#define DEFAULT_BRIGHTNESS_SETTING 28
|
||||
#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING
|
||||
|
||||
#define CONFIG_LCD LCD_COWOND2
|
||||
|
||||
/* Define this if a programmable hotkey is mapped */
|
||||
#define HAVE_HOTKEY
|
||||
|
||||
#define BOOTDIR "/"
|
||||
|
||||
/* No special storage */
|
||||
#define CONFIG_STORAGE STORAGE_HOSTFS
|
||||
#define HAVE_STORAGE_FLUSH
|
||||
|
||||
|
|
@ -35,10 +35,6 @@ extern void ldebugf(const char* file, int line, const char *fmt, ...)
|
|||
|| (defined(APPLICATION) && defined(DEBUG))
|
||||
#define DEBUGF debugf
|
||||
#define LDEBUGF(...) ldebugf(__FILE__, __LINE__, __VA_ARGS__)
|
||||
#elif (CONFIG_PLATFORM & PLATFORM_CTRU)
|
||||
/* let's use second display for debug output */
|
||||
#define DEBUGF debugf
|
||||
#define LDEBUGF(...) ldebugf(__FILE__, __LINE__, __VA_ARGS__)
|
||||
#elif defined(DEBUG) /* DEBUG on native targets */
|
||||
|
||||
#ifdef HAVE_GDB_API
|
||||
|
|
|
|||
|
|
@ -21,9 +21,8 @@
|
|||
#ifndef HOSTED_CODEC_H
|
||||
#define HOSTED_CODEC_H
|
||||
|
||||
#if (defined(HAVE_SDL_AUDIO) \
|
||||
&& !(CONFIG_PLATFORM & PLATFORM_MAEMO5)) \
|
||||
|| (CONFIG_PLATFORM & PLATFORM_CTRU)
|
||||
#if defined(HAVE_SDL_AUDIO) \
|
||||
&& !(CONFIG_PLATFORM & PLATFORM_MAEMO5)
|
||||
AUDIOHW_SETTING(VOLUME, "dB", 0, 1, -80, 0, 0)
|
||||
#else
|
||||
#define AUDIOHW_CAPS (MONO_VOL_CAP)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
/* NOTE: target-specific hosted HOME_DIR resides in filesystem-app.c */
|
||||
#if !defined(APPLICATION) || defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1) || \
|
||||
defined(DX50) || defined(DX90) || defined(SONY_NWZ_LINUX) || \
|
||||
defined(HIBY_LINUX) || defined(FIIO_M3K_LINUX) || defined(CTRU)
|
||||
defined(HIBY_LINUX) || defined(FIIO_M3K_LINUX)
|
||||
|
||||
#define HOME_DIR "/"
|
||||
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
|
||||
#if defined(APPLICATION) && \
|
||||
!(defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1) || \
|
||||
defined(DX50) || defined(DX90) || defined(SONY_NWZ_LINUX) || defined(HIBY_LINUX) || defined(FIIO_M3K_LINUX) || defined(CTRU))
|
||||
defined(DX50) || defined(DX90) || defined(SONY_NWZ_LINUX) || defined(HIBY_LINUX) || defined(FIIO_M3K_LINUX))
|
||||
|
||||
#define PLUGIN_DATA_DIR ROCKBOX_DIR "/rocks.data"
|
||||
#define PLUGIN_GAMES_DATA_DIR PLUGIN_DATA_DIR
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
"comment out" the non-ANSI parts of the ANSI header files (non-ANSI header
|
||||
files aren't affected). */
|
||||
|
||||
#ifndef __ANSIDECL_H__
|
||||
#define __ANSIDECL_H__
|
||||
#ifndef _ANSIDECL_H_
|
||||
#define _ANSIDECL_H_
|
||||
|
||||
/* First try to figure out whether we really are in an ANSI C environment. */
|
||||
/* FIXME: This probably needs some work. Perhaps sys/config.h can be
|
||||
|
|
@ -64,4 +64,4 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* __ANSIDECL_H__ */
|
||||
#endif /* _ANSIDECL_H_ */
|
||||
|
|
|
|||
|
|
@ -28,9 +28,7 @@
|
|||
#include "fs_attr.h"
|
||||
#include "fs_defines.h"
|
||||
|
||||
#if defined(CTRU) && !defined(SIMULATOR)
|
||||
#include "filesystem-ctru.h"
|
||||
#elif defined (APPLICATION) || defined(CHECKWPS)
|
||||
#if defined (APPLICATION) || defined(CHECKWPS)
|
||||
#include "filesystem-app.h"
|
||||
#elif defined(SIMULATOR) || defined(DBTOOL)
|
||||
#include "../../uisimulator/common/filesystem-sim.h"
|
||||
|
|
|
|||
|
|
@ -40,9 +40,7 @@ enum relate_result
|
|||
RELATE_PREFIX, /* the path2 contains path1 as a prefix */
|
||||
};
|
||||
|
||||
#if defined(CTRU) && !defined(SIMULATOR)
|
||||
#include "filesystem-ctru.h"
|
||||
#elif defined(APPLICATION) || defined(CHECKWPS)
|
||||
#if defined(APPLICATION) || defined(CHECKWPS)
|
||||
#include "filesystem-app.h"
|
||||
#elif defined(SIMULATOR) || defined(DBTOOL)
|
||||
#include "../../uisimulator/common/filesystem-sim.h"
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
#endif
|
||||
|
||||
#ifndef __MINGW32__
|
||||
#if defined(__APPLE__) || defined(CTRU)
|
||||
#ifdef __APPLE__
|
||||
#include <sys/types.h>
|
||||
#else
|
||||
#include <endian.h>
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ struct thread_entry;
|
|||
*
|
||||
* simulator (possibly) doesn't simulate stack usage anyway but well ... */
|
||||
|
||||
#if defined(HAVE_SDL_THREADS) || defined(__PCTOOL__) || defined(CTRU)
|
||||
#if defined(HAVE_SDL_THREADS) || defined(__PCTOOL__)
|
||||
#define DEFAULT_STACK_SIZE 0x100 /* tiny, ignored anyway */
|
||||
#else
|
||||
#include "asm/thread.h"
|
||||
|
|
|
|||
|
|
@ -75,15 +75,11 @@ void mutex_lock(struct mutex *m)
|
|||
/* Release ownership of a mutex object - only owning thread must call this */
|
||||
void mutex_unlock(struct mutex *m)
|
||||
{
|
||||
#ifndef CTRU
|
||||
/* FIXME: synchronization primitives does not behave
|
||||
correctly between different cores */
|
||||
/* unlocker not being the owner is an unlocking violation */
|
||||
KERNEL_ASSERT(m->blocker.thread == __running_self_entry(),
|
||||
"mutex_unlock->wrong thread (%s != %s)\n",
|
||||
m->blocker.thread->name,
|
||||
__running_self_entry()->name);
|
||||
#endif
|
||||
|
||||
if(m->recursion > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ int thread_get_debug_info(unsigned int thread_id,
|
|||
#ifdef HAVE_SCHEDULER_BOOSTCTRL
|
||||
cpu_boost = thread->cpu_boost;
|
||||
#endif
|
||||
#if !defined(HAVE_SDL_THREADS) && !defined(CTRU)
|
||||
#ifndef HAVE_SDL_THREADS
|
||||
infop->stack_usage = stack_usage(thread->stack, thread->stack_size);
|
||||
|
||||
size_t stack_used_current =
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
*
|
||||
* simulator (possibly) doesn't simulate stack usage anyway but well ... */
|
||||
|
||||
#if defined(HAVE_SDL_THREADS) || defined(__PCTOOL__) || defined(CTRU)
|
||||
#if defined(HAVE_SDL_THREADS) || defined(__PCTOOL__)
|
||||
struct regs
|
||||
{
|
||||
void *t; /* OS thread */
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
*
|
||||
* Copyright (C) 2017 Marcin Bukat
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _BACKLIGHT_TARGET_H_
|
||||
#define _BACKLIGHT_TARGET_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* See backlight.c */
|
||||
bool backlight_hw_init(void);
|
||||
void backlight_hw_on(void);
|
||||
void backlight_hw_off(void);
|
||||
void backlight_hw_brightness(int brightness);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2007 by Rob Purchase
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _BUTTON_TARGET_H_
|
||||
#define _BUTTON_TARGET_H_
|
||||
|
||||
/* Logical buttons key codes */
|
||||
#define BUTTON_UP 0x00000001
|
||||
#define BUTTON_DOWN 0x00000002
|
||||
#define BUTTON_LEFT 0x00000004
|
||||
#define BUTTON_RIGHT 0x00000008
|
||||
#define BUTTON_USER 0x00000010
|
||||
#define BUTTON_MENU 0x00000020
|
||||
#define BUTTON_BACK 0x00000040
|
||||
#define BUTTON_POWER 0x00000080
|
||||
#define BUTTON_SELECT 0x00000100
|
||||
|
||||
/* Touch Screen Area Buttons */
|
||||
#define BUTTON_TOPLEFT 0x00001000
|
||||
#define BUTTON_TOPMIDDLE 0x00002000
|
||||
#define BUTTON_TOPRIGHT 0x00004000
|
||||
#define BUTTON_MIDLEFT 0x00008000
|
||||
#define BUTTON_CENTER 0x00010000
|
||||
#define BUTTON_MIDRIGHT 0x00020000
|
||||
#define BUTTON_BOTTOMLEFT 0x00040000
|
||||
#define BUTTON_BOTTOMMIDDLE 0x00080000
|
||||
#define BUTTON_BOTTOMRIGHT 0x00100000
|
||||
|
||||
#define BUTTON_MAIN 0x1FFF
|
||||
|
||||
/* Software power-off */
|
||||
#define POWEROFF_BUTTON BUTTON_POWER
|
||||
/* About 3 seconds */
|
||||
#define POWEROFF_COUNT 10
|
||||
|
||||
#endif /* _BUTTON_TARGET_H_ */
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "config.h"
|
||||
#include "backlight-target.h"
|
||||
#include "sysfs.h"
|
||||
#include "panic.h"
|
||||
#include "lcd.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <3ds/services/gsplcd.h>
|
||||
#include <3ds/result.h>
|
||||
#include "luminance-ctru.h"
|
||||
|
||||
/* TODO: To use calibrated values in rockbox,
|
||||
MIN_BRIGHTNESS_SETTING (etc) would need to be
|
||||
declared as constants in settings-list.c */
|
||||
|
||||
u32 ctru_min_lum = 16;
|
||||
u32 ctru_max_lum = 142;
|
||||
u32 ctru_luminance = 28;
|
||||
|
||||
/* FIXME: After calling gspLcdInit() the home menu will no longer be
|
||||
accesible, to fix this we have to call gspLcdInit/gspLcdExit as
|
||||
a mutex lock/unlock when using gsplcd.h functions. */
|
||||
void lcd_mutex_lock(void)
|
||||
{
|
||||
Result res = gspLcdInit();
|
||||
if (R_FAILED(res)) {
|
||||
DEBUGF("backlight_hw_init: gspLcdInit failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_mutex_unlock(void)
|
||||
{
|
||||
gspLcdExit();
|
||||
}
|
||||
|
||||
bool backlight_hw_init(void)
|
||||
{
|
||||
/* read calibrated values */
|
||||
ctru_luminance = getCurrentLuminance(false);
|
||||
ctru_min_lum = getMinLuminancePreset();
|
||||
ctru_max_lum = getMaxLuminancePreset();
|
||||
|
||||
backlight_hw_on();
|
||||
backlight_hw_brightness(DEFAULT_BRIGHTNESS_SETTING);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int last_bl = -1;
|
||||
|
||||
void backlight_hw_on(void)
|
||||
{
|
||||
lcd_mutex_lock();
|
||||
if (last_bl != 1) {
|
||||
#ifdef HAVE_LCD_ENABLE
|
||||
lcd_enable(true);
|
||||
#endif
|
||||
GSPLCD_PowerOnAllBacklights();
|
||||
last_bl = 1;
|
||||
}
|
||||
lcd_mutex_unlock();
|
||||
}
|
||||
|
||||
void backlight_hw_off(void)
|
||||
{
|
||||
lcd_mutex_lock();
|
||||
if (last_bl != 0) {
|
||||
/* only power off rockbox ui screen */
|
||||
GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_BOTTOM);
|
||||
#ifdef HAVE_LCD_ENABLE
|
||||
lcd_enable(false);
|
||||
#endif
|
||||
last_bl = 0;
|
||||
}
|
||||
lcd_mutex_unlock();
|
||||
}
|
||||
|
||||
void backlight_hw_brightness(int brightness)
|
||||
{
|
||||
/* cap range, just in case */
|
||||
if (brightness > MAX_BRIGHTNESS_SETTING)
|
||||
brightness = MAX_BRIGHTNESS_SETTING;
|
||||
if (brightness < MIN_BRIGHTNESS_SETTING)
|
||||
brightness = MIN_BRIGHTNESS_SETTING;
|
||||
|
||||
/* normalize level on both screens */
|
||||
lcd_mutex_lock();
|
||||
GSPLCD_SetBrightnessRaw(GSPLCD_SCREEN_TOP | GSPLCD_SCREEN_BOTTOM, (u32) brightness);
|
||||
lcd_mutex_unlock();
|
||||
}
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 by Felix Arends
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h> /* EXIT_SUCCESS */
|
||||
#include <stdio.h>
|
||||
#include "config.h"
|
||||
#include "button.h"
|
||||
#include "kernel.h"
|
||||
#include "backlight.h"
|
||||
#include "system.h"
|
||||
#include "button-ctru.h"
|
||||
#include "buttonmap.h"
|
||||
#include "debug.h"
|
||||
#include "powermgmt.h"
|
||||
#include "storage.h"
|
||||
#include "settings.h"
|
||||
#include "sound.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include "touchscreen.h"
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include <3ds/services/apt.h>
|
||||
#include <3ds/services/hid.h>
|
||||
#include <3ds/services/mcuhwc.h>
|
||||
#include <3ds/services/dsp.h>
|
||||
|
||||
static u8 old_slider_level = 0;
|
||||
static int last_y, last_x;
|
||||
|
||||
static enum {
|
||||
STATE_UNKNOWN = -1,
|
||||
STATE_UP = 0,
|
||||
STATE_DOWN = 1,
|
||||
} last_touch_state = STATE_UNKNOWN;
|
||||
|
||||
static double map_values(double n, double source_start, double source_end, double dest_start, double dest_end, int decimal_precision ) {
|
||||
double delta_start = source_end - source_start;
|
||||
double delta_end = dest_end - dest_start;
|
||||
if(delta_start == 0.0 || delta_end == 0.0) {
|
||||
return 1.0;
|
||||
}
|
||||
double scale = delta_end / delta_start;
|
||||
double neg_start = -1.0 * source_start;
|
||||
double offset = (neg_start * scale) + dest_start;
|
||||
double final_number = (n * scale) + offset;
|
||||
int calc_scale = (int) pow(10.0, decimal_precision);
|
||||
return (double) round(final_number * calc_scale) / calc_scale;
|
||||
}
|
||||
|
||||
void update_sound_slider_level(void)
|
||||
{
|
||||
/* update global volume based on sound slider level */
|
||||
u8 level;
|
||||
MCUHWC_GetSoundSliderLevel(&level);
|
||||
|
||||
if (level != old_slider_level) {
|
||||
int volume = (int) map_values((double) level,
|
||||
0.0, /* min slider voslume */
|
||||
(double) 0x3f, /* max slider value */
|
||||
(double) sound_min(SOUND_VOLUME),
|
||||
(double) sound_max(SOUND_VOLUME),
|
||||
0);
|
||||
global_status.volume = volume;
|
||||
setvol();
|
||||
old_slider_level = level;
|
||||
}
|
||||
}
|
||||
|
||||
int button_read_device(int* data)
|
||||
{
|
||||
int key = BUTTON_NONE;
|
||||
|
||||
/* TODO: implement Home Menu button support */
|
||||
/* if (!aptMainLoop()) {
|
||||
return true;
|
||||
} */
|
||||
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown();
|
||||
|
||||
if (kDown & KEY_SELECT) {
|
||||
touchscreen_set_mode(touchscreen_get_mode() == TOUCHSCREEN_POINT ? TOUCHSCREEN_BUTTON : TOUCHSCREEN_POINT);
|
||||
printf("Touchscreen mode: %s\n", touchscreen_get_mode() == TOUCHSCREEN_POINT ? "TOUCHSCREEN_POINT" : "TOUCHSCREEN_BUTTON");
|
||||
}
|
||||
|
||||
u32 kHeld = hidKeysHeld();
|
||||
|
||||
/* rockbox will handle button repeats */
|
||||
kDown |= kHeld;
|
||||
|
||||
/* Check for all the keys */
|
||||
if (kDown & KEY_A) {
|
||||
key |= BUTTON_SELECT;
|
||||
}
|
||||
if (kDown & KEY_B) {
|
||||
key |= BUTTON_BACK;
|
||||
}
|
||||
if (kDown & KEY_X) {
|
||||
key |= BUTTON_MENU;
|
||||
}
|
||||
if (kDown & KEY_Y) {
|
||||
key |= BUTTON_USER;
|
||||
}
|
||||
if (kDown & KEY_START) {
|
||||
key |= BUTTON_POWER;
|
||||
}
|
||||
if (kDown & KEY_DRIGHT) {
|
||||
key |= BUTTON_RIGHT;
|
||||
}
|
||||
if (kDown & KEY_DLEFT) {
|
||||
key |= BUTTON_LEFT;
|
||||
}
|
||||
if (kDown & KEY_DUP) {
|
||||
key |= BUTTON_UP;
|
||||
}
|
||||
if (kDown & KEY_DDOWN) {
|
||||
key |= BUTTON_DOWN;
|
||||
}
|
||||
if (kDown & KEY_START) {
|
||||
key |= BUTTON_POWER;
|
||||
}
|
||||
|
||||
touchPosition touch;
|
||||
hidTouchRead(&touch);
|
||||
|
||||
/* Generate UP and DOWN events */
|
||||
if (kDown & KEY_TOUCH) {
|
||||
last_touch_state = STATE_DOWN;
|
||||
}
|
||||
else {
|
||||
last_touch_state = STATE_UP;
|
||||
}
|
||||
|
||||
last_x = touch.px;
|
||||
last_y = touch.py;
|
||||
|
||||
int tkey = touchscreen_to_pixels(last_x, last_y, data);
|
||||
if (last_touch_state == STATE_DOWN) {
|
||||
key |= tkey;
|
||||
}
|
||||
|
||||
update_sound_slider_level();
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void button_init_device(void)
|
||||
{
|
||||
hidInit();
|
||||
}
|
||||
|
||||
#ifndef HAS_BUTTON_HOLD
|
||||
void touchscreen_enable_device(bool en)
|
||||
{
|
||||
(void)en;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool headphones_inserted(void)
|
||||
{
|
||||
bool is_inserted;
|
||||
DSP_GetHeadphoneStatus(&is_inserted);
|
||||
return is_inserted;
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2009 by Thomas Martitz
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __BUTTON_CTRU_H__
|
||||
#define __BUTTON_CTRU_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "config.h"
|
||||
|
||||
bool button_hold(void);
|
||||
#undef button_init_device
|
||||
void button_init_device(void);
|
||||
int button_read_device(int *data);
|
||||
|
||||
#endif /* __BUTTON_CTRU_H__ */
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2010 by Fred Bauer
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __BUTTONMAP_H__
|
||||
#define __BUTTONMAP_H__
|
||||
/* Button maps: simulated key, x, y, radius, name */
|
||||
/* Run sim with --mapping to get coordinates */
|
||||
/* or --debugbuttons to check */
|
||||
/* The First matching button is returned */
|
||||
struct button_map {
|
||||
int button, x, y, radius;
|
||||
char *description;
|
||||
};
|
||||
|
||||
extern struct button_map bm[];
|
||||
|
||||
int xy2button( int x, int y);
|
||||
|
||||
#ifdef HAVE_TOUCHSCREEN
|
||||
int key_to_touch(int keyboard_button, unsigned int mouse_coords);
|
||||
#endif
|
||||
|
||||
#endif /* __BUTTONMAP_H__ */
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2010 by Thomas Martitz
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#define RB_FILESYSTEM_OS
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <utime.h>
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "file.h"
|
||||
#include "dir.h"
|
||||
#include "mv.h"
|
||||
#include "debug.h"
|
||||
#include "pathfuncs.h"
|
||||
#include "string-extra.h"
|
||||
|
||||
#include <3ds/archive.h>
|
||||
|
||||
void paths_init(void)
|
||||
{
|
||||
/* is this needed in 3DS? */
|
||||
#if 0
|
||||
char config_dir[MAX_PATH];
|
||||
|
||||
const char *home = "/3ds";
|
||||
mkdir("/3ds/.rockbox" __MKDIR_MODE_ARG);
|
||||
|
||||
snprintf(config_dir, sizeof(config_dir), "%s/.config", home);
|
||||
mkdir(config_dir __MKDIR_MODE_ARG);
|
||||
snprintf(config_dir, sizeof(config_dir), "%s/.config/rockbox.org", home);
|
||||
mkdir(config_dir __MKDIR_MODE_ARG);
|
||||
/* Plugin data directory */
|
||||
snprintf(config_dir, sizeof(config_dir), "%s/.config/rockbox.org/rocks.data", home);
|
||||
mkdir(config_dir __MKDIR_MODE_ARG);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* only sdcard volume is accesible to the user */
|
||||
void volume_size(IF_MV(int volume,) sector_t *sizep, sector_t *freep)
|
||||
{
|
||||
sector_t size = 0, free = 0;
|
||||
|
||||
FS_ArchiveResource sdmcRSRC;
|
||||
FSUSER_GetSdmcArchiveResource(&sdmcRSRC);
|
||||
|
||||
size = (sdmcRSRC.totalClusters * sdmcRSRC.clusterSize) / sdmcRSRC.sectorSize;
|
||||
free = (sdmcRSRC.freeClusters * sdmcRSRC.clusterSize) / sdmcRSRC.sectorSize;
|
||||
|
||||
if (sizep)
|
||||
*sizep = size;
|
||||
|
||||
if (freep)
|
||||
*freep = free;
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 by Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef _FILESYSTEM_CTRU_H_
|
||||
#define _FILESYSTEM_CTRU_H_
|
||||
|
||||
#if defined(PLUGIN) || defined(CODEC)
|
||||
#define FILEFUNCTIONS_DECLARED
|
||||
#define FILEFUNCTIONS_DEFINED
|
||||
#define DIRFUNCTIONS_DECLARED
|
||||
#define DIRFUNCTIONS_DEFINED
|
||||
#define OSFUNCTIONS_DECLARED
|
||||
#endif /* PLUGIN || CODEC */
|
||||
|
||||
#ifndef OSFUNCTIONS_DECLARED
|
||||
#define FS_PREFIX(_x_) ctru_ ## _x_
|
||||
|
||||
void paths_init(void);
|
||||
#endif /* !OSFUNCTIONS_DECLARED */
|
||||
#endif /* _FILESYSTEM_CTRU_H_ */
|
||||
|
||||
#ifdef _FILE_H_
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef _FILESYSTEM_CTRU__FILE_H_
|
||||
#define _FILESYSTEM_CTRU__FILE_H_
|
||||
|
||||
#ifdef RB_FILESYSTEM_OS
|
||||
#define FILEFUNCTIONS_DEFINED
|
||||
#endif
|
||||
|
||||
#ifndef FILEFUNCTIONS_DECLARED
|
||||
#define __OPEN_MODE_ARG
|
||||
#define __CREAT_MODE_ARG \
|
||||
, mode
|
||||
|
||||
#include <time.h>
|
||||
|
||||
int ctru_open(const char *name, int oflag, ...);
|
||||
int ctru_creat(const char *name, mode_t mode);
|
||||
int ctru_close(int fildes);
|
||||
int ctru_ftruncate(int fildes, off_t length);
|
||||
int ctru_fsync(int fildes);
|
||||
off_t ctru_lseek(int fildes, off_t offset, int whence);
|
||||
ssize_t ctru_read(int fildes, void *buf, size_t nbyte);
|
||||
ssize_t ctru_write(int fildes, const void *buf, size_t nbyte);
|
||||
int ctru_remove(const char *path);
|
||||
int ctru_rename(const char *old, const char *new);
|
||||
int ctru_modtime(const char *path, time_t modtime);
|
||||
off_t ctru_filesize(int fildes);
|
||||
int ctru_fsamefile(int fildes1, int fildes2);
|
||||
int ctru_relate(const char *path1, const char *path2);
|
||||
bool ctru_file_exists(const char *path);
|
||||
ssize_t ctru_readlink(const char *path, char *buf, size_t bufsiz);
|
||||
|
||||
#endif /* !FILEFUNCTIONS_DECLARED */
|
||||
|
||||
#endif /* _FILESYSTEM_CTRU__FILE_H_ */
|
||||
#endif /* _FILE_H_ */
|
||||
|
||||
#ifdef _DIR_H_
|
||||
#ifndef _FILESYSTEM_CTRU__DIR_H_
|
||||
#define _FILESYSTEM_CTRU__DIR_H_
|
||||
|
||||
#define DIRENT dirent
|
||||
struct dirent;
|
||||
|
||||
struct dirinfo_native
|
||||
{
|
||||
unsigned int attr;
|
||||
off_t size;
|
||||
uint16_t wrtdate;
|
||||
uint16_t wrttime;
|
||||
};
|
||||
|
||||
typedef struct {} DIR;
|
||||
|
||||
#ifndef DIRFUNCTIONS_DECLARED
|
||||
#define __MKDIR_MODE_ARG \
|
||||
, 0777
|
||||
|
||||
#ifdef RB_FILESYSTEM_OS
|
||||
#define DIRFUNCTIONS_DEFINED
|
||||
#endif
|
||||
|
||||
DIR * ctru_opendir(const char *dirname);
|
||||
struct dirent * ctru_readdir(DIR *dirp);
|
||||
int ctru_readdir_r(DIR *dirp, struct dirent *entry,
|
||||
struct dirent **result);
|
||||
void ctru_rewinddir(DIR *dirp);
|
||||
int ctru_closedir(DIR *dirp);
|
||||
int ctru_mkdir(const char *path);
|
||||
int ctru_rmdir(const char *path);
|
||||
int ctru_samedir(DIR *dirp1, DIR *dirp2);
|
||||
bool ctru_dir_exists(const char *dirname);
|
||||
#endif /* !DIRFUNCTIONS_DECLARED */
|
||||
|
||||
#endif /* _FILESYSTEM_CTRU__DIR_H_ */
|
||||
#endif /* _DIR_H_ */
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "system-ctru.h"
|
||||
#include "thread-ctru.h"
|
||||
#include "kernel.h"
|
||||
#include "thread.h"
|
||||
#include "panic.h"
|
||||
#include "debug.h"
|
||||
|
||||
static int tick_timer_id;
|
||||
long start_tick;
|
||||
|
||||
/* Condition to signal that "interrupts" may proceed */
|
||||
static sysCond *sim_thread_cond;
|
||||
/* Mutex to serialize changing levels and exclude other threads while
|
||||
* inside a handler */
|
||||
static RecursiveLock sim_irq_mtx;
|
||||
/* Level: 0 = enabled, not 0 = disabled */
|
||||
static int volatile interrupt_level = HIGHEST_IRQ_LEVEL;
|
||||
/* How many handers waiting? Not strictly needed because CondSignal is a
|
||||
* noop if no threads were waiting but it filters-out calls to functions
|
||||
* with higher overhead and provides info when debugging. */
|
||||
static int handlers_pending = 0;
|
||||
/* 1 = executing a handler; prevents CondSignal calls in set_irq_level
|
||||
* while in a handler */
|
||||
static int status_reg = 0;
|
||||
|
||||
/* Nescessary logic:
|
||||
* 1) All threads must pass unblocked
|
||||
* 2) Current handler must always pass unblocked
|
||||
* 3) Threads must be excluded when irq routine is running
|
||||
* 4) No more than one handler routine should execute at a time
|
||||
*/
|
||||
int set_irq_level(int level)
|
||||
{
|
||||
RecursiveLock_Lock(&sim_irq_mtx);
|
||||
|
||||
int oldlevel = interrupt_level;
|
||||
|
||||
if (status_reg == 0 && level == 0 && oldlevel != 0)
|
||||
{
|
||||
/* Not in a handler and "interrupts" are going from disabled to
|
||||
* enabled; signal any pending handlers still waiting */
|
||||
if (handlers_pending > 0)
|
||||
sys_cond_broadcast(sim_thread_cond);
|
||||
}
|
||||
|
||||
interrupt_level = level; /* save new level */
|
||||
|
||||
RecursiveLock_Unlock(&sim_irq_mtx);
|
||||
return oldlevel;
|
||||
}
|
||||
|
||||
void sim_enter_irq_handler(void)
|
||||
{
|
||||
RecursiveLock_Lock(&sim_irq_mtx);
|
||||
handlers_pending++;
|
||||
|
||||
/* Check each time before proceeding: disabled->enabled->...->disabled
|
||||
* is possible on an app thread before a handler thread is ever granted
|
||||
* the mutex; a handler can also leave "interrupts" disabled during
|
||||
* its execution */
|
||||
while (interrupt_level != 0)
|
||||
sys_cond_wait(sim_thread_cond, &sim_irq_mtx);
|
||||
|
||||
status_reg = 1;
|
||||
}
|
||||
|
||||
void sim_exit_irq_handler(void)
|
||||
{
|
||||
/* If any others are waiting, give the signal */
|
||||
if (--handlers_pending > 0)
|
||||
sys_cond_signal(sim_thread_cond);
|
||||
|
||||
status_reg = 0;
|
||||
RecursiveLock_Unlock(&sim_irq_mtx);
|
||||
}
|
||||
|
||||
static bool sim_kernel_init(void)
|
||||
{
|
||||
RecursiveLock_Init(&sim_irq_mtx);
|
||||
sim_thread_cond = sys_cond_create();
|
||||
if (sim_thread_cond == NULL)
|
||||
{
|
||||
panicf("Cannot create sim_thread_cond\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void sim_kernel_shutdown(void)
|
||||
{
|
||||
sys_remove_timer(tick_timer_id);
|
||||
enable_irq();
|
||||
while(handlers_pending > 0)
|
||||
sys_delay(10);
|
||||
|
||||
sys_cond_destroy(sim_thread_cond);
|
||||
}
|
||||
|
||||
u32 tick_timer(u32 interval, void *param)
|
||||
{
|
||||
long new_tick;
|
||||
|
||||
(void) interval;
|
||||
(void) param;
|
||||
|
||||
new_tick = (sys_get_ticks() - start_tick) / (1000/HZ);
|
||||
|
||||
while(new_tick != current_tick)
|
||||
{
|
||||
sim_enter_irq_handler();
|
||||
|
||||
/* Run through the list of tick tasks - increments tick
|
||||
* on each iteration. */
|
||||
call_tick_tasks();
|
||||
|
||||
sim_exit_irq_handler();
|
||||
}
|
||||
|
||||
return interval;
|
||||
}
|
||||
|
||||
void tick_start(unsigned int interval_in_ms)
|
||||
{
|
||||
if (!sim_kernel_init())
|
||||
{
|
||||
panicf("Could not initialize kernel!");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (tick_timer_id != 0)
|
||||
{
|
||||
sys_remove_timer(tick_timer_id);
|
||||
tick_timer_id = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
start_tick = sys_get_ticks();
|
||||
}
|
||||
|
||||
tick_timer_id = sys_add_timer(interval_in_ms, tick_timer, NULL);
|
||||
}
|
||||
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#define RB_FILESYSTEM_OS
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "system.h"
|
||||
#include "load_code.h"
|
||||
#include "filesystem-ctru.h"
|
||||
#include "debug.h"
|
||||
|
||||
void* programResolver(const char* sym, void *userData);
|
||||
void * lc_open(const char *filename, unsigned char *buf, size_t buf_size)
|
||||
{
|
||||
DEBUGF("dlopen(path=\"%s\")\n", filename);
|
||||
|
||||
/* note: the 3ds dlopen implementation needs a custom resolver
|
||||
for the unresolved symbols in shared objects */
|
||||
/* void *handle = dlopen(filename, RTLD_NOW | RTLD_LOCAL); */
|
||||
void *handle = ctrdlOpen(filename,
|
||||
RTLD_NOW | RTLD_LOCAL,
|
||||
programResolver,
|
||||
NULL);
|
||||
if (handle == NULL)
|
||||
{
|
||||
DEBUGF("%s(\"%s\") failed\n", __func__, filename);
|
||||
DEBUGF(" lc_open error '%s'\n", dlerror());
|
||||
}
|
||||
DEBUGF("handle = %p\n", handle);
|
||||
|
||||
return handle;
|
||||
(void) buf; (void) buf_size;
|
||||
}
|
||||
|
||||
void * lc_get_header(void *handle)
|
||||
{
|
||||
void *symbol = dlsym(handle, "__header");
|
||||
if (!symbol) {
|
||||
symbol = dlsym(handle, "___header");
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
void lc_close(void *handle)
|
||||
{
|
||||
if (handle) {
|
||||
dlclose(handle);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <setjmp.h>
|
||||
#include "debug.h"
|
||||
|
||||
#define RESOLVER_ENTRY(name) \
|
||||
if (!strcmp(symName, #name)) \
|
||||
return (void *)name;
|
||||
|
||||
/* void* ctrdlProgramResolver(const char* symName) */
|
||||
void* programResolver(const char* symName, void *userData)
|
||||
{
|
||||
DEBUGF("programResolver(name=\"%s\")\n", symName);
|
||||
|
||||
/* codecs, etc */
|
||||
RESOLVER_ENTRY(abs);
|
||||
RESOLVER_ENTRY(labs);
|
||||
RESOLVER_ENTRY(llabs);
|
||||
RESOLVER_ENTRY(printf);
|
||||
|
||||
/* plugins */
|
||||
RESOLVER_ENTRY(_ctype_);
|
||||
RESOLVER_ENTRY(__errno);
|
||||
RESOLVER_ENTRY(longjmp);
|
||||
RESOLVER_ENTRY(setjmp);
|
||||
|
||||
return NULL;
|
||||
(void) userData;
|
||||
}
|
||||
|
|
@ -1,245 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2006 Dan Everton
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "debug.h"
|
||||
#include "system.h"
|
||||
#include "button-ctru.h"
|
||||
#include "screendump.h"
|
||||
#include "lcd-target.h"
|
||||
|
||||
#include <3ds/gfx.h>
|
||||
#include <3ds/allocator/linear.h>
|
||||
#include <3ds/console.h>
|
||||
#include <3ds/services/cfgu.h>
|
||||
|
||||
/*#define LOGF_ENABLE*/
|
||||
#include "logf.h"
|
||||
|
||||
fb_data *dev_fb = 0;
|
||||
|
||||
static u8 system_model = CFG_MODEL_3DS;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int width, height;
|
||||
} dimensions_t;
|
||||
|
||||
int get_dest_offset(int x, int y, int dest_width)
|
||||
{
|
||||
return dest_width - y - 1 + dest_width * x;
|
||||
}
|
||||
|
||||
int get_source_offset(int x, int y, int source_width)
|
||||
{
|
||||
return x + y * source_width;
|
||||
}
|
||||
|
||||
void copy_framebuffer_16(u16 *dest, const dimensions_t dest_dim, const u16 *source, const dimensions_t source_dim)
|
||||
{
|
||||
int rows = MIN(dest_dim.width, source_dim.height);
|
||||
int cols = MIN(dest_dim.height, source_dim.width);
|
||||
for (int y = 0; y < rows; ++y) {
|
||||
for (int x = 0; x < cols; ++x) {
|
||||
const u16 *s = source + get_source_offset(x, y, source_dim.width);
|
||||
u16 *d = dest + get_dest_offset(x, y, dest_dim.width);
|
||||
*d = *s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void copy_framebuffer_24(u8 *dest, const dimensions_t dest_dim, const u8 *source, const dimensions_t source_dim)
|
||||
{
|
||||
int rows = MIN(dest_dim.width, source_dim.height);
|
||||
int cols = MIN(dest_dim.height, source_dim.width);
|
||||
for (int y = 0; y < rows; ++y) {
|
||||
for (int x = 0; x < cols; ++x) {
|
||||
const u8 *s = source + get_source_offset(x, y, source_dim.width) * 3;
|
||||
u8 *d = dest + get_dest_offset(x, y, dest_dim.width) * 3;
|
||||
d[0] = s[0];
|
||||
d[1] = s[1];
|
||||
d[2] = s[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void copy_framebuffer_32(u32 *dest, const dimensions_t dest_dim, const u32 *source, const dimensions_t source_dim)
|
||||
{
|
||||
int rows = MIN(dest_dim.width, source_dim.height);
|
||||
int cols = MIN(dest_dim.height, source_dim.width);
|
||||
for (int y = 0; y < rows; ++y) {
|
||||
for (int x = 0; x < cols; ++x) {
|
||||
const u32 *s = source + get_source_offset(x, y, source_dim.width);
|
||||
u32 *d = dest + get_dest_offset(x, y, dest_dim.width);
|
||||
*d = *s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_framebuffer(void)
|
||||
{
|
||||
u16 fb_width, fb_height;
|
||||
u32 bufsize;
|
||||
u8* fb = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, &fb_width, &fb_height);
|
||||
bufsize = fb_width * fb_height * 2;
|
||||
|
||||
/* lock_window_mutex() */
|
||||
|
||||
if (FB_DATA_SZ == 2) {
|
||||
copy_framebuffer_16((void *)fb, (dimensions_t){ fb_width, fb_height },
|
||||
(u16 *)dev_fb, (dimensions_t){ LCD_WIDTH, LCD_HEIGHT });
|
||||
}
|
||||
else if (FB_DATA_SZ == 3) {
|
||||
copy_framebuffer_24((void *)fb, (dimensions_t){ fb_width, fb_height },
|
||||
(u8 *)dev_fb, (dimensions_t){ LCD_WIDTH, LCD_HEIGHT });
|
||||
}
|
||||
else {
|
||||
copy_framebuffer_32((void *)fb, (dimensions_t){ fb_width, fb_height },
|
||||
(u32 *)dev_fb, (dimensions_t){ LCD_WIDTH, LCD_HEIGHT });
|
||||
}
|
||||
|
||||
// Flush and swap framebuffers
|
||||
/* gfxFlushBuffers(); */
|
||||
GSPGPU_FlushDataCache(fb, bufsize);
|
||||
gfxSwapBuffers();
|
||||
gspWaitForVBlank();
|
||||
|
||||
/* unlock_window_mutex() */
|
||||
}
|
||||
|
||||
/* lcd-as-memframe.c */
|
||||
extern void lcd_copy_buffer_rect(fb_data *dst, const fb_data *src,
|
||||
int width, int height);
|
||||
|
||||
void lcd_update_rect(int x, int y, int width, int height)
|
||||
{
|
||||
fb_data *dst, *src;
|
||||
|
||||
if (x + width > LCD_WIDTH)
|
||||
width = LCD_WIDTH - x; /* Clip right */
|
||||
if (x < 0)
|
||||
width += x, x = 0; /* Clip left */
|
||||
if (width <= 0)
|
||||
return; /* nothing left to do */
|
||||
|
||||
if (y + height > LCD_HEIGHT)
|
||||
height = LCD_HEIGHT - y; /* Clip bottom */
|
||||
if (y < 0)
|
||||
height += y, y = 0; /* Clip top */
|
||||
if (height <= 0)
|
||||
return; /* nothing left to do */
|
||||
|
||||
dst = LCD_FRAMEBUF_ADDR(x, y);
|
||||
src = FBADDR(x,y);
|
||||
|
||||
/* Copy part of the Rockbox framebuffer to the second framebuffer */
|
||||
if (width < LCD_WIDTH)
|
||||
{
|
||||
/* Not full width - do line-by-line */
|
||||
lcd_copy_buffer_rect(dst, src, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Full width - copy as one line */
|
||||
lcd_copy_buffer_rect(dst, src, LCD_WIDTH*height, 1);
|
||||
}
|
||||
|
||||
update_framebuffer();
|
||||
}
|
||||
|
||||
void lcd_update(void)
|
||||
{
|
||||
/* update a full screen rect */
|
||||
lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
|
||||
}
|
||||
|
||||
void sys_console_init(void)
|
||||
{
|
||||
gfxInit(GSP_BGR8_OES, GSP_RGB565_OES, false);
|
||||
consoleInit(GFX_TOP, NULL);
|
||||
}
|
||||
|
||||
void lcd_init_device(void)
|
||||
{
|
||||
gfxSetDoubleBuffering(GFX_BOTTOM, true);
|
||||
|
||||
/* hidInit(); */
|
||||
|
||||
u16 fb_width, fb_height;
|
||||
u8* fb = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, &fb_width, &fb_height);
|
||||
u32 bufsize = fb_width * fb_height * 2;
|
||||
|
||||
dev_fb = (fb_data *) linearAlloc(bufsize);
|
||||
if (dev_fb == NULL) {
|
||||
logf("could not allocate dev_fb size %d bytes\n",
|
||||
bufsize);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(dev_fb, 0x00, bufsize);
|
||||
|
||||
/* Set dpi value from system model */
|
||||
CFGU_GetSystemModel(&system_model);
|
||||
}
|
||||
|
||||
void lcd_shutdown(void)
|
||||
{
|
||||
if (dev_fb) {
|
||||
linearFree(dev_fb);
|
||||
dev_fb = 0;
|
||||
}
|
||||
|
||||
/* hidExit(); */
|
||||
gfxExit();
|
||||
}
|
||||
|
||||
int lcd_get_dpi(void)
|
||||
{
|
||||
/* link: https://www.reddit.com/r/nintendo/comments/2uzk5y/informative_post_about_dpi_on_the_new_3ds/ */
|
||||
/* all values for bottom lcd */
|
||||
float dpi = 96.0f;
|
||||
switch(system_model) {
|
||||
case CFG_MODEL_3DS:
|
||||
dpi = 132.45f;
|
||||
break;
|
||||
case CFG_MODEL_3DSXL:
|
||||
dpi = 95.69f;
|
||||
break;
|
||||
case CFG_MODEL_N3DS:
|
||||
dpi = 120.1f;
|
||||
break;
|
||||
case CFG_MODEL_2DS:
|
||||
dpi = 132.45f;
|
||||
break;
|
||||
case CFG_MODEL_N3DSXL:
|
||||
dpi = 95.69f;
|
||||
break;
|
||||
case CFG_MODEL_N2DSXL:
|
||||
dpi = 95.69f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
return (int) roundf(dpi);
|
||||
}
|
||||
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __LCDBITMAP_H__
|
||||
#define __LCDBITMAP_H__
|
||||
|
||||
#include "lcd.h"
|
||||
|
||||
void sim_lcd_init(void);
|
||||
void sys_console_init(void);
|
||||
|
||||
#endif /* #ifndef __LCDBITMAP_H__ */
|
||||
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef LCD_TARGET_H
|
||||
#define LCD_TARGET_H
|
||||
|
||||
#include "lcd.h"
|
||||
|
||||
/*
|
||||
Framebuffer device and framebuffer access.
|
||||
See lcd-memframe.c
|
||||
*/
|
||||
extern fb_data *dev_fb;
|
||||
#define LCD_FRAMEBUF_ADDR(col, row) (dev_fb + row * LCD_WIDTH + col)
|
||||
|
||||
#endif /* LCD_TARGET_H */
|
||||
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
#ifndef _BFILE_INTERNAL_H_
|
||||
#define _BFILE_INTERNAL_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/stat.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <3ds/gfx.h>
|
||||
#include <3ds/svc.h>
|
||||
#include <3ds/types.h>
|
||||
#include <3ds/thread.h>
|
||||
#include <3ds/result.h>
|
||||
#include <3ds/services/fs.h>
|
||||
#include <3ds/synchronization.h>
|
||||
|
||||
/* #define MALLOC_DEBUG
|
||||
#include "rmalloc/rmalloc.h" */
|
||||
|
||||
#include "cslice.h"
|
||||
#include "cmap.h"
|
||||
|
||||
#define nil NULL
|
||||
|
||||
/* in go functions can return two values */
|
||||
#define two_type_value(type1, type2, name1, name2, type_name) \
|
||||
typedef struct { \
|
||||
type1 name1; \
|
||||
type2 name2; \
|
||||
} type_name##_t;
|
||||
|
||||
/* single mutex implementation */
|
||||
#define sync_Mutex LightLock
|
||||
static inline void sync_MutexInit(sync_Mutex* m) {
|
||||
LightLock_Init(m);
|
||||
}
|
||||
|
||||
/* mutex unlock */
|
||||
static inline void sync_MutexLock(sync_Mutex* m) {
|
||||
LightLock_Lock(m);
|
||||
}
|
||||
|
||||
/* mutex lock */
|
||||
static inline void sync_MutexUnlock(sync_Mutex* m) {
|
||||
LightLock_Unlock(m);
|
||||
}
|
||||
|
||||
/* read_write mutex implementation */
|
||||
typedef struct {
|
||||
sync_Mutex shared;
|
||||
CondVar reader_q;
|
||||
CondVar writer_q;
|
||||
int active_readers;
|
||||
int active_writers;
|
||||
int waiting_writers;
|
||||
} sync_RWMutex;
|
||||
|
||||
void sync_RWMutexInit(sync_RWMutex *m);
|
||||
void sync_RWMutexRLock(sync_RWMutex *m);
|
||||
void sync_RWMutexRUnlock(sync_RWMutex *m);
|
||||
void sync_RWMutexLock(sync_RWMutex *m);
|
||||
void sync_RWMutexUnlock(sync_RWMutex *m);
|
||||
|
||||
/* declare a two type value with name 'n_err_t' */
|
||||
two_type_value(int, const char*, n, err, int_error);
|
||||
two_type_value(struct stat, const char*, fi, err, stat_error);
|
||||
typedef const char* file_error_t;
|
||||
|
||||
typedef struct page {
|
||||
s64 num;
|
||||
struct page* prev;
|
||||
struct page* next;
|
||||
u8* data;
|
||||
} page;
|
||||
|
||||
/* the two map types used by this library */
|
||||
cmap_declare(page, s64, struct page*);
|
||||
cmap_declare(bool, s64, bool);
|
||||
|
||||
typedef struct shard {
|
||||
sync_Mutex mu;
|
||||
cmap(page) pages;
|
||||
cmap(bool) dirty;
|
||||
struct page* head;
|
||||
struct page* tail;
|
||||
} shard;
|
||||
|
||||
typedef struct Pager {
|
||||
Handle file;
|
||||
s64 pgsize;
|
||||
s64 pgmax;
|
||||
/* sync_RWMutex mu; */
|
||||
s64 size;
|
||||
cslice(shard) shards;
|
||||
} Pager;
|
||||
|
||||
#endif /* _BFILE_INTERNAL_H_ */
|
||||
|
|
@ -1,471 +0,0 @@
|
|||
/*
|
||||
* This code is based on bfile.go by Josh Baker.
|
||||
* Converted to C code by Mauricio G.
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
/* IMPORTANT: this code only works for O_RDONLY and O_RDWR files. */
|
||||
|
||||
#include "bfile.h"
|
||||
|
||||
/* note: for ease of reading and ease of comparing go code
|
||||
with c implementation, function names are similar to the
|
||||
go version */
|
||||
|
||||
/* note2: sync_RWMutex calls have been moved to rockbox sys_file
|
||||
implementation. To use as standalone code please uncomment those calls. */
|
||||
|
||||
void sync_RWMutexInit(sync_RWMutex *m) {
|
||||
LightLock_Init(&m->shared);
|
||||
CondVar_Init(&m->reader_q);
|
||||
CondVar_Init(&m->writer_q);
|
||||
m->active_readers = 0;
|
||||
m->active_writers = 0;
|
||||
m->waiting_writers = 0;
|
||||
}
|
||||
|
||||
static inline LightLock *unique_lock(LightLock *lk) {
|
||||
LightLock_Lock(lk);
|
||||
return lk;
|
||||
}
|
||||
|
||||
void sync_RWMutexRLock(sync_RWMutex *m) {
|
||||
LightLock *lk = unique_lock(&m->shared);
|
||||
while (m->waiting_writers != 0) {
|
||||
CondVar_Wait(&m->reader_q, lk);
|
||||
}
|
||||
++m->active_readers;
|
||||
LightLock_Unlock(lk);
|
||||
}
|
||||
|
||||
void sync_RWMutexRUnlock(sync_RWMutex *m) {
|
||||
LightLock *lk = unique_lock(&m->shared);
|
||||
--m->active_readers;
|
||||
LightLock_Unlock(lk);
|
||||
CondVar_Signal(&m->writer_q);
|
||||
}
|
||||
|
||||
void sync_RWMutexLock(sync_RWMutex *m) {
|
||||
LightLock *lk = unique_lock(&m->shared);
|
||||
++m->waiting_writers;
|
||||
while ((m->active_readers != 0) || (m->active_writers != 0)) {
|
||||
CondVar_Wait(&m->writer_q, lk);
|
||||
}
|
||||
++m->active_writers;
|
||||
LightLock_Unlock(lk);
|
||||
}
|
||||
|
||||
void sync_RWMutexUnlock(sync_RWMutex *m) {
|
||||
LightLock *lk = unique_lock(&m->shared);
|
||||
--m->waiting_writers;
|
||||
--m->active_writers;
|
||||
if (m->waiting_writers > 0) {
|
||||
CondVar_Signal(&m->writer_q);
|
||||
} else {
|
||||
CondVar_Broadcast(&m->reader_q);
|
||||
}
|
||||
LightLock_Unlock(lk);
|
||||
}
|
||||
|
||||
void s_init(shard* s);
|
||||
|
||||
void s_push(shard* s, page* p) {
|
||||
s->head->next->prev = p;
|
||||
p->next = s->head->next;
|
||||
p->prev = s->head;
|
||||
s->head->next = p;
|
||||
}
|
||||
|
||||
void s_pop(shard* s, page* p) {
|
||||
p->prev->next = p->next;
|
||||
p->next->prev = p->prev;
|
||||
}
|
||||
|
||||
void s_bump(shard* s, page* p) {
|
||||
s_pop(s, p);
|
||||
s_push(s, p);
|
||||
}
|
||||
|
||||
/* page_pair_t destructor */
|
||||
/* page_pair_t type is defined by cmap_declare(page, s64, struct page*) */
|
||||
/* struct {
|
||||
s64 key;
|
||||
struct page* value;
|
||||
} page_pair_t;
|
||||
*/
|
||||
void page_pair_free(void* pair_ptr) {
|
||||
if (pair_ptr) {
|
||||
page_pair_t *pair = (page_pair_t*) pair_ptr;
|
||||
struct page *p = pair->value;
|
||||
if (p != nil) {
|
||||
if (p->data != nil) {
|
||||
free(p->data);
|
||||
}
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* shard destructor */
|
||||
void s_free(void* s_ptr) {
|
||||
if (s_ptr) {
|
||||
shard *s = (shard*) s_ptr;
|
||||
if (s->pages != nil) {
|
||||
cmap_set_elem_destructor(s->pages, page_pair_free);
|
||||
cmap_clear(page, s->pages);
|
||||
cmap_clear(bool, s->dirty);
|
||||
free(s->head);
|
||||
free(s->tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pager* NewPager(Handle file) {
|
||||
return NewPagerSize(file, 0, 0);
|
||||
}
|
||||
|
||||
Pager* NewPagerSize(Handle file, int pageSize, int bufferSize) {
|
||||
if (pageSize <= 0) {
|
||||
pageSize = defaultPageSize;
|
||||
} else if ((pageSize & 4095) != 0) {
|
||||
// must be a power of two
|
||||
int x = 1;
|
||||
while (x < pageSize) {
|
||||
x *= 2;
|
||||
}
|
||||
pageSize = x;
|
||||
}
|
||||
|
||||
if (bufferSize <= 0) {
|
||||
bufferSize = defaultBufferSize;
|
||||
} else if (bufferSize < pageSize) {
|
||||
bufferSize = pageSize;
|
||||
}
|
||||
|
||||
Pager* f = (Pager*) malloc(sizeof(Pager));
|
||||
f->file = file;
|
||||
f->size = -1;
|
||||
f->pgsize = (s64) pageSize;
|
||||
|
||||
/* sync_RWMutexInit(&f->mu); */
|
||||
|
||||
// calculate the max number of pages across all shards
|
||||
s64 pgmax = (s64) bufferSize / f->pgsize;
|
||||
if (pgmax < minPages) {
|
||||
pgmax = minPages;
|
||||
}
|
||||
|
||||
// calculate how many shards are needed, power of 2
|
||||
s64 nshards = (s64) ceil((double) pgmax / (double) pagesPerShard);
|
||||
if (nshards > maxShards) {
|
||||
nshards = maxShards;
|
||||
}
|
||||
s64 x = 1;
|
||||
while (x < nshards) {
|
||||
x *= 2;
|
||||
}
|
||||
nshards = x;
|
||||
|
||||
// calculate the max number of pages per shard
|
||||
f->pgmax = (s64) floor((double) pgmax / (double) nshards);
|
||||
cslice_make(f->shards, nshards, (shard) { 0 });
|
||||
|
||||
// initialize sync mutex
|
||||
size_t i;
|
||||
for (i = 0; i < cslice_len(f->shards); i++) {
|
||||
sync_MutexInit(&f->shards[i].mu);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
static int_error_t read_at(Handle file, u8 *data, size_t data_len, off_t off)
|
||||
{
|
||||
u32 read_bytes = 0;
|
||||
if (R_FAILED(FSFILE_Read(file, &read_bytes, (u64) off, data, (u32) data_len))) {
|
||||
return (int_error_t) { -1, "I/O error" };
|
||||
}
|
||||
|
||||
/* io.EOF */
|
||||
if (read_bytes == 0) {
|
||||
return (int_error_t) { 0, "io.EOF" };
|
||||
}
|
||||
|
||||
return (int_error_t) { (int) read_bytes, nil };
|
||||
}
|
||||
|
||||
static int_error_t write_at(Handle file, u8 *data, size_t data_len, off_t off)
|
||||
{
|
||||
u32 written = 0;
|
||||
if (R_FAILED(FSFILE_Write(file, &written, (u64) off, data, (u32) data_len, FS_WRITE_FLUSH))) {
|
||||
return (int_error_t) { -1, "I/O error" };
|
||||
}
|
||||
|
||||
/* I/O error */
|
||||
if ((written == 0) || (written < (u32) data_len)) {
|
||||
return (int_error_t) { -1, "I/O error" };
|
||||
}
|
||||
|
||||
return (int_error_t) { (int) written, nil };
|
||||
}
|
||||
|
||||
static stat_error_t file_stat(Handle file)
|
||||
{
|
||||
u64 size = 0;
|
||||
struct stat fi = { 0 };
|
||||
if (R_FAILED(FSFILE_GetSize(file, &size))) {
|
||||
fi.st_size = 0;
|
||||
return (stat_error_t) { fi, "I/O error" };
|
||||
}
|
||||
|
||||
fi.st_size = (off_t) size;
|
||||
return (stat_error_t) { fi, nil };
|
||||
}
|
||||
|
||||
void s_init(shard* s)
|
||||
{
|
||||
if (s->pages == nil) {
|
||||
s->pages = cmap_make(/*s64*/page);
|
||||
s->dirty = cmap_make(/*s64*/bool);
|
||||
s->head = (page*) malloc(sizeof(page));
|
||||
s->tail = (page*) malloc(sizeof(page));
|
||||
s->head->next = s->tail;
|
||||
s->tail->prev = s->head;
|
||||
}
|
||||
}
|
||||
|
||||
file_error_t f_write(Pager* f, page* p) {
|
||||
s64 off = p->num * f->pgsize;
|
||||
s64 end = f->pgsize;
|
||||
if ((off + end) > f->size) {
|
||||
end = f->size - off;
|
||||
}
|
||||
int_error_t __err = write_at(f->file, p->data, end, off);
|
||||
if (__err.err != nil) {
|
||||
return __err.err;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
file_error_t f_read(Pager* f, page* p) {
|
||||
int_error_t __err = read_at(f->file, p->data, f->pgsize, p->num * f->pgsize);
|
||||
if ((__err.err != nil) && strcmp(__err.err, "io.EOF")) {
|
||||
return "I/O error";
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
const char* f_incrSize(Pager* f, s64 end, bool write)
|
||||
{
|
||||
#define defer(m) \
|
||||
sync_RWMutexUnlock(&f->mu); \
|
||||
sync_RWMutexRLock(&f->mu);
|
||||
|
||||
/* sync_RWMutexRUnlock(&f->mu);
|
||||
sync_RWMutexLock(&f->mu); */
|
||||
|
||||
if (f->size == -1) {
|
||||
stat_error_t fi_err = file_stat(f->file);
|
||||
if (fi_err.err != nil) {
|
||||
/* defer(&f->mu); */
|
||||
return nil;
|
||||
}
|
||||
f->size = fi_err.fi.st_size;
|
||||
}
|
||||
if (write && (end > f->size)) {
|
||||
f->size = end;
|
||||
}
|
||||
|
||||
/* defer(&f->mu); */
|
||||
return nil;
|
||||
}
|
||||
|
||||
int_error_t f_pio(Pager *f, u8 *b, size_t len_b, s64 pnum, s64 pstart, s64 pend, bool write);
|
||||
int_error_t f_io(Pager *f, u8 *b, size_t len_b, s64 off, bool write) {
|
||||
if (f == nil) {
|
||||
return (int_error_t) { 0, "invalid argument" };
|
||||
}
|
||||
bool eof = false;
|
||||
s64 start = off, end = off + len_b;
|
||||
if (start < 0) {
|
||||
return (int_error_t) { 0, "negative offset" };
|
||||
}
|
||||
|
||||
// Check the upper bounds of the input to the known file size.
|
||||
// Increase the file size if needed.
|
||||
/* sync_RWMutexRLock(&f->mu); */
|
||||
if (end > f->size) {
|
||||
file_error_t err = f_incrSize(f, end, write);
|
||||
if (err != nil) {
|
||||
/* sync_RWMutexRUnlock(&f->mu); */
|
||||
return (int_error_t) { 0, err };
|
||||
}
|
||||
if (!write && (end > f->size)) {
|
||||
end = f->size;
|
||||
if ((end - start) < 0) {
|
||||
end = start;
|
||||
}
|
||||
eof = true;
|
||||
len_b = end-start; /* b = b[:end-start] */
|
||||
}
|
||||
}
|
||||
/* sync_RWMutexRUnlock(&f->mu); */
|
||||
|
||||
// Perform the page I/O.
|
||||
int total = 0;
|
||||
while (len_b > 0) {
|
||||
s64 pnum = off / f->pgsize;
|
||||
s64 pstart = off & (f->pgsize - 1);
|
||||
s64 pend = pstart + (s64) len_b;
|
||||
if (pend > f->pgsize) {
|
||||
pend = f->pgsize;
|
||||
}
|
||||
|
||||
int_error_t result = f_pio(f, b, pend - pstart, pnum, pstart, pend, write);
|
||||
if (result.err != nil) {
|
||||
return (int_error_t) { total, result.err };
|
||||
}
|
||||
|
||||
off += (s64) result.n;
|
||||
total += result.n;
|
||||
b = &b[result.n]; len_b -= result.n; /* b = b[n:] */
|
||||
}
|
||||
if (eof) {
|
||||
return (int_error_t) { total, "io.EOF" };
|
||||
}
|
||||
|
||||
return (int_error_t) { total, nil };
|
||||
}
|
||||
|
||||
int_error_t f_pio(Pager *f, u8 *b, size_t len_b, s64 pnum, s64 pstart, s64 pend, bool write) {
|
||||
/* printf("pio(%p, %d, %lld, %lld, %lld, %s)\n", b, len_b, pnum, pstart, pend, write == true ? "true" : "false"); */
|
||||
shard *s = &f->shards[pnum & (s64) (cslice_len(f->shards) - 1)];
|
||||
sync_MutexLock(&s->mu);
|
||||
s_init(s);
|
||||
page *p = cmap_get_ptr(page, s->pages, pnum);
|
||||
if (p == nil) {
|
||||
// Page does not exist in memory.
|
||||
// Acquire a new one.
|
||||
if (cmap_len(s->pages) == f->pgmax) {
|
||||
// The buffer is at capacity.
|
||||
// Evict lru page and hang on to it.
|
||||
p = s->tail->prev;
|
||||
s_pop(s, p);
|
||||
cmap_delete(page, s->pages, p->num);
|
||||
if (cmap_get(bool, s->dirty, p->num)) {
|
||||
// dirty page. flush it now
|
||||
file_error_t err = f_write(f, p);
|
||||
if (err != nil) {
|
||||
sync_MutexUnlock(&s->mu);
|
||||
return (int_error_t) { 0, err };
|
||||
}
|
||||
cmap_delete(bool, s->dirty, p->num);
|
||||
}
|
||||
// Clear the previous page memory for partial page writes for
|
||||
// pages that are being partially written to.
|
||||
if (write && ((pend - pstart) < f->pgsize)) {
|
||||
memset(p->data, 0, f->pgsize);
|
||||
}
|
||||
} else {
|
||||
// Allocate an entirely new page.
|
||||
p = (page *) malloc(sizeof(page));
|
||||
p->data = (u8 *) malloc(f->pgsize);
|
||||
}
|
||||
p->num = pnum;
|
||||
// Read contents of page from file for all read operations, and
|
||||
// partial write operations. Ignore for full page writes.
|
||||
if (!write || ((pend-pstart) < f->pgsize)) {
|
||||
file_error_t err = f_read(f, p);
|
||||
if (err != nil) {
|
||||
sync_MutexUnlock(&s->mu);
|
||||
return (int_error_t) { 0, err };
|
||||
}
|
||||
}
|
||||
// Add the newly acquired page to the list.
|
||||
cmap_set(page, s->pages, p->num, p);
|
||||
s_push(s, p);
|
||||
} else {
|
||||
// Bump the page to the front of the list.
|
||||
s_bump(s, p);
|
||||
}
|
||||
if (write) {
|
||||
memcpy(p->data + pstart, b, pend - pstart);
|
||||
cmap_set(bool, s->dirty, pnum, true);
|
||||
} else {
|
||||
memcpy(b, p->data + pstart, pend - pstart);
|
||||
}
|
||||
sync_MutexUnlock(&s->mu);
|
||||
return (int_error_t) { len_b, nil };
|
||||
}
|
||||
|
||||
// Flush writes any unwritten buffered data to the underlying file.
|
||||
file_error_t PagerFlush(Pager *f) {
|
||||
if (f == nil) {
|
||||
return "invalid argument";
|
||||
}
|
||||
|
||||
/* sync_RWMutexLock(&f->mu); */
|
||||
for (size_t i = 0; i < cslice_len(f->shards); i++) {
|
||||
cmap_iterator(bool) pnum;
|
||||
if (f->shards[i].dirty != nil) {
|
||||
for (pnum = cmap_begin(f->shards[i].dirty);
|
||||
pnum != cmap_end(f->shards[i].dirty); pnum++) {
|
||||
if (pnum->value == true) {
|
||||
page *p = cmap_get_ptr(page, f->shards[i].pages, pnum->key);
|
||||
if (p != nil) {
|
||||
file_error_t err = f_write(f, p);
|
||||
if (err != nil) {
|
||||
/* sync_RWMutexUnlock(&f->mu); */
|
||||
return err;
|
||||
}
|
||||
}
|
||||
cmap_set(bool, f->shards[i].dirty, pnum->key, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* sync_RWMutexUnlock(&f->mu); */
|
||||
return nil;
|
||||
}
|
||||
|
||||
// ReadAt reads len(b) bytes from the File starting at byte offset off.
|
||||
int_error_t PagerReadAt(Pager *f, u8 *b, size_t len_b, off_t off) {
|
||||
return f_io(f, b, len_b, off, false);
|
||||
}
|
||||
|
||||
// WriteAt writes len(b) bytes to the File starting at byte offset off.
|
||||
int_error_t PagerWriteAt(Pager *f, u8 *b, size_t len_b, off_t off) {
|
||||
return f_io(f, b, len_b, off, true);
|
||||
}
|
||||
|
||||
file_error_t PagerTruncate(Pager *f, off_t length) {
|
||||
if (f == nil) {
|
||||
return "invalid argument";
|
||||
}
|
||||
|
||||
/* flush unwritten changes to disk */
|
||||
PagerFlush(f);
|
||||
|
||||
/* sync_RWMutexRLock(&f->mu); */
|
||||
/* set new file size */
|
||||
Handle handle = f->file;
|
||||
Result res = FSFILE_SetSize(handle, (u64) length);
|
||||
if (R_FAILED(res)) {
|
||||
return "I/O error";
|
||||
}
|
||||
/* sync_RWMutexRUnlock(&f->mu); */
|
||||
|
||||
/* FIXME: truncate only required pages. Remove all for now */
|
||||
PagerClear(f);
|
||||
f = NewPager(handle);
|
||||
return nil;
|
||||
}
|
||||
|
||||
void PagerClear(Pager *f) {
|
||||
if (f) {
|
||||
cslice_set_elem_destructor(f->shards, s_free);
|
||||
cslice_clear(f->shards);
|
||||
free(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#ifndef _BFILE_H_
|
||||
#define _BFILE_H_
|
||||
|
||||
#include "bfile-internal.h"
|
||||
|
||||
static const int defaultPageSize = 4096; // all pages are this size
|
||||
static const int defaultBufferSize = 0x800000; // default buffer size, 8 MB
|
||||
static const int minPages = 4; // minimum total pages per file
|
||||
static const int pagesPerShard = 32; // ideal number of pages per shard
|
||||
static const int maxShards = 128; // maximum number of shards per file
|
||||
|
||||
// NewPager returns a new Pager that is backed by the provided file.
|
||||
Pager* NewPager(Handle file);
|
||||
|
||||
// NewPagerSize returns a new Pager with a custom page size and buffer size.
|
||||
// The bufferSize is the maximum amount of memory dedicated to individual
|
||||
// pages. Setting pageSize and bufferSize to zero will use their defaults,
|
||||
// which are 4096 and 8 MB respectively. Custom values are rounded up to the
|
||||
// nearest power of 2.
|
||||
Pager* NewPagerSize(Handle file, int pageSize, int bufferSize);
|
||||
|
||||
// ReadAt reads len(b) bytes from the File starting at byte offset off.
|
||||
int_error_t PagerReadAt(Pager *f, u8 *b, size_t len_b, off_t off);
|
||||
|
||||
// WriteAt writes len(b) bytes to the File starting at byte offset off.
|
||||
int_error_t PagerWriteAt(Pager *f, u8 *b, size_t len_b, off_t off);
|
||||
|
||||
// Flush writes any unwritten buffered data to the underlying file.
|
||||
file_error_t PagerFlush(Pager *f);
|
||||
|
||||
// Truncates pager to specified length
|
||||
file_error_t PagerTruncate(Pager *f, off_t length);
|
||||
|
||||
// Free all memory associated to a Pager file
|
||||
void PagerClear(Pager *f);
|
||||
|
||||
#endif /* _B_FILE_H_ */
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
#ifndef CMAP_H_
|
||||
#define CMAP_H_
|
||||
|
||||
#define CVECTOR_LINEAR_GROWTH
|
||||
#include "cvector.h"
|
||||
|
||||
/* note: for ease of porting go code to c, many functions (macros) names
|
||||
remain similar to the ones used by go */
|
||||
|
||||
/* note2: this is a very basic map implementation. It does not do any sorting, and only works for basic types (and pointers) that can be compared with the equality operator */
|
||||
|
||||
#define nil NULL
|
||||
|
||||
#define cmap_elem_destructor_t cvector_elem_destructor_t
|
||||
|
||||
/**
|
||||
* @brief cmap_declare - The map type used in this library
|
||||
* @param name - The name associated to a map type.
|
||||
* @param key_type - The map pair key type.
|
||||
* @param val_type - The map pair value type.
|
||||
* @param compare_func - The function used to compare for key_type. Should return value < 0 when a < b, 0 when a == b and value > 0 when a > b.
|
||||
*/
|
||||
#define cmap_declare(name, key_type, val_type) \
|
||||
typedef struct { \
|
||||
key_type key; \
|
||||
val_type value; \
|
||||
} name##_pair_t; \
|
||||
\
|
||||
typedef struct { \
|
||||
cvector(name##_pair_t) tree; \
|
||||
cmap_elem_destructor_t elem_destructor; \
|
||||
} name##_map_t; \
|
||||
\
|
||||
static inline val_type name##_get_( \
|
||||
name##_map_t *this, const key_type key) \
|
||||
{ \
|
||||
if (this) { \
|
||||
size_t i; \
|
||||
for (i = 0; i < cvector_size(this->tree); i++) { \
|
||||
if (key == this->tree[i].key) { \
|
||||
return this->tree[i].value; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
static inline val_type name##_get_ptr_( \
|
||||
name##_map_t *this, const key_type key) \
|
||||
{ \
|
||||
if (this) { \
|
||||
size_t i; \
|
||||
for (i = 0; i < cvector_size(this->tree); i++) { \
|
||||
if (key == this->tree[i].key) { \
|
||||
return this->tree[i].value; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
return nil; \
|
||||
} \
|
||||
\
|
||||
static inline void name##_set_( \
|
||||
name##_map_t *this, const key_type key, val_type value) \
|
||||
{ \
|
||||
if (this) { \
|
||||
size_t i; \
|
||||
for (i = 0; i < cvector_size(this->tree); i++) { \
|
||||
if (key == this->tree[i].key) { \
|
||||
this->tree[i].value = value; \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
name##_pair_t new_pair = (name##_pair_t) { key, value }; \
|
||||
cvector_push_back(this->tree, new_pair); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void name##_delete_( \
|
||||
name##_map_t *this, const key_type key) \
|
||||
{ \
|
||||
if (this) { \
|
||||
size_t i; \
|
||||
for (i = 0; i < cvector_size(this->tree); i++) { \
|
||||
if (key == this->tree[i].key) { \
|
||||
cvector_erase(this->tree, i); \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline name##_map_t* name##_map_make_(void) \
|
||||
{ \
|
||||
name##_map_t *map = (name##_map_t*) malloc(sizeof(name##_map_t)); \
|
||||
if (map) { \
|
||||
map->tree = nil; \
|
||||
cvector_init(map->tree, 0, nil); \
|
||||
return map; \
|
||||
} \
|
||||
return nil; \
|
||||
} \
|
||||
\
|
||||
static inline void name##_clear_(name##_map_t *this) \
|
||||
{ \
|
||||
if (this) { \
|
||||
cvector_free(this->tree); \
|
||||
free(this); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
|
||||
|
||||
/**
|
||||
* @brief cmap - The map type used in this library
|
||||
* @param name - The name associated to a map type.
|
||||
*/
|
||||
#define cmap(name) name##_map_t *
|
||||
|
||||
/**
|
||||
* @brief cmap_make - creates a new map. Automatically initializes the map.
|
||||
* @param name - the name asociated to the map type
|
||||
* @return a pointer to a new map.
|
||||
*/
|
||||
#define cmap_make(name) name##_map_make_()
|
||||
|
||||
/**
|
||||
* @brief cmap_size - gets the current size of the map
|
||||
* @param map_ptr - the map pointer
|
||||
* @return the size as a size_t
|
||||
*/
|
||||
#define cmap_len(map_ptr) cvector_size(map_ptr->tree)
|
||||
|
||||
/**
|
||||
* @brief cmap_get - gets value associated to a key.
|
||||
* @param name - the name asociated to the map type
|
||||
* @param map_ptr - the map pointer
|
||||
* @param key - the key to search for
|
||||
* @return the value associated to a key
|
||||
*/
|
||||
#define cmap_get(name, map_ptr, key) name##_get_(map_ptr, key)
|
||||
|
||||
/**
|
||||
* @brief cmap_get-ptr - gets ptr_value associated to a key. Use it to avoid assigning a ptr to 0.
|
||||
* @param name - the name asociated to the map type
|
||||
* @param map_ptr - the map pointer
|
||||
* @param key - the key to search for
|
||||
* @return the value associated to a key
|
||||
*/
|
||||
#define cmap_get_ptr(name, map_ptr, key) name##_get_ptr_(map_ptr, key)
|
||||
|
||||
|
||||
/**
|
||||
* @brief cmap_set - sets value associated to a key.
|
||||
* @param name - the name asociated to the map type
|
||||
* @param map_ptr - the map pointer
|
||||
* @param key - the key to search for
|
||||
* @param value - the new value
|
||||
* @return void
|
||||
*/
|
||||
#define cmap_set(name, map_ptr, key, val) name##_set_(map_ptr, key, val)
|
||||
|
||||
/**
|
||||
* @brief cmap_delete - deletes map entry associated to a key.
|
||||
* @param name - the name asociated to the map type
|
||||
* @param map_ptr - the map pointer
|
||||
* @param key - the key to search for
|
||||
* @return void
|
||||
*/
|
||||
#define cmap_delete(name, map_ptr, key) name##_delete_(map_ptr, key)
|
||||
|
||||
/**
|
||||
* @brief cmap_set_elem_destructor - set the element destructor function
|
||||
* used to clean up removed elements. The map must NOT be NULL for this to do anything.
|
||||
* @param map_ptr - the map pointer
|
||||
* @param elem_destructor_fn - function pointer of type cvector_elem_destructor_t used to destroy elements
|
||||
* @return void
|
||||
*/
|
||||
#define cmap_set_elem_destructor(map_ptr, elem_destructor_fn) \
|
||||
cvector_set_elem_destructor(map_ptr->tree, elem_destructor_fn)
|
||||
|
||||
/**
|
||||
* @brief cmap_clear - deletes all map entries. And frees memory is an element destructor was set previously.
|
||||
* @param name - the name asociated to the map type
|
||||
* @param map_ptr - the map pointer
|
||||
* @return void
|
||||
*/
|
||||
#define cmap_clear(name, map_ptr) name##_clear_(map_ptr)
|
||||
|
||||
/**
|
||||
* @brief cmap_iterator - The iterator type used for cmap
|
||||
* @param type The type of iterator to act on.
|
||||
*/
|
||||
#define cmap_iterator(name) cvector_iterator(name##_pair_t)
|
||||
|
||||
/**
|
||||
* @brief cmap_begin - returns an iterator to first element of the vector
|
||||
* @param map_ptr - the map pointer
|
||||
* @return a pointer to the first element (or NULL)
|
||||
*/
|
||||
#define cmap_begin(map_ptr) ((map_ptr) ? cvector_begin(map_ptr->tree) : nil)
|
||||
|
||||
/**
|
||||
* @brief cmap_end - returns an iterator to one past the last element of the vector
|
||||
* @param map_ptrs - the map pointer
|
||||
* @return a pointer to one past the last element (or NULL)
|
||||
*/
|
||||
#define cmap_end(map_ptr) ((map_ptr) ? cvector_end(map_ptr->tree) : nil)
|
||||
|
||||
#endif /* CMAP_H_ */
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
#ifndef CSLICE_H_
|
||||
#define CSLICE_H_
|
||||
|
||||
#define CVECTOR_LINEAR_GROWTH
|
||||
#include "cvector.h"
|
||||
|
||||
/* note: for ease of porting go code to c, many functions (macros) names
|
||||
remain similar to the ones used by go */
|
||||
|
||||
#define nil NULL
|
||||
|
||||
/**
|
||||
* @brief cslice - The slice type used in this library
|
||||
* @param type The type of slice to act on.
|
||||
*/
|
||||
#define cslice(type) cvector(type)
|
||||
|
||||
/**
|
||||
* @brief cslice_make - creates a new slice. Automatically initializes the slice.
|
||||
* @param slice - the slice
|
||||
* @param count - new size of the slice
|
||||
* @param value - the value to initialize new elements with
|
||||
* @return void
|
||||
*/
|
||||
#define cslice_make(slice, capacity, value) \
|
||||
do { \
|
||||
slice = nil; \
|
||||
cvector_init(slice, capacity, nil); \
|
||||
cvector_resize(slice, capacity, value); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief cslice_size - gets the current size of the slice
|
||||
* @param slice - the slice
|
||||
* @return the size as a size_t
|
||||
*/
|
||||
#define cslice_len(slice) cvector_size(slice)
|
||||
|
||||
/**
|
||||
* @brief cslice_capacity - gets the current capacity of the slice
|
||||
* @param slice - the slice
|
||||
* @return the capacity as a size_t
|
||||
*/
|
||||
#define cslice_cap(slice) cvector_capacity(slice)
|
||||
|
||||
/**
|
||||
* @brief cslice_set_elem_destructor - set the element destructor function
|
||||
* used to clean up removed elements. The vector must NOT be NULL for this to do anything.
|
||||
* @param slice - the slice
|
||||
* @param elem_destructor_fn - function pointer of type cslice_elem_destructor_t used to destroy elements
|
||||
* @return void
|
||||
*/
|
||||
#define cslice_set_elem_destructor(slice, elem_destructor_fn) \
|
||||
cvector_set_elem_destructor(slice, elem_destructor_fn)
|
||||
|
||||
/**
|
||||
* @brief cslice_free - frees all memory associated with the slice
|
||||
* @param slice - the slice
|
||||
* @return void
|
||||
*/
|
||||
#define cslice_clear(slice) cvector_free(slice)
|
||||
|
||||
#endif /* CSLICE_H_ */
|
||||
|
|
@ -1,549 +0,0 @@
|
|||
#ifndef CVECTOR_H_
|
||||
#define CVECTOR_H_
|
||||
/**
|
||||
* @copyright Copyright (c) 2015 Evan Teran,
|
||||
* License: The MIT License (MIT)
|
||||
* @brief cvector heap implemented using C library malloc()
|
||||
* @file cvector.h
|
||||
*/
|
||||
|
||||
/* in case C library malloc() needs extra protection,
|
||||
* allow these defines to be overridden.
|
||||
*/
|
||||
/* functions for allocation and deallocation need to correspond to each other, fall back to C library functions if not all are overridden */
|
||||
#if !defined(cvector_clib_free) || !defined(cvector_clib_malloc) || !defined(cvector_clib_calloc) || !defined(cvector_clib_realloc)
|
||||
#ifdef cvector_clib_free
|
||||
#undef cvector_clib_free
|
||||
#endif
|
||||
#ifdef cvector_clib_malloc
|
||||
#undef cvector_clib_malloc
|
||||
#endif
|
||||
#ifdef cvector_clib_calloc
|
||||
#undef cvector_clib_calloc
|
||||
#endif
|
||||
#ifdef cvector_clib_realloc
|
||||
#undef cvector_clib_realloc
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#define cvector_clib_free free
|
||||
#define cvector_clib_malloc malloc
|
||||
#define cvector_clib_calloc calloc
|
||||
#define cvector_clib_realloc realloc
|
||||
#endif
|
||||
/* functions independent of memory allocation */
|
||||
#ifndef cvector_clib_assert
|
||||
#include <assert.h> /* for assert */
|
||||
#define cvector_clib_assert assert
|
||||
#endif
|
||||
#ifndef cvector_clib_memcpy
|
||||
#include <string.h> /* for memcpy */
|
||||
#define cvector_clib_memcpy memcpy
|
||||
#endif
|
||||
#ifndef cvector_clib_memmove
|
||||
#include <string.h> /* for memmove */
|
||||
#define cvector_clib_memmove memmove
|
||||
#endif
|
||||
|
||||
/* NOTE: Similar to C's qsort and bsearch, you will receive a T*
|
||||
* for a vector of Ts. This means that you cannot use `free` directly
|
||||
* as a destructor. Instead if you have for example a cvector_vector_type(int *)
|
||||
* you will need to supply a function which casts `elem_ptr` to an `int**`
|
||||
* and then does a free on what that pointer points to:
|
||||
*
|
||||
* ex:
|
||||
*
|
||||
* void free_int(void *p) { free(*(int **)p); }
|
||||
*/
|
||||
typedef void (*cvector_elem_destructor_t)(void *elem_ptr);
|
||||
|
||||
typedef struct cvector_metadata_t {
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
cvector_elem_destructor_t elem_destructor;
|
||||
} cvector_metadata_t;
|
||||
|
||||
/**
|
||||
* @brief cvector_vector_type - The vector type used in this library
|
||||
* @param type The type of vector to act on.
|
||||
*/
|
||||
#define cvector_vector_type(type) type *
|
||||
|
||||
/**
|
||||
* @brief cvector - Syntactic sugar to retrieve a vector type
|
||||
* @param type The type of vector to act on.
|
||||
*/
|
||||
#define cvector(type) cvector_vector_type(type)
|
||||
|
||||
/**
|
||||
* @brief cvector_iterator - The iterator type used for cvector
|
||||
* @param type The type of iterator to act on.
|
||||
*/
|
||||
#define cvector_iterator(type) cvector_vector_type(type)
|
||||
|
||||
/**
|
||||
* @note you can also safely pass a pointer to a cvector iterator to a function
|
||||
* but you have to update the pointer at the end to update the original
|
||||
* iterator.
|
||||
* example:
|
||||
* void function( cvector_vector_type( type ) * p_it )
|
||||
* {
|
||||
* cvector_vector_type( type ) it = *p_it;
|
||||
* it ++;
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* *p_it = it;
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief cvector_vector_type_ptr - helper to make code more "readable"
|
||||
* @param type - the vector type pointer
|
||||
*/
|
||||
#define cvector_ptr_type(type) \
|
||||
cvector_vector_type(type) *
|
||||
|
||||
/**
|
||||
* @brief cvector_vector_ptr_get_iterator/set - helpers to make code more "readable"
|
||||
* @param it - the vector iterator
|
||||
* @param ptr - the vector pointer
|
||||
*/
|
||||
#define cvector_ptr_get_iterator(ptr) \
|
||||
*(ptr)
|
||||
|
||||
#define cvector_ptr_set(ptr, it) \
|
||||
*(ptr) = it
|
||||
|
||||
/**
|
||||
* @brief cvector_vector_container_declare - defined a vector container type
|
||||
*/
|
||||
#define cvector_vector_container_declare(name, type) \
|
||||
struct cvector_vector_container_##name { \
|
||||
cvector_vector_type(type) vector; \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief cvector_vector_container - used to pass a cvector wrapped inside a container as a function parameter
|
||||
*/
|
||||
#define cvector_vector_container(name) \
|
||||
struct cvector_vector_container_##name
|
||||
|
||||
|
||||
/**
|
||||
* @brief cvector_vec_to_base - For internal use, converts a vector pointer to a metadata pointer
|
||||
* @param vec - the vector
|
||||
* @return the metadata pointer of the vector
|
||||
* @internal
|
||||
*/
|
||||
#define cvector_vec_to_base(vec) \
|
||||
(&((cvector_metadata_t *)(void *)(vec))[-1])
|
||||
|
||||
/**
|
||||
* @brief cvector_base_to_vec - For internal use, converts a metadata pointer to a vector pointer
|
||||
* @param ptr - pointer to the metadata
|
||||
* @return the vector
|
||||
* @internal
|
||||
*/
|
||||
#define cvector_base_to_vec(ptr) \
|
||||
((void *)&((cvector_metadata_t *)(ptr))[1])
|
||||
|
||||
/**
|
||||
* @brief cvector_capacity - gets the current capacity of the vector
|
||||
* @param vec - the vector
|
||||
* @return the capacity as a size_t
|
||||
*/
|
||||
#define cvector_capacity(vec) \
|
||||
((vec) ? cvector_vec_to_base(vec)->capacity : (size_t)0)
|
||||
|
||||
/**
|
||||
* @brief cvector_size - gets the current size of the vector
|
||||
* @param vec - the vector
|
||||
* @return the size as a size_t
|
||||
*/
|
||||
#define cvector_size(vec) \
|
||||
((vec) ? cvector_vec_to_base(vec)->size : (size_t)0)
|
||||
|
||||
/**
|
||||
* @brief cvector_elem_destructor - get the element destructor function used
|
||||
* to clean up elements
|
||||
* @param vec - the vector
|
||||
* @return the function pointer as cvector_elem_destructor_t
|
||||
*/
|
||||
#define cvector_elem_destructor(vec) \
|
||||
((vec) ? cvector_vec_to_base(vec)->elem_destructor : NULL)
|
||||
|
||||
/**
|
||||
* @brief cvector_empty - returns non-zero if the vector is empty
|
||||
* @param vec - the vector
|
||||
* @return non-zero if empty, zero if non-empty
|
||||
*/
|
||||
#define cvector_empty(vec) \
|
||||
(cvector_size(vec) == 0)
|
||||
|
||||
/**
|
||||
* @brief cvector_reserve - Requests that the vector capacity be at least enough
|
||||
* to contain n elements. If n is greater than the current vector capacity, the
|
||||
* function causes the container to reallocate its storage increasing its
|
||||
* capacity to n (or greater).
|
||||
* @param vec - the vector
|
||||
* @param n - Minimum capacity for the vector.
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_reserve(vec, n) \
|
||||
do { \
|
||||
size_t cv_reserve_cap__ = cvector_capacity(vec); \
|
||||
if (cv_reserve_cap__ < (n)) { \
|
||||
cvector_grow((vec), (n)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_init - Initialize a vector. The vector must be NULL for this to do anything.
|
||||
* @param vec - the vector
|
||||
* @param capacity - vector capacity to reserve
|
||||
* @param elem_destructor_fn - element destructor function
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_init(vec, capacity, elem_destructor_fn) \
|
||||
do { \
|
||||
if (!(vec)) { \
|
||||
cvector_reserve((vec), capacity); \
|
||||
cvector_set_elem_destructor((vec), (elem_destructor_fn)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_init_default - Initialize a vector with default value. The vector must be NULL for this to do anything. Does NOT work for struct types.
|
||||
* @param vec - the vector
|
||||
* @param capacity - vector capacity to reserve
|
||||
* @param elem_destructor_fn - element destructor function
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_init_default(vec, capacity, default) \
|
||||
do { \
|
||||
if (!(vec)) { \
|
||||
cvector_reserve((vec), capacity); \
|
||||
cvector_set_elem_destructor((vec), (elem_destructor_fn)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_erase - removes the element at index i from the vector
|
||||
* @param vec - the vector
|
||||
* @param i - index of element to remove
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_erase(vec, i) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
const size_t cv_erase_sz__ = cvector_size(vec); \
|
||||
if ((i) < cv_erase_sz__) { \
|
||||
cvector_elem_destructor_t cv_erase_elem_dtor__ = cvector_elem_destructor(vec); \
|
||||
if (cv_erase_elem_dtor__) { \
|
||||
cv_erase_elem_dtor__(&(vec)[i]); \
|
||||
} \
|
||||
cvector_set_size((vec), cv_erase_sz__ - 1); \
|
||||
cvector_clib_memmove( \
|
||||
(vec) + (i), \
|
||||
(vec) + (i) + 1, \
|
||||
sizeof(*(vec)) * (cv_erase_sz__ - 1 - (i))); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_clear - erase all of the elements in the vector
|
||||
* @param vec - the vector
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_clear(vec) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
cvector_elem_destructor_t cv_clear_elem_dtor__ = cvector_elem_destructor(vec); \
|
||||
if (cv_clear_elem_dtor__) { \
|
||||
size_t cv_clear_i__; \
|
||||
for (cv_clear_i__ = 0; cv_clear_i__ < cvector_size(vec); ++cv_clear_i__) { \
|
||||
cv_clear_elem_dtor__(&(vec)[cv_clear_i__]); \
|
||||
} \
|
||||
} \
|
||||
cvector_set_size(vec, 0); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_free - frees all memory associated with the vector
|
||||
* @param vec - the vector
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_free(vec) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
void *cv_free_p__ = cvector_vec_to_base(vec); \
|
||||
cvector_elem_destructor_t cv_free_elem_dtor__ = cvector_elem_destructor(vec); \
|
||||
if (cv_free_elem_dtor__) { \
|
||||
size_t cv_free_i__; \
|
||||
for (cv_free_i__ = 0; cv_free_i__ < cvector_size(vec); ++cv_free_i__) { \
|
||||
cv_free_elem_dtor__(&(vec)[cv_free_i__]); \
|
||||
} \
|
||||
} \
|
||||
cvector_clib_free(cv_free_p__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_begin - returns an iterator to first element of the vector
|
||||
* @param vec - the vector
|
||||
* @return a pointer to the first element (or NULL)
|
||||
*/
|
||||
#define cvector_begin(vec) \
|
||||
(vec)
|
||||
|
||||
/**
|
||||
* @brief cvector_end - returns an iterator to one past the last element of the vector
|
||||
* @param vec - the vector
|
||||
* @return a pointer to one past the last element (or NULL)
|
||||
*/
|
||||
#define cvector_end(vec) \
|
||||
((vec) ? &((vec)[cvector_size(vec)]) : NULL)
|
||||
|
||||
/* user request to use linear growth algorithm */
|
||||
#ifdef CVECTOR_LINEAR_GROWTH
|
||||
|
||||
/**
|
||||
* @brief cvector_compute_next_grow - returns an the computed size in next vector grow
|
||||
* size is increased by 1
|
||||
* @param size - current size
|
||||
* @return size after next vector grow
|
||||
*/
|
||||
#define cvector_compute_next_grow(size) \
|
||||
((size) + 1)
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
* @brief cvector_compute_next_grow - returns an the computed size in next vector grow
|
||||
* size is increased by multiplication of 2
|
||||
* @param size - current size
|
||||
* @return size after next vector grow
|
||||
*/
|
||||
#define cvector_compute_next_grow(size) \
|
||||
((size) ? ((size) << 1) : 1)
|
||||
|
||||
#endif /* CVECTOR_LINEAR_GROWTH */
|
||||
|
||||
/**
|
||||
* @brief cvector_push_back - adds an element to the end of the vector
|
||||
* @param vec - the vector
|
||||
* @param value - the value to add
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_push_back(vec, value) \
|
||||
do { \
|
||||
size_t cv_push_back_cap__ = cvector_capacity(vec); \
|
||||
if (cv_push_back_cap__ <= cvector_size(vec)) { \
|
||||
cvector_grow((vec), cvector_compute_next_grow(cv_push_back_cap__)); \
|
||||
} \
|
||||
(vec)[cvector_size(vec)] = (value); \
|
||||
cvector_set_size((vec), cvector_size(vec) + 1); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_insert - insert element at position pos to the vector
|
||||
* @param vec - the vector
|
||||
* @param pos - position in the vector where the new elements are inserted.
|
||||
* @param val - value to be copied (or moved) to the inserted elements.
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_insert(vec, pos, val) \
|
||||
do { \
|
||||
size_t cv_insert_cap__ = cvector_capacity(vec); \
|
||||
if (cv_insert_cap__ <= cvector_size(vec)) { \
|
||||
cvector_grow((vec), cvector_compute_next_grow(cv_insert_cap__)); \
|
||||
} \
|
||||
if ((pos) < cvector_size(vec)) { \
|
||||
cvector_clib_memmove( \
|
||||
(vec) + (pos) + 1, \
|
||||
(vec) + (pos), \
|
||||
sizeof(*(vec)) * ((cvector_size(vec)) - (pos))); \
|
||||
} \
|
||||
(vec)[(pos)] = (val); \
|
||||
cvector_set_size((vec), cvector_size(vec) + 1); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_pop_back - removes the last element from the vector
|
||||
* @param vec - the vector
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_pop_back(vec) \
|
||||
do { \
|
||||
cvector_elem_destructor_t cv_pop_back_elem_dtor__ = cvector_elem_destructor(vec); \
|
||||
if (cv_pop_back_elem_dtor__) { \
|
||||
cv_pop_back_elem_dtor__(&(vec)[cvector_size(vec) - 1]); \
|
||||
} \
|
||||
cvector_set_size((vec), cvector_size(vec) - 1); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_copy - copy a vector
|
||||
* @param from - the original vector
|
||||
* @param to - destination to which the function copy to
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_copy(from, to) \
|
||||
do { \
|
||||
if ((from)) { \
|
||||
cvector_grow(to, cvector_size(from)); \
|
||||
cvector_set_size(to, cvector_size(from)); \
|
||||
cvector_clib_memcpy((to), (from), cvector_size(from) * sizeof(*(from))); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_swap - exchanges the content of the vector by the content of another vector of the same type
|
||||
* @param vec - the original vector
|
||||
* @param other - the other vector to swap content with
|
||||
* @param type - the type of both vectors
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_swap(vec, other, type) \
|
||||
do { \
|
||||
if (vec && other) { \
|
||||
cvector_vector_type(type) cv_swap__ = vec; \
|
||||
vec = other; \
|
||||
other = cv_swap__; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_set_capacity - For internal use, sets the capacity variable of the vector
|
||||
* @param vec - the vector
|
||||
* @param size - the new capacity to set
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
#define cvector_set_capacity(vec, size) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
cvector_vec_to_base(vec)->capacity = (size); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_set_size - For internal use, sets the size variable of the vector
|
||||
* @param vec - the vector
|
||||
* @param _size - the new capacity to set
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
#define cvector_set_size(vec, _size) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
cvector_vec_to_base(vec)->size = (_size); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_set_elem_destructor - set the element destructor function
|
||||
* used to clean up removed elements. The vector must NOT be NULL for this to do anything.
|
||||
* @param vec - the vector
|
||||
* @param elem_destructor_fn - function pointer of type cvector_elem_destructor_t used to destroy elements
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_set_elem_destructor(vec, elem_destructor_fn) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
cvector_vec_to_base(vec)->elem_destructor = (elem_destructor_fn); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_grow - For internal use, ensures that the vector is at least `count` elements big
|
||||
* @param vec - the vector
|
||||
* @param count - the new capacity to set
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
#define cvector_grow(vec, count) \
|
||||
do { \
|
||||
const size_t cv_grow_sz__ = (count) * sizeof(*(vec)) + sizeof(cvector_metadata_t); \
|
||||
if (vec) { \
|
||||
void *cv_grow_p1__ = cvector_vec_to_base(vec); \
|
||||
void *cv_grow_p2__ = cvector_clib_realloc(cv_grow_p1__, cv_grow_sz__); \
|
||||
cvector_clib_assert(cv_grow_p2__); \
|
||||
(vec) = cvector_base_to_vec(cv_grow_p2__); \
|
||||
} else { \
|
||||
void *cv_grow_p__ = cvector_clib_malloc(cv_grow_sz__); \
|
||||
cvector_clib_assert(cv_grow_p__); \
|
||||
(vec) = cvector_base_to_vec(cv_grow_p__); \
|
||||
cvector_set_size((vec), 0); \
|
||||
cvector_set_elem_destructor((vec), NULL); \
|
||||
} \
|
||||
cvector_set_capacity((vec), (count)); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_shrink_to_fit - requests the container to reduce its capacity to fit its size
|
||||
* @param vec - the vector
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_shrink_to_fit(vec) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
const size_t cv_shrink_to_fit_sz__ = cvector_size(vec); \
|
||||
cvector_grow(vec, cv_shrink_to_fit_sz__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_at - returns a reference to the element at position n in the vector.
|
||||
* @param vec - the vector
|
||||
* @param n - position of an element in the vector.
|
||||
* @return the element at the specified position in the vector.
|
||||
*/
|
||||
#define cvector_at(vec, n) \
|
||||
((vec) ? (((int)(n) < 0 || (size_t)(n) >= cvector_size(vec)) ? NULL : &(vec)[n]) : NULL)
|
||||
|
||||
/**
|
||||
* @brief cvector_front - returns a reference to the first element in the vector. Unlike member cvector_begin, which returns an iterator to this same element, this function returns a direct reference.
|
||||
* @param vec - the vector
|
||||
* @return a reference to the first element in the vector container.
|
||||
*/
|
||||
#define cvector_front(vec) \
|
||||
((vec) ? ((cvector_size(vec) > 0) ? cvector_at(vec, 0) : NULL) : NULL)
|
||||
|
||||
/**
|
||||
* @brief cvector_back - returns a reference to the last element in the vector.Unlike member cvector_end, which returns an iterator just past this element, this function returns a direct reference.
|
||||
* @param vec - the vector
|
||||
* @return a reference to the last element in the vector.
|
||||
*/
|
||||
#define cvector_back(vec) \
|
||||
((vec) ? ((cvector_size(vec) > 0) ? cvector_at(vec, cvector_size(vec) - 1) : NULL) : NULL)
|
||||
|
||||
/**
|
||||
* @brief cvector_resize - resizes the container to contain count elements.
|
||||
* @param vec - the vector
|
||||
* @param count - new size of the vector
|
||||
* @param value - the value to initialize new elements with
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_resize(vec, count, value) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
size_t cv_resize_count__ = (size_t)(count); \
|
||||
size_t cv_resize_sz__ = cvector_vec_to_base(vec)->size; \
|
||||
if (cv_resize_count__ > cv_resize_sz__) { \
|
||||
cvector_reserve((vec), cv_resize_count__); \
|
||||
cvector_set_size((vec), cv_resize_count__); \
|
||||
do { \
|
||||
(vec)[cv_resize_sz__++] = (value); \
|
||||
} while (cv_resize_sz__ < cv_resize_count__); \
|
||||
} else { \
|
||||
while (cv_resize_count__ < cv_resize_sz__--) { \
|
||||
cvector_pop_back(vec); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* CVECTOR_H_ */
|
||||
|
|
@ -1,403 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#define DIRFUNCTIONS_DEFINED
|
||||
#include "config.h"
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "debug.h"
|
||||
#include "dir.h"
|
||||
#include "pathfuncs.h"
|
||||
#include "timefuncs.h"
|
||||
#include "system.h"
|
||||
#include "fs_defines.h"
|
||||
#include "sys_file.h"
|
||||
|
||||
#include <3ds/archive.h>
|
||||
#include <3ds/util/utf.h>
|
||||
|
||||
/* This file is based on firmware/common/dir.c */
|
||||
|
||||
/* Define LOGF_ENABLE to enable logf output in this file */
|
||||
// #define LOGF_ENABLE
|
||||
#include "logf.h"
|
||||
|
||||
/* structure used for open directory streams */
|
||||
static struct dirstr_desc
|
||||
{
|
||||
struct filestr_base stream; /* basic stream info (first!) */
|
||||
struct dirent entry; /* current parsed entry information */
|
||||
} open_streams[MAX_OPEN_DIRS] =
|
||||
{
|
||||
[0 ... MAX_OPEN_FILES-1] = { .stream = { .handle = 0 } }
|
||||
};
|
||||
|
||||
extern FS_Archive sdmcArchive;
|
||||
|
||||
/* check and return a struct dirstr_desc* from a DIR* */
|
||||
static struct dirstr_desc * get_dirstr(DIR *dirp)
|
||||
{
|
||||
struct dirstr_desc *dir = (struct dirstr_desc *)dirp;
|
||||
|
||||
if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
|
||||
dir = NULL;
|
||||
else if (dir->stream.handle != 0)
|
||||
return dir;
|
||||
|
||||
int errnum;
|
||||
|
||||
if (!dir)
|
||||
{
|
||||
errnum = EFAULT;
|
||||
}
|
||||
else
|
||||
{
|
||||
logf("dir #%d: dir not open\n", (int)(dir - open_streams));
|
||||
errnum = EBADF;
|
||||
}
|
||||
|
||||
errno = errnum;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define GET_DIRSTR(type, dirp) \
|
||||
({ \
|
||||
file_internal_lock_##type(); \
|
||||
struct dirstr_desc *_dir = get_dirstr(dirp); \
|
||||
if (_dir) \
|
||||
FILESTR_LOCK(type, &_dir->stream); \
|
||||
else { \
|
||||
file_internal_unlock_##type(); \
|
||||
} \
|
||||
_dir; \
|
||||
})
|
||||
|
||||
/* release the lock on the dirstr_desc* */
|
||||
#define RELEASE_DIRSTR(type, dir) \
|
||||
({ \
|
||||
FILESTR_UNLOCK(type, &(dir)->stream); \
|
||||
file_internal_unlock_##type(); \
|
||||
})
|
||||
|
||||
|
||||
/* find a free dir stream descriptor */
|
||||
static struct dirstr_desc * alloc_dirstr(void)
|
||||
{
|
||||
for (unsigned int dd = 0; dd < MAX_OPEN_DIRS; dd++)
|
||||
{
|
||||
struct dirstr_desc *dir = &open_streams[dd];
|
||||
if (dir->stream.handle == 0)
|
||||
return dir;
|
||||
}
|
||||
|
||||
logf("Too many dirs open\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u32 fs_error(void) {
|
||||
u32 err;
|
||||
FSUSER_GetSdmcFatfsError(&err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Initialize the base descriptor */
|
||||
static void filestr_base_init(struct filestr_base *stream)
|
||||
{
|
||||
stream->cache = nil;
|
||||
stream->handle = 0;
|
||||
stream->size = 0;
|
||||
LightLock_Init(&stream->mtx);
|
||||
}
|
||||
|
||||
/** POSIX interface **/
|
||||
|
||||
/* open a directory */
|
||||
DIR * ctru_opendir(const char *dirname)
|
||||
{
|
||||
logf("opendir(dirname=\"%s\")\n", dirname);
|
||||
|
||||
DIR *dirp = NULL;
|
||||
file_internal_lock_WRITER();
|
||||
|
||||
int rc;
|
||||
|
||||
struct dirstr_desc * const dir = alloc_dirstr();
|
||||
if (!dir)
|
||||
FILE_ERROR(EMFILE, _RC);
|
||||
|
||||
filestr_base_init(&dir->stream);
|
||||
Result res = FSUSER_OpenDirectory(&dir->stream.handle,
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, dirname));
|
||||
if (R_FAILED(res)) {
|
||||
logf("Open failed: %lld\n", fs_error());
|
||||
FILE_ERROR(EMFILE, -1);
|
||||
}
|
||||
|
||||
dir->stream.size = 0;
|
||||
dir->stream.flags = 0;
|
||||
|
||||
/* we will use file path to implement ctru_samedir function */
|
||||
strcpy(dir->stream.path, dirname);
|
||||
|
||||
dirp = (DIR *)dir;
|
||||
file_error:
|
||||
file_internal_unlock_WRITER();
|
||||
return dirp;
|
||||
}
|
||||
|
||||
/* close a directory stream */
|
||||
int ctru_closedir(DIR *dirp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
file_internal_lock_WRITER();
|
||||
|
||||
/* needs to work even if marked "nonexistant" */
|
||||
struct dirstr_desc * const dir = (struct dirstr_desc *)dirp;
|
||||
if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
|
||||
FILE_ERROR(EFAULT, -1);
|
||||
|
||||
logf("closedir(dirname=\"%s\")\n", dir->stream.path);
|
||||
|
||||
if (dir->stream.handle == 0)
|
||||
{
|
||||
logf("dir #%d: dir not open\n", (int)(dir - open_streams));
|
||||
FILE_ERROR(EBADF, -2);
|
||||
}
|
||||
|
||||
Result res = FSDIR_Close(dir->stream.handle);
|
||||
if (R_FAILED(res))
|
||||
FILE_ERROR(ERRNO, -3);
|
||||
|
||||
dir->stream.handle = 0;
|
||||
dir->stream.path[0] = '\0';
|
||||
|
||||
rc = 0;
|
||||
file_error:
|
||||
file_internal_unlock_WRITER();
|
||||
return rc;
|
||||
}
|
||||
|
||||
void dirstr_entry_init(FS_DirectoryEntry *dirEntry, struct dirent *entry)
|
||||
{
|
||||
/* clear */
|
||||
memset(entry, 0, sizeof(struct dirent));
|
||||
|
||||
/* attributes */
|
||||
if (dirEntry->attributes & FS_ATTRIBUTE_DIRECTORY)
|
||||
entry->info.attr |= ATTR_DIRECTORY;
|
||||
if (dirEntry->attributes & FS_ATTRIBUTE_HIDDEN)
|
||||
entry->info.attr |= ATTR_HIDDEN;
|
||||
if (dirEntry->attributes & FS_ATTRIBUTE_ARCHIVE)
|
||||
entry->info.attr |= ATTR_ARCHIVE;
|
||||
if (dirEntry->attributes & FS_ATTRIBUTE_READ_ONLY)
|
||||
entry->info.attr |= ATTR_READ_ONLY;
|
||||
|
||||
/* size */
|
||||
entry->info.size = dirEntry->fileSize;
|
||||
|
||||
/* name */
|
||||
uint8_t d_name[0xA0 + 1];
|
||||
memset(d_name, '\0', 0xA0);
|
||||
utf16_to_utf8(d_name, (uint16_t *) &dirEntry->name, 0xA0);
|
||||
memcpy(entry->d_name, d_name, 0xA0);
|
||||
}
|
||||
|
||||
/* read a directory */
|
||||
struct dirent * ctru_readdir(DIR *dirp)
|
||||
{
|
||||
struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp);
|
||||
if (!dir)
|
||||
FILE_ERROR_RETURN(ERRNO, NULL);
|
||||
|
||||
int rc;
|
||||
struct dirent *res = NULL;
|
||||
|
||||
logf("readdir(dirname=\"%s\")\n", dir->stream.path);
|
||||
|
||||
u32 dataRead = 0;
|
||||
FS_DirectoryEntry dirEntry;
|
||||
Result result = FSDIR_Read(dir->stream.handle,
|
||||
&dataRead,
|
||||
1,
|
||||
&dirEntry);
|
||||
if (R_FAILED(result))
|
||||
FILE_ERROR(EIO, _RC);
|
||||
|
||||
if (dataRead == 0) {
|
||||
/* directory end. return NULL value, no errno */
|
||||
res = NULL;
|
||||
goto file_error;
|
||||
}
|
||||
|
||||
res = &dir->entry;
|
||||
dirstr_entry_init(&dirEntry, res);
|
||||
|
||||
/* time */
|
||||
char full_path[MAX_PATH+1];
|
||||
|
||||
if (!strcmp(PATH_ROOTSTR, dir->stream.path))
|
||||
snprintf(full_path, MAX_PATH, "%s%s", dir->stream.path, res->d_name);
|
||||
else
|
||||
snprintf(full_path, MAX_PATH, "%s/%s", dir->stream.path, res->d_name);
|
||||
|
||||
u64 mtime;
|
||||
archive_getmtime(full_path, &mtime);
|
||||
|
||||
/* DEBUGF("archive_getmtime(%s): %lld\n", full_path, mtime); */
|
||||
|
||||
uint16_t dosdate, dostime;
|
||||
dostime_localtime(mtime, &dosdate, &dostime);
|
||||
res->info.wrtdate = dosdate;
|
||||
res->info.wrttime = dostime;
|
||||
|
||||
file_error:
|
||||
RELEASE_DIRSTR(READER, dir);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* make a directory */
|
||||
int ctru_mkdir(const char *path)
|
||||
{
|
||||
logf("mkdir(path=\"%s\")\n", path);
|
||||
|
||||
int rc;
|
||||
|
||||
file_internal_lock_WRITER();
|
||||
|
||||
Result res = FSUSER_CreateDirectory(sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, path),
|
||||
0);
|
||||
if (R_FAILED(res))
|
||||
FILE_ERROR(ERRNO, -1);
|
||||
|
||||
rc = 0;
|
||||
file_error:
|
||||
file_internal_unlock_WRITER();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* remove a directory */
|
||||
int ctru_rmdir(const char *name)
|
||||
{
|
||||
logf("rmdir(name=\"%s\")\n", name);
|
||||
|
||||
int rc;
|
||||
|
||||
if (name)
|
||||
{
|
||||
/* path may not end with "." */
|
||||
const char *basename;
|
||||
size_t len = path_basename(name, &basename);
|
||||
if (basename[0] == '.' && len == 1)
|
||||
{
|
||||
logf("Invalid path; last component is \".\"\n");
|
||||
FILE_ERROR_RETURN(EINVAL, -9);
|
||||
}
|
||||
}
|
||||
|
||||
file_internal_lock_WRITER();
|
||||
Result res = FSUSER_DeleteDirectory(sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, name));
|
||||
if (R_FAILED(res))
|
||||
FILE_ERROR(ERRNO, -1);
|
||||
|
||||
rc = 0;
|
||||
file_error:
|
||||
file_internal_unlock_WRITER();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/** Extended interface **/
|
||||
|
||||
/* return if two directory streams refer to the same directory */
|
||||
int ctru_samedir(DIR *dirp1, DIR *dirp2)
|
||||
{
|
||||
struct dirstr_desc * const dir1 = GET_DIRSTR(WRITER, dirp1);
|
||||
if (!dir1)
|
||||
FILE_ERROR_RETURN(ERRNO, -1);
|
||||
|
||||
int rc = -2;
|
||||
|
||||
struct dirstr_desc * const dir2 = get_dirstr(dirp2);
|
||||
if (dir2) {
|
||||
rc = strcmp(dir1->stream.path, dir2->stream.path) == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
RELEASE_DIRSTR(WRITER, dir1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* test directory existence (returns 'false' if a file) */
|
||||
bool ctru_dir_exists(const char *dirname)
|
||||
{
|
||||
file_internal_lock_WRITER();
|
||||
|
||||
int rc;
|
||||
|
||||
Handle handle;
|
||||
Result res = FSUSER_OpenDirectory(&handle,
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, dirname));
|
||||
if (R_FAILED(res)) {
|
||||
logf("Directory not found: %ld\n", fs_error());
|
||||
FILE_ERROR(EMFILE, -1);
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
file_error:
|
||||
if (rc == 0) {
|
||||
FSDIR_Close(handle);
|
||||
}
|
||||
file_internal_unlock_WRITER();
|
||||
return rc == 0 ? true : false;
|
||||
}
|
||||
|
||||
/* get the portable info from the native entry */
|
||||
struct dirinfo dir_get_info(DIR *dirp, struct dirent *entry)
|
||||
{
|
||||
int rc;
|
||||
if (!dirp || !entry)
|
||||
FILE_ERROR(EFAULT, _RC);
|
||||
|
||||
if (entry->d_name[0] == '\0')
|
||||
FILE_ERROR(ENOENT, _RC);
|
||||
|
||||
if ((file_size_t)entry->info.size > FILE_SIZE_MAX)
|
||||
FILE_ERROR(EOVERFLOW, _RC);
|
||||
|
||||
return (struct dirinfo)
|
||||
{
|
||||
.attribute = entry->info.attr,
|
||||
.size = entry->info.size,
|
||||
.mtime = dostime_mktime(entry->info.wrtdate, entry->info.wrttime),
|
||||
};
|
||||
|
||||
file_error:
|
||||
return (struct dirinfo){ .attribute = 0 };
|
||||
}
|
||||
|
||||
const char* ctru_root_realpath(void)
|
||||
{
|
||||
/* Native only, for APP and SIM see respective filesystem-.c files */
|
||||
return PATH_ROOTSTR; /* rb_namespace.c */
|
||||
}
|
||||
|
|
@ -1,777 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#define RB_FILESYSTEM_OS
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
#include "file.h"
|
||||
#include "debug.h"
|
||||
#include "string-extra.h"
|
||||
#include "fs_defines.h"
|
||||
#include "sys_file.h"
|
||||
|
||||
/* This file is based on firmware/common/file.c */
|
||||
|
||||
/* Define LOGF_ENABLE to enable logf output in this file */
|
||||
// #define LOGF_ENABLE
|
||||
#include "logf.h"
|
||||
|
||||
/**
|
||||
* These functions provide a roughly POSIX-compatible file I/O API.
|
||||
* Important: the bufferio I/O library (bfile) used in the 3ds does not work
|
||||
* with write-only files due to an internal limitation.
|
||||
* So all files will be opened with the read flag by default.
|
||||
*/
|
||||
|
||||
/* structure used for open file descriptors */
|
||||
static struct filestr_desc
|
||||
{
|
||||
struct filestr_base stream; /* basic stream info (first!) */
|
||||
file_size_t offset; /* current offset for stream */
|
||||
u64 *sizep; /* shortcut to file size in fileobj */
|
||||
} open_streams[MAX_OPEN_FILES] =
|
||||
{
|
||||
[0 ... MAX_OPEN_FILES-1] = { .stream = { .cache = nil, .flags = 0 } }
|
||||
};
|
||||
|
||||
extern FS_Archive sdmcArchive;
|
||||
|
||||
/* check and return a struct filestr_desc* from a file descriptor number */
|
||||
static struct filestr_desc * get_filestr(int fildes)
|
||||
{
|
||||
struct filestr_desc *file = &open_streams[fildes];
|
||||
|
||||
if ((unsigned int)fildes >= MAX_OPEN_FILES)
|
||||
file = NULL;
|
||||
else if (file->stream.cache != nil)
|
||||
return file;
|
||||
|
||||
logf("fildes %d: bad file number\n", fildes);
|
||||
errno = (file && (file->stream.cache == nil)) ? ENXIO : EBADF;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define GET_FILESTR(type, fildes) \
|
||||
({ \
|
||||
file_internal_lock_##type(); \
|
||||
struct filestr_desc * _file = get_filestr(fildes); \
|
||||
if (_file) \
|
||||
FILESTR_LOCK(type, &_file->stream); \
|
||||
else { \
|
||||
file_internal_unlock_##type(); \
|
||||
}\
|
||||
_file; \
|
||||
})
|
||||
|
||||
/* release the lock on the filestr_desc* */
|
||||
#define RELEASE_FILESTR(type, file) \
|
||||
({ \
|
||||
FILESTR_UNLOCK(type, &(file)->stream); \
|
||||
file_internal_unlock_##type(); \
|
||||
})
|
||||
|
||||
/* find a free file descriptor */
|
||||
static int alloc_filestr(struct filestr_desc **filep)
|
||||
{
|
||||
for (int fildes = 0; fildes < MAX_OPEN_FILES; fildes++)
|
||||
{
|
||||
struct filestr_desc *file = &open_streams[fildes];
|
||||
if (file->stream.cache == nil)
|
||||
{
|
||||
*filep = file;
|
||||
return fildes;
|
||||
}
|
||||
}
|
||||
|
||||
logf("Too many files open\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check for file existence */
|
||||
int test_stream_exists_internal(const char *path)
|
||||
{
|
||||
int rc;
|
||||
bool is_dir = false;
|
||||
|
||||
Handle handle;
|
||||
Result res = FSUSER_OpenFile(&handle,
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, path),
|
||||
FS_OPEN_READ,
|
||||
0);
|
||||
if (R_FAILED(res)) {
|
||||
/* not a file, try to open a directory */
|
||||
res = FSUSER_OpenDirectory(&handle,
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, path));
|
||||
if (R_FAILED(res)) {
|
||||
logf("File does not exist\n");
|
||||
FILE_ERROR(ERRNO, -1);
|
||||
}
|
||||
|
||||
is_dir = true;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
file_error:
|
||||
if (handle > 0) {
|
||||
if (is_dir)
|
||||
FSDIR_Close(handle);
|
||||
else
|
||||
FSFILE_Close(handle);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* set the file pointer */
|
||||
static off_t lseek_internal(struct filestr_desc *file, off_t offset,
|
||||
int whence)
|
||||
{
|
||||
off_t rc;
|
||||
off_t pos;
|
||||
|
||||
off_t size = MIN(*file->sizep, FILE_SIZE_MAX);
|
||||
off_t file_offset = AtomicLoad(&file->offset);
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
if (offset < 0 || (off_t)offset > size)
|
||||
FILE_ERROR(EINVAL, -1);
|
||||
|
||||
pos = offset;
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
if ((offset < 0 && (off_t)-offset > file_offset) ||
|
||||
(offset > 0 && (off_t)offset > size - file_offset))
|
||||
FILE_ERROR(EINVAL, -1);
|
||||
|
||||
pos = file_offset + offset;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
if (offset > 0 || (off_t)-offset > size)
|
||||
FILE_ERROR(EINVAL, -1);
|
||||
|
||||
pos = size + offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
FILE_ERROR(EINVAL, -1);
|
||||
}
|
||||
|
||||
AtomicSwap(&file->offset, pos);
|
||||
|
||||
return pos;
|
||||
file_error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* read from or write to the file; back end to read() and write() */
|
||||
static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte,
|
||||
bool write)
|
||||
{
|
||||
#ifndef LOGF_ENABLE /* wipes out log before you can save it */
|
||||
/* DEBUGF("readwrite(%p,%lx,%lu,%s)\n",
|
||||
file, (long)buf, (unsigned long)nbyte, write ? "write" : "read"); */
|
||||
#endif
|
||||
|
||||
const file_size_t size = *file->sizep;
|
||||
size_t filerem;
|
||||
|
||||
if (write)
|
||||
{
|
||||
/* if opened in append mode, move pointer to end */
|
||||
if (file->stream.flags & O_APPEND)
|
||||
AtomicSwap(&file->offset, MIN(size, FILE_SIZE_MAX));
|
||||
|
||||
filerem = FILE_SIZE_MAX - AtomicLoad(&file->offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* limit to maximum possible offset (EOF or FILE_SIZE_MAX) */
|
||||
filerem = MIN(size, FILE_SIZE_MAX) - AtomicLoad(&file->offset);
|
||||
}
|
||||
|
||||
if (nbyte > filerem)
|
||||
{
|
||||
nbyte = filerem;
|
||||
if (nbyte > 0)
|
||||
{}
|
||||
else if (write)
|
||||
FILE_ERROR_RETURN(EFBIG, -1); /* would get too large */
|
||||
else if (AtomicLoad(&file->offset) >= FILE_SIZE_MAX)
|
||||
FILE_ERROR_RETURN(EOVERFLOW, -2); /* can't read here */
|
||||
}
|
||||
|
||||
if (nbyte == 0)
|
||||
return 0;
|
||||
|
||||
int rc = 0;
|
||||
int_error_t n_err;
|
||||
|
||||
if (write)
|
||||
n_err = PagerWriteAt(file->stream.cache, (u8 *) buf, nbyte, AtomicLoad(&file->offset));
|
||||
else
|
||||
n_err = PagerReadAt(file->stream.cache, (u8 *) buf, nbyte, AtomicLoad(&file->offset));
|
||||
|
||||
if ((n_err.err != nil) && strcmp(n_err.err, "io.EOF")) {
|
||||
FILE_ERROR(ERRNO, -3);
|
||||
}
|
||||
|
||||
file_error:;
|
||||
#ifdef DEBUG
|
||||
if (errno == ENOSPC)
|
||||
logf("No space left on device\n");
|
||||
#endif
|
||||
|
||||
size_t done = n_err.n;
|
||||
if (done)
|
||||
{
|
||||
/* error or not, update the file offset and size if anything was
|
||||
transferred */
|
||||
AtomicAdd(&file->offset, done);
|
||||
#ifndef LOGF_ENABLE /* wipes out log before you can save it */
|
||||
/* DEBUGF("file offset: %lld\n", file->offset); */
|
||||
#endif
|
||||
/* adjust file size to length written */
|
||||
if (write && AtomicLoad(&file->offset) > size)
|
||||
*file->sizep = AtomicLoad(&file->offset);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* initialize the base descriptor */
|
||||
static void filestr_base_init(struct filestr_base *stream)
|
||||
{
|
||||
stream->cache = nil;
|
||||
stream->handle = 0;
|
||||
stream->size = 0;
|
||||
LightLock_Init(&stream->mtx);
|
||||
}
|
||||
|
||||
int open_internal_inner2(Handle *handle, const char *path, u32 openFlags, u32 attributes)
|
||||
{
|
||||
int rc;
|
||||
Result res = FSUSER_OpenFile(handle,
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, path),
|
||||
openFlags,
|
||||
attributes);
|
||||
if (R_FAILED(res)) {
|
||||
FILE_ERROR(ERRNO, -1);
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
file_error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int open_internal_inner1(const char *path, int oflag)
|
||||
{
|
||||
int rc;
|
||||
struct filestr_desc *file;
|
||||
int fildes = alloc_filestr(&file);
|
||||
if (fildes < 0)
|
||||
FILE_ERROR_RETURN(EMFILE, -1);
|
||||
|
||||
u32 openFlags = 0, attributes = 0;
|
||||
|
||||
/* open for reading by default */
|
||||
openFlags = FS_OPEN_READ;
|
||||
|
||||
if (oflag & O_ACCMODE)
|
||||
{
|
||||
if ((oflag & O_ACCMODE) == O_RDONLY) {
|
||||
attributes |= FS_ATTRIBUTE_READ_ONLY;
|
||||
}
|
||||
if ((oflag & O_ACCMODE) == O_WRONLY) {
|
||||
openFlags |= FS_OPEN_WRITE;
|
||||
}
|
||||
if ((oflag & O_ACCMODE) == O_RDWR) {
|
||||
openFlags |= FS_OPEN_WRITE;
|
||||
}
|
||||
}
|
||||
else if (oflag & O_TRUNC)
|
||||
{
|
||||
/* O_TRUNC requires write mode */
|
||||
logf("No write mode but have O_TRUNC\n");
|
||||
FILE_ERROR(EINVAL, -2);
|
||||
}
|
||||
|
||||
/* O_CREAT and O_APPEND are fine without write mode
|
||||
* for the former, an empty file is created but no data may be written
|
||||
* for the latter, no append will be allowed anyway */
|
||||
if (!(oflag & O_CREAT))
|
||||
oflag &= ~O_EXCL; /* result is undefined: we choose "ignore" */
|
||||
|
||||
filestr_base_init(&file->stream);
|
||||
rc = open_internal_inner2(&file->stream.handle, path, openFlags, attributes);
|
||||
|
||||
if (rc > 0) {
|
||||
if (oflag & O_EXCL)
|
||||
{
|
||||
logf("File exists\n");
|
||||
FILE_ERROR(EEXIST, -4);
|
||||
}
|
||||
}
|
||||
else if (oflag & O_CREAT)
|
||||
{
|
||||
/* not found; try to create it */
|
||||
openFlags |= FS_OPEN_CREATE;
|
||||
rc = open_internal_inner2(&file->stream.handle, path, openFlags, attributes);
|
||||
if (rc < 0)
|
||||
FILE_ERROR(ERRNO, rc * 10 - 6);
|
||||
}
|
||||
else
|
||||
{
|
||||
logf("File not found\n");
|
||||
FILE_ERROR(ENOENT, -5);
|
||||
}
|
||||
|
||||
/* truncate file if requested */
|
||||
if (oflag & O_TRUNC) {
|
||||
Result res = FSFILE_SetSize(file->stream.handle, 0);
|
||||
if (R_FAILED(res)) {
|
||||
FILE_ERROR(ERRNO, -6);
|
||||
}
|
||||
}
|
||||
|
||||
/* we need to set file size here, or else lseek
|
||||
will fail if no read or write has been done */
|
||||
u64 size = 0;
|
||||
Result res = FSFILE_GetSize(file->stream.handle, &size);
|
||||
if (R_FAILED(res)) {
|
||||
FILE_ERROR(ERRNO, -8);
|
||||
}
|
||||
|
||||
int pageSize = 4096; /* 4096 bytes */
|
||||
int bufferSize = 512 * 1024; /* 512 kB */
|
||||
|
||||
/* streamed file formats like flac and mp3 need very large page
|
||||
sizes to avoid stuttering */
|
||||
if (((oflag & O_ACCMODE) == O_RDONLY) && (size > 0x200000)) {
|
||||
/* printf("open(%s)_BIG_pageSize\n", path); */
|
||||
pageSize = 32 * 1024;
|
||||
bufferSize = MIN(size, defaultBufferSize);
|
||||
}
|
||||
|
||||
file->stream.cache = NewPagerSize(file->stream.handle,
|
||||
pageSize,
|
||||
bufferSize);
|
||||
if (file->stream.cache == nil) {
|
||||
FILE_ERROR(ERRNO, -7);
|
||||
}
|
||||
|
||||
file->stream.flags = oflag;
|
||||
file->stream.size = size;
|
||||
file->sizep = &file->stream.size;
|
||||
AtomicSwap(&file->offset, 0);
|
||||
|
||||
/* we will use file path to implement ctru_fsamefile function */
|
||||
strcpy(file->stream.path, path);
|
||||
|
||||
return fildes;
|
||||
|
||||
file_error:
|
||||
if (fildes >= 0) {
|
||||
if (file->stream.cache != nil) {
|
||||
PagerFlush(file->stream.cache);
|
||||
PagerClear(file->stream.cache);
|
||||
file->stream.cache = nil;
|
||||
}
|
||||
|
||||
FSFILE_Close(file->stream.handle);
|
||||
file->stream.handle = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int open_internal_locked(const char *path, int oflag)
|
||||
{
|
||||
file_internal_lock_WRITER();
|
||||
int rc = open_internal_inner1(path, oflag);
|
||||
file_internal_unlock_WRITER();
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ctru_open(const char *path, int oflag, ...)
|
||||
{
|
||||
logf("open(path=\"%s\",oflag=%X)\n", path, (unsigned)oflag);
|
||||
return open_internal_locked(path, oflag);
|
||||
}
|
||||
|
||||
int ctru_creat(const char *path, mode_t mode)
|
||||
{
|
||||
logf("creat(path=\"%s\")\n", path);
|
||||
return ctru_open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
|
||||
}
|
||||
|
||||
int ctru_close(int fildes)
|
||||
{
|
||||
logf("close(fd=%d)\n", fildes);
|
||||
|
||||
int rc;
|
||||
|
||||
file_internal_lock_WRITER();
|
||||
|
||||
/* needs to work even if marked "nonexistant" */
|
||||
struct filestr_desc *file = &open_streams[fildes];
|
||||
if ((unsigned int)fildes >= MAX_OPEN_FILES || (file->stream.cache == nil))
|
||||
{
|
||||
logf("filedes %d not open\n", fildes);
|
||||
FILE_ERROR(EBADF, -2);
|
||||
}
|
||||
|
||||
if (file->stream.cache != nil) {
|
||||
PagerFlush(file->stream.cache);
|
||||
PagerClear(file->stream.cache);
|
||||
file->stream.cache = nil;
|
||||
}
|
||||
|
||||
FSFILE_Close(file->stream.handle);
|
||||
file->stream.handle = 0;
|
||||
file->stream.path[0] = '\0';
|
||||
|
||||
rc = 0;
|
||||
file_error:
|
||||
file_internal_unlock_WRITER();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* truncate a file to a specified length */
|
||||
int ctru_ftruncate(int fildes, off_t length)
|
||||
{
|
||||
logf("ftruncate(fd=%d,len=%ld)\n", fildes, (long)length);
|
||||
|
||||
struct filestr_desc * const file = GET_FILESTR(READER, fildes);
|
||||
if (!file)
|
||||
FILE_ERROR_RETURN(ERRNO, -1);
|
||||
|
||||
int rc;
|
||||
|
||||
if (file->stream.flags & O_RDONLY)
|
||||
{
|
||||
logf("Descriptor is read-only mode\n");
|
||||
FILE_ERROR(EBADF, -2);
|
||||
}
|
||||
|
||||
if (length < 0)
|
||||
{
|
||||
logf("Length %ld is invalid\n", (long)length);
|
||||
FILE_ERROR(EINVAL, -3);
|
||||
}
|
||||
|
||||
file_error_t err = PagerTruncate(file->stream.cache, length);
|
||||
if (err) {
|
||||
FILE_ERROR(ERRNO, -11);
|
||||
}
|
||||
|
||||
*file->sizep = length;
|
||||
|
||||
rc = 0;
|
||||
file_error:
|
||||
RELEASE_FILESTR(READER, file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* synchronize changes to a file */
|
||||
int ctru_fsync(int fildes)
|
||||
{
|
||||
logf("fsync(fd=%d)\n", fildes);
|
||||
|
||||
struct filestr_desc * const file = GET_FILESTR(WRITER, fildes);
|
||||
if (!file)
|
||||
FILE_ERROR_RETURN(ERRNO, -1);
|
||||
|
||||
int rc;
|
||||
|
||||
if (file->stream.flags & O_RDONLY)
|
||||
{
|
||||
logf("Descriptor is read-only mode\n");
|
||||
FILE_ERROR(EINVAL, -2);
|
||||
}
|
||||
|
||||
/* flush all pending changes to disk */
|
||||
file_error_t err = PagerFlush(file->stream.cache);
|
||||
if (err != nil) {
|
||||
FILE_ERROR(ERRNO, -3);
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
file_error:
|
||||
RELEASE_FILESTR(WRITER, file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* move the read/write file offset */
|
||||
off_t ctru_lseek(int fildes, off_t offset, int whence)
|
||||
{
|
||||
#ifndef LOGF_ENABLE /* wipes out log before you can save it */
|
||||
/* DEBUGF("lseek(fd=%d,ofs=%ld,wh=%d)\n", fildes, (long)offset, whence); */
|
||||
#endif
|
||||
struct filestr_desc * const file = GET_FILESTR(READER, fildes);
|
||||
if (!file)
|
||||
FILE_ERROR_RETURN(ERRNO, -1);
|
||||
|
||||
off_t rc = lseek_internal(file, offset, whence);
|
||||
if (rc < 0)
|
||||
FILE_ERROR(ERRNO, rc * 10 - 2);
|
||||
|
||||
file_error:
|
||||
RELEASE_FILESTR(READER, file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* read from a file */
|
||||
ssize_t ctru_read(int fildes, void *buf, size_t nbyte)
|
||||
{
|
||||
struct filestr_desc * const file = GET_FILESTR(READER, fildes);
|
||||
if (!file)
|
||||
FILE_ERROR_RETURN(ERRNO, -1);
|
||||
|
||||
ssize_t rc;
|
||||
|
||||
if (file->stream.flags & O_WRONLY)
|
||||
{
|
||||
logf("read(fd=%d,buf=%p,nb=%lu) - "
|
||||
"descriptor is write-only mode\n",
|
||||
fildes, buf, (unsigned long)nbyte);
|
||||
FILE_ERROR(EBADF, -2);
|
||||
}
|
||||
|
||||
rc = readwrite(file, buf, nbyte, false);
|
||||
if (rc < 0)
|
||||
FILE_ERROR(ERRNO, rc * 10 - 3);
|
||||
|
||||
file_error:
|
||||
RELEASE_FILESTR(READER, file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* write on a file */
|
||||
ssize_t ctru_write(int fildes, const void *buf, size_t nbyte)
|
||||
{
|
||||
struct filestr_desc * const file = GET_FILESTR(READER, fildes);
|
||||
if (!file)
|
||||
FILE_ERROR_RETURN(ERRNO, -1);
|
||||
|
||||
ssize_t rc;
|
||||
|
||||
if (file->stream.flags & O_RDONLY)
|
||||
{
|
||||
logf("write(fd=%d,buf=%p,nb=%lu) - "
|
||||
"descriptor is read-only mode\n",
|
||||
fildes, buf, (unsigned long)nbyte);
|
||||
FILE_ERROR(EBADF, -2);
|
||||
}
|
||||
|
||||
rc = readwrite(file, (void *)buf, nbyte, true);
|
||||
if (rc < 0)
|
||||
FILE_ERROR(ERRNO, rc * 10 - 3);
|
||||
|
||||
file_error:
|
||||
RELEASE_FILESTR(READER, file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* remove a file */
|
||||
int ctru_remove(const char *path)
|
||||
{
|
||||
logf("remove(path=\"%s\")\n", path);
|
||||
|
||||
int rc;
|
||||
|
||||
file_internal_lock_WRITER();
|
||||
Result res = FSUSER_DeleteFile(sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, path));
|
||||
if (R_FAILED(res))
|
||||
FILE_ERROR(ERRNO, -1);
|
||||
|
||||
rc = 0;
|
||||
file_error:
|
||||
file_internal_unlock_WRITER();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* rename a file */
|
||||
int ctru_rename(const char *old, const char *new)
|
||||
{
|
||||
/* note: move by rename does not work in devkitARM toolchain */
|
||||
logf("rename(old=\"%s\",new=\"%s\")\n", old, new);
|
||||
|
||||
int rc;
|
||||
|
||||
/* if 'old' is a directory then 'new' is also required to be one if 'new'
|
||||
is to be overwritten */
|
||||
bool are_dirs = false;
|
||||
|
||||
file_internal_lock_WRITER();
|
||||
|
||||
if (!strcmp(new, old)) /* case-only is ok */
|
||||
{
|
||||
logf("No name change (success)\n");
|
||||
rc = 0;
|
||||
FILE_ERROR(ERRNO, _RC);
|
||||
}
|
||||
|
||||
/* open 'old'; it must exist */
|
||||
Handle open1rc;
|
||||
Result res = FSUSER_OpenFile(&open1rc,
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, old),
|
||||
FS_OPEN_READ,
|
||||
0);
|
||||
if (R_FAILED(res)) {
|
||||
/* not a file, try to open a directory */
|
||||
res = FSUSER_OpenDirectory(&open1rc,
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, old));
|
||||
if (R_FAILED(res)) {
|
||||
logf("Failed opening old\n");
|
||||
FILE_ERROR(ERRNO, -1);
|
||||
}
|
||||
|
||||
are_dirs = true;
|
||||
}
|
||||
|
||||
if (are_dirs) {
|
||||
/* rename directory */
|
||||
FSUSER_RenameDirectory(sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, old),
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, new));
|
||||
}
|
||||
else {
|
||||
/* rename file */
|
||||
FSUSER_RenameFile(sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, old),
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, new));
|
||||
}
|
||||
|
||||
if (R_FAILED(res)) {
|
||||
logf("Rename failed\n");
|
||||
FILE_ERROR(ERRNO, -2);
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
file_error:
|
||||
/* for now, there is nothing to fail upon closing the old stream */
|
||||
if (open1rc > 0) {
|
||||
if (are_dirs)
|
||||
FSDIR_Close(open1rc);
|
||||
else
|
||||
FSFILE_Close(open1rc);
|
||||
}
|
||||
|
||||
file_internal_unlock_WRITER();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** Extensions **/
|
||||
|
||||
/* todo: utime does not work in devkitARM toolchain */
|
||||
int ctru_modtime(const char *path, time_t modtime)
|
||||
{
|
||||
struct utimbuf times =
|
||||
{
|
||||
.actime = modtime,
|
||||
.modtime = modtime,
|
||||
};
|
||||
|
||||
return utime(path, ×);
|
||||
}
|
||||
|
||||
/* get the binary size of a file (in bytes) */
|
||||
off_t ctru_filesize(int fildes)
|
||||
{
|
||||
struct filestr_desc * const file = GET_FILESTR(READER, fildes);
|
||||
if (!file)
|
||||
FILE_ERROR_RETURN(ERRNO, -1);
|
||||
|
||||
off_t rc;
|
||||
file_size_t size = *file->sizep;
|
||||
|
||||
if (size > FILE_SIZE_MAX)
|
||||
FILE_ERROR(EOVERFLOW, -2);
|
||||
|
||||
rc = (off_t)size;
|
||||
file_error:
|
||||
RELEASE_FILESTR(READER, file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* test if two file descriptors refer to the same file */
|
||||
int ctru_fsamefile(int fildes1, int fildes2)
|
||||
{
|
||||
struct filestr_desc * const file1 = GET_FILESTR(WRITER, fildes1);
|
||||
if (!file1)
|
||||
FILE_ERROR_RETURN(ERRNO, -1);
|
||||
|
||||
int rc = -2;
|
||||
|
||||
struct filestr_desc * const file2 = get_filestr(fildes2);
|
||||
if (file2)
|
||||
rc = strcmp(file1->stream.path, file2->stream.path) == 0 ? 1 : 0;
|
||||
|
||||
RELEASE_FILESTR(WRITER, file1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* tell the relationship of path1 to path2 */
|
||||
int ctru_relate(const char *path1, const char *path2)
|
||||
{
|
||||
/* FAT32 file system does not support symbolic links,
|
||||
therefore, comparing the two full paths should be enough
|
||||
to tell relationship */
|
||||
logf("relate(path1=\"%s\",path2=\"%s\")\n", path1, path2);
|
||||
int rc = RELATE_DIFFERENT;
|
||||
if (strcmp(path1, path2) == 0)
|
||||
rc = RELATE_SAME;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* test file or directory existence */
|
||||
bool ctru_file_exists(const char *path)
|
||||
{
|
||||
file_internal_lock_WRITER();
|
||||
bool rc = test_stream_exists_internal(path) > 0;
|
||||
file_internal_unlock_WRITER();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* note: no symbolic links support in devkitARM */
|
||||
ssize_t ctru_readlink(const char *path, char *buf, size_t bufsiz)
|
||||
{
|
||||
return readlink(path, buf, bufsiz);
|
||||
}
|
||||
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 by Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef _SYS_FILE_H
|
||||
#define _SYS_FILE_H
|
||||
|
||||
#include "bfile.h"
|
||||
|
||||
/* Include for file.h and dir.h because mkdir and friends may be here */
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define strlcpy_from_os strlcpy
|
||||
|
||||
#define AtomicLoad(ptr) __atomic_load_n((u32*)(ptr), __ATOMIC_SEQ_CST)
|
||||
#define AtomicAdd(ptr, value) __atomic_add_fetch((u32*)(ptr), value, __ATOMIC_SEQ_CST)
|
||||
|
||||
struct filestr_base {
|
||||
Pager* cache; /* buffer IO implementation (cache) */
|
||||
Handle handle; /* file handle */
|
||||
u64 size; /* file size */
|
||||
int flags; /* stream flags */
|
||||
char path[MAX_PATH+1];
|
||||
|
||||
LightLock mtx; /* serialization for this stream */
|
||||
};
|
||||
|
||||
static inline void filestr_lock(struct filestr_base *stream)
|
||||
{
|
||||
LightLock_Lock(&stream->mtx);
|
||||
}
|
||||
|
||||
static inline void filestr_unlock(struct filestr_base *stream)
|
||||
{
|
||||
LightLock_Unlock(&stream->mtx);
|
||||
}
|
||||
|
||||
/* stream lock doesn't have to be used if getting RW lock writer access */
|
||||
#define FILESTR_WRITER 0
|
||||
#define FILESTR_READER 1
|
||||
|
||||
#define FILESTR_LOCK(type, stream) \
|
||||
({ if (FILESTR_##type) filestr_lock(stream); })
|
||||
|
||||
#define FILESTR_UNLOCK(type, stream) \
|
||||
({ if (FILESTR_##type) filestr_unlock(stream); })
|
||||
|
||||
/** Synchronization used throughout **/
|
||||
|
||||
/* acquire the filesystem lock as READER */
|
||||
static inline void file_internal_lock_READER(void)
|
||||
{
|
||||
extern sync_RWMutex file_internal_mrsw;
|
||||
sync_RWMutexRLock(&file_internal_mrsw);
|
||||
}
|
||||
|
||||
/* release the filesystem lock as READER */
|
||||
static inline void file_internal_unlock_READER(void)
|
||||
{
|
||||
extern sync_RWMutex file_internal_mrsw;
|
||||
sync_RWMutexRUnlock(&file_internal_mrsw);
|
||||
}
|
||||
|
||||
/* acquire the filesystem lock as WRITER */
|
||||
static inline void file_internal_lock_WRITER(void)
|
||||
{
|
||||
extern sync_RWMutex file_internal_mrsw;
|
||||
sync_RWMutexLock(&file_internal_mrsw);
|
||||
}
|
||||
|
||||
/* release the filesystem lock as WRITER */
|
||||
static inline void file_internal_unlock_WRITER(void)
|
||||
{
|
||||
extern sync_RWMutex file_internal_mrsw;
|
||||
sync_RWMutexUnlock(&file_internal_mrsw);
|
||||
}
|
||||
|
||||
#define ERRNO 0 /* maintain errno value */
|
||||
#define _RC 0 /* maintain rc value */
|
||||
|
||||
/* NOTES: if _errno is a non-constant expression, it must set an error
|
||||
* number and not return the ERRNO constant which will merely set
|
||||
* errno to zero, not preserve the current value; if you must set
|
||||
* errno to zero, set it explicitly, not in the macro
|
||||
*
|
||||
* if _rc is constant-expression evaluation to 'RC', then rc will
|
||||
* NOT be altered; i.e. if you must set rc to zero, set it explicitly,
|
||||
* not in the macro
|
||||
*/
|
||||
|
||||
#define FILE_SET_CODE(_name, _keepcode, _value) \
|
||||
({ __builtin_constant_p(_value) ? \
|
||||
({ if ((_value) != (_keepcode)) _name = (_value); }) : \
|
||||
({ _name = (_value); }); })
|
||||
|
||||
/* set errno and rc and proceed to the "file_error:" label */
|
||||
#define FILE_ERROR(_errno, _rc) \
|
||||
({ FILE_SET_CODE(errno, ERRNO, (_errno)); \
|
||||
FILE_SET_CODE(rc, _RC, (_rc)); \
|
||||
goto file_error; })
|
||||
|
||||
/* set errno and return a value at the point of invocation */
|
||||
#define FILE_ERROR_RETURN(_errno, _rc...) \
|
||||
({ FILE_SET_CODE(errno, ERRNO, _errno); \
|
||||
return _rc; })
|
||||
|
||||
/* set errno and return code, no branching */
|
||||
#define FILE_ERROR_SET(_errno, _rc) \
|
||||
({ FILE_SET_CODE(errno, ERRNO, (_errno)); \
|
||||
FILE_SET_CODE(rc, _RC, (_rc)); })
|
||||
|
||||
#endif /* _SYS_FILE_H */
|
||||
|
||||
|
|
@ -1,403 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio Ga.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "sys_thread.h"
|
||||
#include "sys_timer.h"
|
||||
#include "debug.h"
|
||||
#include "logf.h"
|
||||
|
||||
bool _AtomicCAS(u32 *ptr, int oldval, int newval)
|
||||
{
|
||||
int expected = oldval;
|
||||
int desired = newval;
|
||||
return __atomic_compare_exchange(ptr, &expected, &desired, false,
|
||||
__ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
bool _AtomicTryLock(int *lock)
|
||||
{
|
||||
int result;
|
||||
asm volatile(
|
||||
"ldrex %0, [%2] \n"
|
||||
"teq %0, #0 \n"
|
||||
"strexeq %0, %1, [%2] \n"
|
||||
: "=&r"(result)
|
||||
: "r"(1), "r"(lock)
|
||||
: "cc", "memory"
|
||||
);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
#define CPUPauseInstruction() asm volatile("yield" ::: "memory")
|
||||
void AtomicLock(int *lock)
|
||||
{
|
||||
int iterations = 0;
|
||||
while (!_AtomicTryLock(lock)) {
|
||||
if (iterations < 32) {
|
||||
iterations++;
|
||||
CPUPauseInstruction();
|
||||
} else {
|
||||
sys_delay(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AtomicUnlock(int *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
/* Convert rockbox priority value to libctru value */
|
||||
int get_ctru_thread_priority(int priority)
|
||||
{
|
||||
if ((priority == PRIORITY_REALTIME_1) || (priority == PRIORITY_REALTIME_2) ||
|
||||
(priority == PRIORITY_REALTIME_3) || (priority == PRIORITY_REALTIME_4) ||
|
||||
(priority == PRIORITY_REALTIME))
|
||||
return 0x18;
|
||||
else if (priority == PRIORITY_BUFFERING)
|
||||
return 0x18; /* Highest */
|
||||
else if ((priority == PRIORITY_USER_INTERFACE) || (priority == PRIORITY_RECORDING) ||
|
||||
(priority == PRIORITY_PLAYBACK))
|
||||
return 0x30;
|
||||
else if (priority == PRIORITY_PLAYBACK_MAX)
|
||||
return 0x2F;
|
||||
else if (priority == PRIORITY_SYSTEM)
|
||||
return 0x30;
|
||||
else if (priority == PRIORITY_BACKGROUND)
|
||||
return 0x3F; /* Lowest */
|
||||
else
|
||||
return 0x30;
|
||||
}
|
||||
|
||||
static size_t get_thread_stack_size(size_t requested_size)
|
||||
{
|
||||
if (requested_size == 0) {
|
||||
return (80 * 1024); /* 80 kB */
|
||||
}
|
||||
|
||||
return requested_size;
|
||||
}
|
||||
|
||||
static void thread_entry(void *arg)
|
||||
{
|
||||
sys_run_thread((sysThread *)arg);
|
||||
threadExit(0);
|
||||
}
|
||||
|
||||
int wait_on_semaphore_for(LightSemaphore *sem, u32 timeout)
|
||||
{
|
||||
u64 stop_time = sys_get_ticks64() + timeout;
|
||||
u64 current_time = sys_get_ticks64();
|
||||
while (current_time < stop_time) {
|
||||
if (LightSemaphore_TryAcquire(sem, 1) == 0) {
|
||||
return 0;
|
||||
}
|
||||
/* 100 microseconds seems to be the sweet spot */
|
||||
svcSleepThread(100000LL);
|
||||
current_time = sys_get_ticks64();
|
||||
}
|
||||
|
||||
/* If we failed, yield to avoid starvation on busy waits */
|
||||
svcSleepThread(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sys_sem_try_wait(LightSemaphore *sem)
|
||||
{
|
||||
if (LightSemaphore_TryAcquire(sem, 1) != 0) {
|
||||
/* If we failed, yield to avoid starvation on busy waits */
|
||||
svcSleepThread(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_sem_wait_timeout(LightSemaphore *sem, u32 timeout)
|
||||
{
|
||||
if (timeout == (~(u32)0)) {
|
||||
LightSemaphore_Acquire(sem, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LightSemaphore_TryAcquire(sem, 1) != 0) {
|
||||
return wait_on_semaphore_for(sem, timeout);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_sem_wait(LightSemaphore *sem)
|
||||
{
|
||||
return sys_sem_wait_timeout(sem, (~(u32)0));
|
||||
}
|
||||
|
||||
u32 sys_sem_value(LightSemaphore *sem)
|
||||
{
|
||||
return sem->current_count;
|
||||
}
|
||||
|
||||
int sys_thread_id(void)
|
||||
{
|
||||
u32 thread_ID = 0;
|
||||
svcGetThreadId(&thread_ID, CUR_THREAD_HANDLE);
|
||||
return (int)thread_ID;
|
||||
}
|
||||
|
||||
void sys_run_thread(sysThread *thread)
|
||||
{
|
||||
void *userdata = thread->userdata;
|
||||
int(* userfunc)(void *) = thread->userfunc;
|
||||
|
||||
int *statusloc = &thread->status;
|
||||
|
||||
/* Get the thread id */
|
||||
thread->threadid = sys_thread_id();
|
||||
|
||||
/* Run the function */
|
||||
*statusloc = userfunc(userdata);
|
||||
|
||||
/* Mark us as ready to be joined (or detached) */
|
||||
if (!AtomicCAS(&thread->state, THREAD_STATE_ALIVE, THREAD_STATE_ZOMBIE)) {
|
||||
/* Clean up if something already detached us. */
|
||||
if (AtomicCAS(&thread->state, THREAD_STATE_DETACHED, THREAD_STATE_CLEANED)) {
|
||||
free(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sysThread *sys_create_thread(int(*fn)(void *), const char *name, const size_t stacksize,
|
||||
void *data IF_PRIO(, int priority) IF_COP(, unsigned int core))
|
||||
{
|
||||
sys_ticks_init();
|
||||
|
||||
/* Allocate memory for the thread info structure */
|
||||
sysThread *thread = (sysThread *) calloc(1, sizeof(sysThread));
|
||||
if (thread == NULL) {
|
||||
DEBUGF("sys_create_thread: could not allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
thread->status = -1;
|
||||
AtomicSet(&thread->state, THREAD_STATE_ALIVE);
|
||||
|
||||
/* Set up the arguments for the thread */
|
||||
thread->userfunc = fn;
|
||||
thread->userdata = data;
|
||||
thread->stacksize = stacksize;
|
||||
|
||||
int cpu = -1;
|
||||
if (name && (strncmp(name, "tagcache", 8) == 0) && R_SUCCEEDED(APT_SetAppCpuTimeLimit(30))) {
|
||||
cpu = 1;
|
||||
printf("thread: %s, running in cpu 1\n", name);
|
||||
}
|
||||
|
||||
thread->handle = threadCreate(thread_entry,
|
||||
thread,
|
||||
get_thread_stack_size(stacksize),
|
||||
get_ctru_thread_priority(priority),
|
||||
cpu,
|
||||
false);
|
||||
|
||||
if (!thread->handle) {
|
||||
DEBUGF("sys_create_thread: threadCreate failed\n");
|
||||
free(thread);
|
||||
thread = NULL;
|
||||
}
|
||||
|
||||
/* Everything is running now */
|
||||
return thread;
|
||||
}
|
||||
|
||||
void sys_wait_thread(sysThread *thread, int *status)
|
||||
{
|
||||
if (thread) {
|
||||
Result res = threadJoin(thread->handle, U64_MAX);
|
||||
|
||||
/*
|
||||
Detached threads can be waited on, but should NOT be cleaned manually
|
||||
as it would result in a fatal error.
|
||||
*/
|
||||
if (R_SUCCEEDED(res) && AtomicGet(&thread->state) != THREAD_STATE_DETACHED) {
|
||||
threadFree(thread->handle);
|
||||
}
|
||||
if (status) {
|
||||
*status = thread->status;
|
||||
}
|
||||
free(thread);
|
||||
}
|
||||
}
|
||||
|
||||
void sys_detach_thread(sysThread *thread)
|
||||
{
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Grab dibs if the state is alive+joinable. */
|
||||
if (AtomicCAS(&thread->state, THREAD_STATE_ALIVE, THREAD_STATE_DETACHED)) {
|
||||
threadDetach(thread->handle);
|
||||
} else {
|
||||
/* all other states are pretty final, see where we landed. */
|
||||
const int thread_state = AtomicGet(&thread->state);
|
||||
if ((thread_state == THREAD_STATE_DETACHED) || (thread_state == THREAD_STATE_CLEANED)) {
|
||||
return; /* already detached (you shouldn't call this twice!) */
|
||||
} else if (thread_state == THREAD_STATE_ZOMBIE) {
|
||||
sys_wait_thread(thread, NULL); /* already done, clean it up. */
|
||||
} else {
|
||||
assert(0 && "Unexpected thread state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sys_set_thread_priority(sysThread *thread, int priority)
|
||||
{
|
||||
Handle h = threadGetHandle(thread->handle);
|
||||
int old_priority = priority;
|
||||
Result res = svcSetThreadPriority(h, get_ctru_thread_priority(priority));
|
||||
if (R_SUCCEEDED(res)) {
|
||||
return priority;
|
||||
}
|
||||
|
||||
return old_priority;
|
||||
}
|
||||
|
||||
/* sysCond */
|
||||
sysCond *sys_cond_create(void)
|
||||
{
|
||||
sysCond *cond;
|
||||
|
||||
cond = (sysCond *)malloc(sizeof(sysCond));
|
||||
if (cond) {
|
||||
RecursiveLock_Init(&cond->lock);
|
||||
LightSemaphore_Init(&cond->wait_sem, 0, ((s16)0x7FFF));
|
||||
LightSemaphore_Init(&cond->wait_done, 0, ((s16)0x7FFF));
|
||||
cond->waiting = cond->signals = 0;
|
||||
} else {
|
||||
DEBUGF("sys_cond_create: out of memory.\n");;
|
||||
}
|
||||
return cond;
|
||||
}
|
||||
|
||||
/* Destroy a condition variable */
|
||||
void sys_cond_destroy(sysCond *cond)
|
||||
{
|
||||
if (cond) {
|
||||
free(cond);
|
||||
}
|
||||
}
|
||||
|
||||
/* Restart one of the threads that are waiting on the condition variable */
|
||||
int sys_cond_signal(sysCond *cond)
|
||||
{
|
||||
if (!cond) {
|
||||
DEBUGF("sys_cond_signal: Invalid param 'cond'\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If there are waiting threads not already signalled, then
|
||||
signal the condition and wait for the thread to respond.
|
||||
*/
|
||||
RecursiveLock_Lock(&cond->lock);
|
||||
if (cond->waiting > cond->signals) {
|
||||
++cond->signals;
|
||||
LightSemaphore_Release(&cond->wait_sem, 1);
|
||||
RecursiveLock_Unlock(&cond->lock);
|
||||
LightSemaphore_Acquire(&cond->wait_done, 1);
|
||||
} else {
|
||||
RecursiveLock_Unlock(&cond->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Restart all threads that are waiting on the condition variable */
|
||||
int sys_cond_broadcast(sysCond *cond)
|
||||
{
|
||||
if (!cond) {
|
||||
DEBUGF("sys_cond_signal: Invalid param 'cond'\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If there are waiting threads not already signalled, then
|
||||
signal the condition and wait for the thread to respond.
|
||||
*/
|
||||
RecursiveLock_Lock(&cond->lock);
|
||||
if (cond->waiting > cond->signals) {
|
||||
int i, num_waiting;
|
||||
|
||||
num_waiting = (cond->waiting - cond->signals);
|
||||
cond->signals = cond->waiting;
|
||||
for (i = 0; i < num_waiting; ++i) {
|
||||
LightSemaphore_Release(&cond->wait_sem, 1);
|
||||
}
|
||||
/* Now all released threads are blocked here, waiting for us.
|
||||
Collect them all (and win fabulous prizes!) :-)
|
||||
*/
|
||||
RecursiveLock_Unlock(&cond->lock);
|
||||
for (i = 0; i < num_waiting; ++i) {
|
||||
LightSemaphore_Acquire(&cond->wait_done, 1);
|
||||
}
|
||||
} else {
|
||||
RecursiveLock_Unlock(&cond->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_cond_wait(sysCond *cond, RecursiveLock *mutex)
|
||||
{
|
||||
if (!cond) {
|
||||
DEBUGF("sys_cond_signal: Invalid param 'cond'\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
RecursiveLock_Lock(&cond->lock);
|
||||
++cond->waiting;
|
||||
RecursiveLock_Unlock(&cond->lock);
|
||||
|
||||
/* Unlock the mutex, as is required by condition variable semantics */
|
||||
RecursiveLock_Unlock(mutex);
|
||||
|
||||
/* Wait for a signal */
|
||||
LightSemaphore_Acquire(&cond->wait_sem, 1);
|
||||
|
||||
RecursiveLock_Lock(&cond->lock);
|
||||
if (cond->signals > 0) {
|
||||
/* We always notify the signal thread that we are done */
|
||||
LightSemaphore_Release(&cond->wait_done, 1);
|
||||
|
||||
/* Signal handshake complete */
|
||||
--cond->signals;
|
||||
}
|
||||
--cond->waiting;
|
||||
RecursiveLock_Unlock(&cond->lock);
|
||||
|
||||
/* Lock the mutex, as is required by condition variable semantics */
|
||||
RecursiveLock_Lock(mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __SYSTHREAD_H__
|
||||
#define __SYSTHREAD_H__
|
||||
|
||||
#include "thread.h"
|
||||
|
||||
#include <3ds/synchronization.h>
|
||||
#include <3ds/thread.h>
|
||||
#include <3ds/services/apt.h>
|
||||
|
||||
/* Complementary atomic operations */
|
||||
bool _AtomicCAS(u32 *ptr, int oldval, int newval);
|
||||
#define AtomicGet(ptr) __atomic_load_n((u32*)(ptr), __ATOMIC_SEQ_CST)
|
||||
#define AtomicSet(ptr, value) AtomicSwap(ptr, value)
|
||||
#define AtomicCAS(ptr, oldvalue, newvalue) _AtomicCAS((u32 *)(ptr), oldvalue, newvalue)
|
||||
void AtomicLock(int *lock);
|
||||
void AtomicUnlock(int *lock);
|
||||
|
||||
/* This code was taken from SDL2 thread implementation */
|
||||
|
||||
enum thread_state_t
|
||||
{
|
||||
THREAD_STATE_ALIVE,
|
||||
THREAD_STATE_DETACHED,
|
||||
THREAD_STATE_ZOMBIE,
|
||||
THREAD_STATE_CLEANED,
|
||||
};
|
||||
|
||||
typedef struct _thread
|
||||
{
|
||||
int threadid;
|
||||
Thread handle;
|
||||
int status;
|
||||
int state;
|
||||
size_t stacksize;
|
||||
int(* userfunc)(void *);
|
||||
void *userdata;
|
||||
void *data;
|
||||
} sysThread;
|
||||
|
||||
typedef struct _cond
|
||||
{
|
||||
RecursiveLock lock;
|
||||
int waiting;
|
||||
int signals;
|
||||
LightSemaphore wait_sem;
|
||||
LightSemaphore wait_done;
|
||||
} sysCond;
|
||||
|
||||
int sys_sem_wait(LightSemaphore *sem);
|
||||
int sys_sem_wait_timeout(LightSemaphore *sem, u32 timeout);
|
||||
int sys_sem_try_wait(LightSemaphore *sem);
|
||||
u32 sys_sem_value(LightSemaphore *sem);
|
||||
|
||||
sysThread *sys_create_thread(int(*fn)(void *), const char *name, const size_t stacksize,
|
||||
void *data IF_PRIO(, int priority) IF_COP(, unsigned int core));
|
||||
void sys_run_thread(sysThread *thread);
|
||||
void sys_wait_thread(sysThread *thread, int *status);
|
||||
int sys_thread_id(void);
|
||||
int sys_set_thread_priority(sysThread *thread, int priority);
|
||||
|
||||
sysCond *sys_cond_create(void);
|
||||
void sys_cond_destroy(sysCond *cond);
|
||||
int sys_cond_signal(sysCond *cond);
|
||||
int sys_cond_broadcast(sysCond *cond);
|
||||
int sys_cond_wait(sysCond *cond, RecursiveLock *mutex);
|
||||
#endif /* #ifndef __SYSTHREAD_H__ */
|
||||
|
||||
|
|
@ -1,402 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2006 Dan Everton
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "debug.h"
|
||||
#include "logf.h"
|
||||
|
||||
#include <3ds/os.h>
|
||||
#include "sys_thread.h"
|
||||
#include "sys_timer.h"
|
||||
|
||||
#define CACHELINE_SIZE 128
|
||||
|
||||
static bool ticks_started = false;
|
||||
static u64 start_tick;
|
||||
|
||||
#define NSEC_PER_MSEC 1000000ULL
|
||||
|
||||
void sys_ticks_init(void)
|
||||
{
|
||||
if (ticks_started) {
|
||||
return;
|
||||
}
|
||||
ticks_started = true;
|
||||
|
||||
start_tick = svcGetSystemTick();
|
||||
}
|
||||
|
||||
void sys_ticks_quit(void)
|
||||
{
|
||||
ticks_started = false;
|
||||
}
|
||||
|
||||
u64 sys_get_ticks64(void)
|
||||
{
|
||||
u64 elapsed;
|
||||
if (!ticks_started) {
|
||||
sys_ticks_init();
|
||||
}
|
||||
|
||||
elapsed = svcGetSystemTick() - start_tick;
|
||||
return elapsed / CPU_TICKS_PER_MSEC;
|
||||
}
|
||||
|
||||
u32 sys_get_ticks(void)
|
||||
{
|
||||
return (u32)(sys_get_ticks64() & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
void sys_delay(u32 ms)
|
||||
{
|
||||
svcSleepThread(ms * NSEC_PER_MSEC);
|
||||
}
|
||||
|
||||
typedef struct _timer
|
||||
{
|
||||
int timerID;
|
||||
timer_callback_ptr callback;
|
||||
void *param;
|
||||
u32 interval;
|
||||
u32 scheduled;
|
||||
int canceled;
|
||||
struct _timer *next;
|
||||
} sysTimer;
|
||||
|
||||
typedef struct _timer_map
|
||||
{
|
||||
int timerID;
|
||||
sysTimer *timer;
|
||||
struct _timer_map *next;
|
||||
} timerMap;
|
||||
|
||||
/* The timers are kept in a sorted list */
|
||||
typedef struct
|
||||
{
|
||||
/* Data used by the main thread */
|
||||
Thread thread;
|
||||
int nextID;
|
||||
timerMap *timermap;
|
||||
RecursiveLock timermap_lock;
|
||||
|
||||
/* Padding to separate cache lines between threads */
|
||||
char cache_pad[CACHELINE_SIZE];
|
||||
|
||||
/* Data used to communicate with the timer thread */
|
||||
int lock;
|
||||
LightSemaphore sem;
|
||||
sysTimer *pending;
|
||||
sysTimer *freelist;
|
||||
int active;
|
||||
|
||||
/* List of timers - this is only touched by the timer thread */
|
||||
sysTimer *timers;
|
||||
} timerData;
|
||||
|
||||
static timerData timer_data = { .active = 0 };
|
||||
|
||||
/* The idea here is that any thread might add a timer, but a single
|
||||
* thread manages the active timer queue, sorted by scheduling time.
|
||||
*
|
||||
* Timers are removed by simply setting a canceled flag
|
||||
*/
|
||||
|
||||
static void add_timer_interval(timerData *data, sysTimer *timer)
|
||||
{
|
||||
sysTimer *prev, *curr;
|
||||
|
||||
prev = NULL;
|
||||
for (curr = data->timers; curr; prev = curr, curr = curr->next) {
|
||||
if ((s32)(timer->scheduled - curr->scheduled) < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert the timer here! */
|
||||
if (prev) {
|
||||
prev->next = timer;
|
||||
} else {
|
||||
data->timers = timer;
|
||||
}
|
||||
timer->next = curr;
|
||||
}
|
||||
|
||||
static void timer_thread(void *_data)
|
||||
{
|
||||
timerData *data = (timerData *)_data;
|
||||
sysTimer *pending;
|
||||
sysTimer *current;
|
||||
sysTimer *freelist_head = NULL;
|
||||
sysTimer *freelist_tail = NULL;
|
||||
u32 tick, now, interval, delay;
|
||||
|
||||
/* Threaded timer loop:
|
||||
* 1. Queue timers added by other threads
|
||||
* 2. Handle any timers that should dispatch this cycle
|
||||
* 3. Wait until next dispatch time or new timer arrives
|
||||
*/
|
||||
for (;;) {
|
||||
/* Pending and freelist maintenance */
|
||||
AtomicLock(&data->lock);
|
||||
{
|
||||
/* Get any timers ready to be queued */
|
||||
pending = data->pending;
|
||||
data->pending = NULL;
|
||||
|
||||
/* Make any unused timer structures available */
|
||||
if (freelist_head) {
|
||||
freelist_tail->next = data->freelist;
|
||||
data->freelist = freelist_head;
|
||||
}
|
||||
}
|
||||
AtomicUnlock(&data->lock);
|
||||
|
||||
/* Sort the pending timers into our list */
|
||||
while (pending) {
|
||||
current = pending;
|
||||
pending = pending->next;
|
||||
add_timer_interval(data, current);
|
||||
}
|
||||
freelist_head = NULL;
|
||||
freelist_tail = NULL;
|
||||
|
||||
/* Check to see if we're still running, after maintenance */
|
||||
if (!AtomicGet(&data->active)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Initial delay if there are no timers */
|
||||
delay = (~(u32)0);
|
||||
|
||||
tick = sys_get_ticks();
|
||||
|
||||
/* Process all the pending timers for this tick */
|
||||
while (data->timers) {
|
||||
current = data->timers;
|
||||
|
||||
if ((s32)(tick - current->scheduled) < 0) {
|
||||
/* Scheduled for the future, wait a bit */
|
||||
delay = (current->scheduled - tick);
|
||||
break;
|
||||
}
|
||||
|
||||
/* We're going to do something with this timer */
|
||||
data->timers = current->next;
|
||||
|
||||
if (AtomicGet(¤t->canceled)) {
|
||||
interval = 0;
|
||||
} else {
|
||||
interval = current->callback(current->interval, current->param);
|
||||
}
|
||||
|
||||
if (interval > 0) {
|
||||
/* Reschedule this timer */
|
||||
current->interval = interval;
|
||||
current->scheduled = tick + interval;
|
||||
add_timer_interval(data, current);
|
||||
} else {
|
||||
if (!freelist_head) {
|
||||
freelist_head = current;
|
||||
}
|
||||
if (freelist_tail) {
|
||||
freelist_tail->next = current;
|
||||
}
|
||||
freelist_tail = current;
|
||||
|
||||
AtomicSet(¤t->canceled, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust the delay based on processing time */
|
||||
now = sys_get_ticks();
|
||||
interval = (now - tick);
|
||||
if (interval > delay) {
|
||||
delay = 0;
|
||||
} else {
|
||||
delay -= interval;
|
||||
}
|
||||
|
||||
/* Note that each time a timer is added, this will return
|
||||
immediately, but we process the timers added all at once.
|
||||
That's okay, it just means we run through the loop a few
|
||||
extra times.
|
||||
*/
|
||||
sys_sem_wait_timeout(&data->sem, delay);
|
||||
}
|
||||
}
|
||||
|
||||
int sys_timer_init(void)
|
||||
{
|
||||
timerData *data = &timer_data;
|
||||
|
||||
if (!AtomicGet(&data->active)) {
|
||||
RecursiveLock_Init(&data->timermap_lock);
|
||||
LightSemaphore_Init(&data->sem, 0, ((s16)0x7FFF));
|
||||
AtomicSet(&data->active, 1);
|
||||
|
||||
/* Timer threads use a callback into the app, so we can't set a limited stack size here. */
|
||||
data->thread = threadCreate(timer_thread,
|
||||
data,
|
||||
32 * 1024,
|
||||
0x28,
|
||||
-1,
|
||||
false);
|
||||
if (!data->thread) {
|
||||
sys_timer_quit();
|
||||
return -1;
|
||||
}
|
||||
|
||||
AtomicSet(&data->nextID, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sys_timer_quit(void)
|
||||
{
|
||||
timerData *data = &timer_data;
|
||||
sysTimer *timer;
|
||||
timerMap *entry;
|
||||
|
||||
if (AtomicCAS(&data->active, 1, 0)) { /* active? Move to inactive. */
|
||||
/* Shutdown the timer thread */
|
||||
if (data->thread) {
|
||||
LightSemaphore_Release(&data->sem, 1);
|
||||
Result res = threadJoin(data->thread, U64_MAX);
|
||||
threadFree(data->thread);
|
||||
data->thread = NULL;
|
||||
}
|
||||
|
||||
/* Clean up the timer entries */
|
||||
while (data->timers) {
|
||||
timer = data->timers;
|
||||
data->timers = timer->next;
|
||||
free(timer);
|
||||
}
|
||||
while (data->freelist) {
|
||||
timer = data->freelist;
|
||||
data->freelist = timer->next;
|
||||
free(timer);
|
||||
}
|
||||
while (data->timermap) {
|
||||
entry = data->timermap;
|
||||
data->timermap = entry->next;
|
||||
free(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sys_add_timer(u32 interval, timer_callback_ptr callback, void *param)
|
||||
{
|
||||
timerData *data = &timer_data;
|
||||
sysTimer *timer;
|
||||
timerMap *entry;
|
||||
|
||||
AtomicLock(&data->lock);
|
||||
if (!AtomicGet(&data->active)) {
|
||||
if (sys_timer_init() < 0) {
|
||||
AtomicUnlock(&data->lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
timer = data->freelist;
|
||||
if (timer) {
|
||||
data->freelist = timer->next;
|
||||
}
|
||||
AtomicUnlock(&data->lock);
|
||||
|
||||
if (timer) {
|
||||
sys_remove_timer(timer->timerID);
|
||||
} else {
|
||||
timer = (sysTimer *) malloc(sizeof(*timer));
|
||||
if (!timer) {
|
||||
DEBUGF("sys_add_timer: out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
timer->timerID = AtomicIncrement(&data->nextID);
|
||||
timer->callback = callback;
|
||||
timer->param = param;
|
||||
timer->interval = interval;
|
||||
timer->scheduled = sys_get_ticks() + interval;
|
||||
AtomicSet(&timer->canceled, 0);
|
||||
|
||||
entry = (timerMap *) malloc(sizeof(*entry));
|
||||
if (!entry) {
|
||||
free(timer);
|
||||
DEBUGF("sys_add_timer: out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
entry->timer = timer;
|
||||
entry->timerID = timer->timerID;
|
||||
|
||||
RecursiveLock_Lock(&data->timermap_lock);
|
||||
entry->next = data->timermap;
|
||||
data->timermap = entry;
|
||||
RecursiveLock_Unlock(&data->timermap_lock);
|
||||
|
||||
/* Add the timer to the pending list for the timer thread */
|
||||
AtomicLock(&data->lock);
|
||||
timer->next = data->pending;
|
||||
data->pending = timer;
|
||||
AtomicUnlock(&data->lock);
|
||||
|
||||
/* Wake up the timer thread if necessary */
|
||||
LightSemaphore_Release(&data->sem, 1);
|
||||
|
||||
return entry->timerID;
|
||||
}
|
||||
|
||||
bool sys_remove_timer(int id)
|
||||
{
|
||||
timerData *data = &timer_data;
|
||||
timerMap *prev, *entry;
|
||||
bool canceled = false;
|
||||
|
||||
/* Find the timer */
|
||||
RecursiveLock_Lock(&data->timermap_lock);
|
||||
prev = NULL;
|
||||
for (entry = data->timermap; entry; prev = entry, entry = entry->next) {
|
||||
if (entry->timerID == id) {
|
||||
if (prev) {
|
||||
prev->next = entry->next;
|
||||
} else {
|
||||
data->timermap = entry->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
RecursiveLock_Unlock(&data->timermap_lock);
|
||||
|
||||
if (entry) {
|
||||
if (!AtomicGet(&entry->timer->canceled)) {
|
||||
AtomicSet(&entry->timer->canceled, 1);
|
||||
canceled = true;
|
||||
}
|
||||
free(entry);
|
||||
}
|
||||
return canceled;
|
||||
}
|
||||
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __SYSTIMER_H__
|
||||
#define __SYSTIMER_H__
|
||||
|
||||
#include <3ds/svc.h>
|
||||
#include "sys_thread.h"
|
||||
|
||||
typedef u32 (* timer_callback_ptr) (u32 interval, void *param);
|
||||
|
||||
void sys_ticks_init(void);
|
||||
void sys_ticks_quit(void);
|
||||
u32 sys_get_ticks(void);
|
||||
u64 sys_get_ticks64(void);
|
||||
void sys_delay(u32 ms);
|
||||
|
||||
int sys_timer_init(void);
|
||||
void sys_timer_quit(void);
|
||||
int sys_add_timer(u32 interval, timer_callback_ptr callback, void *param);
|
||||
bool sys_remove_timer(int id);
|
||||
|
||||
#endif /* #ifndef __SYSTIMER_H__ */
|
||||
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016-2020 Aurora Wright, TuxSH
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||
* * Requiring preservation of specified reasonable legal notices or
|
||||
* author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
* * Prohibiting misrepresentation of the origin of that material,
|
||||
* or requiring that modified versions of such material be marked in
|
||||
* reasonable ways as different from the original version.
|
||||
*/
|
||||
|
||||
/* This file is taken from Luma3DS project */
|
||||
|
||||
#include <3ds.h>
|
||||
#include <math.h>
|
||||
#include "luminance-ctru.h"
|
||||
|
||||
// For accessing physmem uncached (and directly)
|
||||
#define PA_PTR(addr) (void *)((u32)(addr) | 1 << 31)
|
||||
|
||||
#ifndef PA_FROM_VA_PTR
|
||||
#define PA_FROM_VA_PTR(addr) PA_PTR(svcConvertVAToPA((const void *)(addr), false))
|
||||
#endif
|
||||
|
||||
#define REG32(addr) (*(vu32 *)(PA_PTR(addr)))
|
||||
|
||||
|
||||
extern bool is_n3ds;
|
||||
|
||||
typedef struct BlPwmData
|
||||
{
|
||||
float coeffs[3][3];
|
||||
u8 numLevels;
|
||||
u8 unk;
|
||||
u16 luminanceLevels[7];
|
||||
u16 brightnessMax;
|
||||
u16 brightnessMin;
|
||||
} BlPwmData;
|
||||
|
||||
// Calibration, with (dubious) default values as fallback
|
||||
static BlPwmData s_blPwmData = {
|
||||
.coeffs = {
|
||||
{ 0.00111639f, 1.41412f, 0.07178809f },
|
||||
{ 0.000418169f, 0.66567f, 0.06098654f },
|
||||
{ 0.00208543f, 1.55639f, 0.0385939f }
|
||||
},
|
||||
.numLevels = 5,
|
||||
.unk = 0,
|
||||
.luminanceLevels = { 20, 43, 73, 95, 117, 172, 172 },
|
||||
.brightnessMax = 512,
|
||||
.brightnessMin = 13,
|
||||
};
|
||||
|
||||
static inline float getPwmRatio(u32 brightnessMax, u32 pwmCnt)
|
||||
{
|
||||
u32 val = (pwmCnt & 0x10000) ? pwmCnt & 0x3FF : 511; // check pwm enabled flag
|
||||
return (float)brightnessMax / (val + 1);
|
||||
}
|
||||
|
||||
// nn's asm has rounding errors (originally at 10^-3)
|
||||
static inline u32 luminanceToBrightness(u32 luminance, const float coeffs[3], u32 minLuminance, float pwmRatio)
|
||||
{
|
||||
float x = (float)luminance;
|
||||
float y = coeffs[0]*x*x + coeffs[1]*x + coeffs[2];
|
||||
y = (y <= minLuminance ? (float)minLuminance : y) / pwmRatio;
|
||||
|
||||
return (u32)(y + 0.5f);
|
||||
}
|
||||
|
||||
static inline u32 brightnessToLuminance(u32 brightness, const float coeffs[3], float pwmRatio)
|
||||
{
|
||||
// Find polynomial root of ax^2 + bx + c = y
|
||||
|
||||
float y = (float)brightness * pwmRatio;
|
||||
float a = coeffs[0];
|
||||
float b = coeffs[1];
|
||||
float c = coeffs[2] - y;
|
||||
|
||||
float x0 = (-b + sqrtf(b*b - 4.0f*a*c)) / (a + a);
|
||||
|
||||
return (u32)(x0 + 0.5f);
|
||||
}
|
||||
|
||||
static void readCalibration(void)
|
||||
{
|
||||
static bool calibRead = false;
|
||||
|
||||
if (!calibRead) {
|
||||
cfguInit();
|
||||
calibRead = R_SUCCEEDED(CFG_GetConfigInfoBlk8(sizeof(BlPwmData), 0x50002, &s_blPwmData));
|
||||
cfguExit();
|
||||
}
|
||||
}
|
||||
|
||||
u32 getMinLuminancePreset(void)
|
||||
{
|
||||
readCalibration();
|
||||
return s_blPwmData.luminanceLevels[0];
|
||||
}
|
||||
|
||||
u32 getMaxLuminancePreset(void)
|
||||
{
|
||||
readCalibration();
|
||||
return s_blPwmData.luminanceLevels[s_blPwmData.numLevels - 1];
|
||||
}
|
||||
|
||||
u32 getCurrentLuminance(bool top)
|
||||
{
|
||||
u32 regbase = top ? 0x10202200 : 0x10202A00;
|
||||
|
||||
readCalibration();
|
||||
|
||||
const float *coeffs = s_blPwmData.coeffs[top ? (is_n3ds ? 2 : 1) : 0];
|
||||
u32 brightness = REG32(regbase + 0x40);
|
||||
float ratio = getPwmRatio(s_blPwmData.brightnessMax, REG32(regbase + 0x44));
|
||||
|
||||
return brightnessToLuminance(brightness, coeffs, ratio);
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016-2020 Aurora Wright, TuxSH
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||
* * Requiring preservation of specified reasonable legal notices or
|
||||
* author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
* * Prohibiting misrepresentation of the origin of that material,
|
||||
* or requiring that modified versions of such material be marked in
|
||||
* reasonable ways as different from the original version.
|
||||
*/
|
||||
|
||||
#ifndef _LUMINANCE_CTRU_H_
|
||||
#define _LUMINANCE_CTRU_H_
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
extern u32 ctru_min_lum;
|
||||
extern u32 ctru_max_lum;
|
||||
extern u32 ctru_luminance;
|
||||
|
||||
u32 getMinLuminancePreset(void);
|
||||
u32 getMaxLuminancePreset(void);
|
||||
u32 getCurrentLuminance(bool top);
|
||||
|
||||
#endif /* _LUMINANCE_CTRU_H_ */
|
||||
|
|
@ -1,359 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2005 by Nick Lanham
|
||||
* Copyright (C) 2010 by Thomas Martitz
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#include "autoconf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "sound.h"
|
||||
#include "audiohw.h"
|
||||
#include "system.h"
|
||||
#include "panic.h"
|
||||
|
||||
#ifdef HAVE_RECORDING
|
||||
#include "audiohw.h"
|
||||
#ifdef HAVE_SPDIF_IN
|
||||
#include "spdif.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "pcm.h"
|
||||
#include "pcm-internal.h"
|
||||
#include "pcm_sampr.h"
|
||||
#include "pcm_mixer.h"
|
||||
|
||||
#include <3ds/ndsp/ndsp.h>
|
||||
#include <3ds/ndsp/channel.h>
|
||||
#include <3ds/services/dsp.h>
|
||||
#include <3ds/synchronization.h>
|
||||
#include <3ds/allocator/linear.h>
|
||||
|
||||
/*#define LOGF_ENABLE*/
|
||||
#include "logf.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
extern bool debug_audio;
|
||||
#endif
|
||||
|
||||
extern const char *audiodev;
|
||||
|
||||
/* Bytes left in the Rockbox PCM frame buffer. */
|
||||
static size_t _pcm_buffer_size = 0;
|
||||
|
||||
|
||||
/* Rockbox PCM frame buffer. */
|
||||
static const void *_pcm_buffer = NULL;
|
||||
|
||||
/*
|
||||
1: PCM thread suspended.
|
||||
0: PCM thread running.
|
||||
These are used by pcm_play_[lock|unlock] or pcm_play_dma_[start|stop|pause]. These need to be
|
||||
separated because of nested calls for suspending and stopping.
|
||||
*/
|
||||
static volatile int _dsp_enabled = 1;
|
||||
static volatile int _pcm_shutdown = 0;
|
||||
|
||||
|
||||
/* Mutex for PCM thread suspend/unsuspend. */
|
||||
static RecursiveLock _pcm_lock_mtx; /* audio device mutex */
|
||||
static LightEvent _dsp_callback_event; /* dsp callback synchronization flag */
|
||||
|
||||
static Thread _pcm_thread;
|
||||
static int _pcm_thread_id = -1;
|
||||
|
||||
/* DSP wave buffers */
|
||||
static ndspWaveBuf _dsp_wave_bufs[3];
|
||||
static s16 *_dsp_audio_buffer = NULL;
|
||||
|
||||
static inline bool is_in_audio_thread(int audio_thread_id)
|
||||
{
|
||||
/* The device thread locks the same mutex, but not through the public API.
|
||||
This check is in case the application, in the audio callback,
|
||||
tries to lock the thread that we've already locked from the
|
||||
device thread...just in case we only have non-recursive mutexes. */
|
||||
if ( (sys_thread_id() == audio_thread_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void pcm_play_lock(void)
|
||||
{
|
||||
if (!is_in_audio_thread(_pcm_thread_id)) {
|
||||
RecursiveLock_Lock(&_pcm_lock_mtx);
|
||||
}
|
||||
}
|
||||
|
||||
void pcm_play_unlock(void)
|
||||
{
|
||||
if (!is_in_audio_thread(_pcm_thread_id)) {
|
||||
RecursiveLock_Unlock(&_pcm_lock_mtx);
|
||||
}
|
||||
}
|
||||
|
||||
static void pcm_write_to_soundcard(const void *pcm_buffer, size_t pcm_buffer_size, ndspWaveBuf *dsp_buffer)
|
||||
{
|
||||
s16 *buffer = dsp_buffer->data_pcm16;
|
||||
memcpy(buffer, pcm_buffer, pcm_buffer_size);
|
||||
|
||||
dsp_buffer->nsamples = pcm_buffer_size / 2 / sizeof(s16);
|
||||
ndspChnWaveBufAdd(0, dsp_buffer);
|
||||
DSP_FlushDataCache(buffer, pcm_buffer_size);
|
||||
}
|
||||
|
||||
bool fill_buffer(ndspWaveBuf *dsp_buffer)
|
||||
{
|
||||
if(_pcm_buffer_size == 0)
|
||||
{
|
||||
/* Retrive a new PCM buffer from Rockbox. */
|
||||
if(!pcm_play_dma_complete_callback(PCM_DMAST_OK, &_pcm_buffer, &_pcm_buffer_size))
|
||||
{
|
||||
/* DEBUGF("DEBUG %s: No new buffer.\n", __func__); */
|
||||
svcSleepThread(10000);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pcm_play_dma_status_callback(PCM_DMAST_STARTED);
|
||||
|
||||
/* This relies on Rockbox PCM frame buffer size == ALSA PCM frame buffer size. */
|
||||
pcm_write_to_soundcard(_pcm_buffer, _pcm_buffer_size, dsp_buffer);
|
||||
_pcm_buffer_size = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pcm_thread_run(void* nothing)
|
||||
{
|
||||
(void) nothing;
|
||||
|
||||
DEBUGF("DEBUG %s: Thread start.\n", __func__);
|
||||
|
||||
_pcm_thread_id = sys_thread_id();
|
||||
|
||||
while(!AtomicGet(&_pcm_shutdown))
|
||||
{
|
||||
RecursiveLock_Lock(&_pcm_lock_mtx);
|
||||
for(size_t i = 0; i < ARRAY_SIZE(_dsp_wave_bufs); ++i) {
|
||||
if(_dsp_wave_bufs[i].status != NDSP_WBUF_DONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!fill_buffer(&_dsp_wave_bufs[i])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
RecursiveLock_Unlock(&_pcm_lock_mtx);
|
||||
|
||||
// Wait for a signal that we're needed again before continuing,
|
||||
// so that we can yield to other things that want to run
|
||||
// (Note that the 3DS uses cooperative threading)
|
||||
LightEvent_Wait(&_dsp_callback_event);
|
||||
}
|
||||
|
||||
DEBUGF("DEBUG %s: Thread end.\n", __func__);
|
||||
}
|
||||
|
||||
void dsp_callback(void *const nul_) {
|
||||
(void)nul_;
|
||||
|
||||
if(AtomicGet(&_pcm_shutdown)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LightEvent_Signal(&_dsp_callback_event);
|
||||
}
|
||||
|
||||
static void pcm_dma_apply_settings_nolock(void)
|
||||
{
|
||||
ndspChnReset(0);
|
||||
|
||||
ndspSetOutputMode(NDSP_OUTPUT_STEREO);
|
||||
|
||||
ndspChnSetRate(0, pcm_sampr);
|
||||
ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16);
|
||||
/* ndspChnSetInterp(0, NDSP_INTERP_POLYPHASE); */
|
||||
/* ndspChnSetInterp(0, NDSP_INTERP_NONE); */
|
||||
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
|
||||
|
||||
float mix[12];
|
||||
memset(mix, 0, sizeof(mix));
|
||||
mix[0] = 1.0;
|
||||
mix[1] = 1.0;
|
||||
ndspChnSetMix(0, mix);
|
||||
|
||||
memset(_dsp_wave_bufs, 0, sizeof(_dsp_wave_bufs) * ARRAY_SIZE(_dsp_wave_bufs));
|
||||
|
||||
const size_t wave_buffer_size = MIX_FRAME_SAMPLES * 2 * sizeof(s16);
|
||||
size_t buffer_size = wave_buffer_size * ARRAY_SIZE(_dsp_wave_bufs);
|
||||
|
||||
_dsp_audio_buffer = (s16 *)linearAlloc(buffer_size);
|
||||
|
||||
s16 *buffer = _dsp_audio_buffer;
|
||||
for (unsigned i = 0; i < ARRAY_SIZE(_dsp_wave_bufs); i++) {
|
||||
_dsp_wave_bufs[i].data_vaddr = buffer;
|
||||
_dsp_wave_bufs[i].status = NDSP_WBUF_DONE;
|
||||
|
||||
buffer += wave_buffer_size / sizeof(buffer[0]);
|
||||
}
|
||||
|
||||
ndspChnSetPaused(0, true);
|
||||
ndspSetCallback(dsp_callback, NULL);
|
||||
|
||||
AtomicSet(&_pcm_shutdown, 0);
|
||||
AtomicSet(&_dsp_enabled, 1);
|
||||
|
||||
// Start the thread, passing our opusFile as an argument.
|
||||
_pcm_thread = threadCreate(pcm_thread_run,
|
||||
NULL,
|
||||
32 * 1024, /* 32kB stack size */
|
||||
0x18, /* high priority */
|
||||
-1, /* run on any core */ false);
|
||||
}
|
||||
|
||||
void pcm_play_dma_start(const void *addr, size_t size)
|
||||
{
|
||||
_pcm_buffer = addr;
|
||||
_pcm_buffer_size = size;
|
||||
|
||||
RecursiveLock_Lock(&_pcm_lock_mtx);
|
||||
ndspChnSetPaused(0, false);
|
||||
RecursiveLock_Unlock(&_pcm_lock_mtx);
|
||||
}
|
||||
|
||||
void pcm_play_dma_stop(void)
|
||||
{
|
||||
RecursiveLock_Lock(&_pcm_lock_mtx);
|
||||
ndspChnSetPaused(0, true);
|
||||
RecursiveLock_Unlock(&_pcm_lock_mtx);
|
||||
}
|
||||
|
||||
/* TODO: implement recording */
|
||||
#ifdef HAVE_RECORDING
|
||||
void pcm_rec_lock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void pcm_rec_unlock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void pcm_rec_dma_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void pcm_rec_dma_close(void)
|
||||
{
|
||||
}
|
||||
|
||||
void pcm_rec_dma_start(void *start, size_t size)
|
||||
{
|
||||
(void)start;
|
||||
(void)size;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void pcm_rec_dma_stop(void)
|
||||
{
|
||||
}
|
||||
|
||||
const void * pcm_rec_dma_get_peak_buffer(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void audiohw_set_recvol(int left, int right, int type)
|
||||
{
|
||||
(void)left;
|
||||
(void)right;
|
||||
(void)type;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SPDIF_IN
|
||||
unsigned long spdif_measure_frequency(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_RECORDING */
|
||||
|
||||
void pcm_play_dma_init(void)
|
||||
{
|
||||
Result ndsp_init_res = ndspInit();
|
||||
if (R_FAILED(ndsp_init_res)) {
|
||||
if ((R_SUMMARY(ndsp_init_res) == RS_NOTFOUND) && (R_MODULE(ndsp_init_res) == RM_DSP)) {
|
||||
logf("DSP init failed: dspfirm.cdc missing!");
|
||||
} else {
|
||||
logf("DSP init failed. Error code: 0x%lX", ndsp_init_res);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
RecursiveLock_Init(&_pcm_lock_mtx);
|
||||
LightEvent_Init(&_dsp_callback_event, RESET_ONESHOT);
|
||||
}
|
||||
|
||||
void pcm_play_dma_postinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void pcm_dma_apply_settings(void)
|
||||
{
|
||||
pcm_play_lock();
|
||||
pcm_dma_apply_settings_nolock();
|
||||
pcm_play_unlock();
|
||||
}
|
||||
|
||||
void pcm_close_device(void)
|
||||
{
|
||||
RecursiveLock_Lock(&_pcm_lock_mtx);
|
||||
AtomicSet(&_pcm_shutdown, 1);
|
||||
LightEvent_Signal(&_dsp_callback_event);
|
||||
|
||||
threadJoin(_pcm_thread, UINT64_MAX);
|
||||
threadFree(_pcm_thread);
|
||||
_pcm_thread_id = -1;
|
||||
RecursiveLock_Unlock(&_pcm_lock_mtx);
|
||||
|
||||
ndspSetCallback(NULL, NULL);
|
||||
|
||||
ndspChnReset(0);
|
||||
if (_dsp_audio_buffer != NULL) {
|
||||
linearFree(_dsp_audio_buffer);
|
||||
_dsp_audio_buffer = NULL;
|
||||
}
|
||||
|
||||
ndspExit();
|
||||
}
|
||||
|
||||
/* moved from drivers/audio/ctru.c */
|
||||
void audiohw_close(void)
|
||||
{
|
||||
pcm_close_device();
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "kernel.h"
|
||||
#include "powermgmt.h"
|
||||
#include "power.h"
|
||||
#include "adc.h"
|
||||
#include "system.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include <3ds/result.h>
|
||||
#include <3ds/services/mcuhwc.h>
|
||||
#include <3ds/services/ptmu.h>
|
||||
|
||||
void mcuhwc_init(void)
|
||||
{
|
||||
Result result = mcuHwcInit();
|
||||
if (R_FAILED(result)) {
|
||||
DEBUGF("mcuhwc_init: warning, mcuHwcInit failed\n");
|
||||
}
|
||||
|
||||
result = ptmuInit();
|
||||
if (R_FAILED(result)) {
|
||||
DEBUGF("mcuhwc_init: warning, ptmuInit failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
void mcuhwc_close(void)
|
||||
{
|
||||
mcuHwcExit();
|
||||
ptmuExit();
|
||||
}
|
||||
|
||||
/* FIXME: what level should disksafe be?*/
|
||||
unsigned short battery_level_disksafe = 0;
|
||||
|
||||
unsigned short battery_level_shutoff = 0;
|
||||
|
||||
/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
|
||||
unsigned short percent_to_volt_discharge[11] =
|
||||
{
|
||||
};
|
||||
|
||||
/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
|
||||
unsigned short percent_to_volt_charge[11] =
|
||||
{
|
||||
3450, 3670, 3721, 3751, 3782, 3821, 3876, 3941, 4034, 4125, 4200
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
BATT_NOT_CHARGING = 0,
|
||||
BATT_CHARGING,
|
||||
};
|
||||
|
||||
static u8 charging_status = BATT_NOT_CHARGING;
|
||||
|
||||
unsigned int power_input_status(void)
|
||||
{
|
||||
unsigned status = POWER_INPUT_NONE;
|
||||
PTMU_GetBatteryChargeState(&charging_status);
|
||||
if (charging_status == BATT_CHARGING)
|
||||
status = POWER_INPUT_MAIN_CHARGER;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Returns battery voltage from MAX17040 VCELL ADC [millivolts steps],
|
||||
* adc returns voltage in 1.25mV steps */
|
||||
/*
|
||||
* TODO this would be interesting to be mixed with battery percentage, for information
|
||||
* and completition purpouses
|
||||
*/
|
||||
int _battery_level(void)
|
||||
{
|
||||
u8 level = 100;
|
||||
MCUHWC_GetBatteryLevel(&level);
|
||||
return level;
|
||||
}
|
||||
|
||||
bool charging_state(void)
|
||||
{
|
||||
return (charging_status == BATT_CHARGING);
|
||||
}
|
||||
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2006 by Daniel Everton <dan@iocaine.org>
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "system.h"
|
||||
#include "kernel.h"
|
||||
#include "thread-ctru.h"
|
||||
#include "system-ctru.h"
|
||||
#include "button-ctru.h"
|
||||
#include "lcd-bitmap.h"
|
||||
#include "panic.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include <3ds/allocator/linear.h>
|
||||
#include <3ds/services/cfgu.h>
|
||||
#include "bfile.h"
|
||||
|
||||
const char *audiodev = NULL;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool debug_audio = false;
|
||||
#endif
|
||||
|
||||
/* default main thread priority, low */
|
||||
s32 main_thread_priority = 0x30;
|
||||
bool is_n3ds = false;
|
||||
|
||||
/* filesystem */
|
||||
sync_RWMutex file_internal_mrsw;
|
||||
FS_Archive sdmcArchive;
|
||||
|
||||
void ctru_sys_quit(void)
|
||||
{
|
||||
sys_poweroff();
|
||||
}
|
||||
|
||||
void power_off(void)
|
||||
{
|
||||
/* since sim_thread_shutdown() grabs the mutex we need to let it free,
|
||||
* otherwise sys_wait_thread will deadlock */
|
||||
struct thread_entry* t = sim_thread_unlock();
|
||||
sim_thread_shutdown();
|
||||
|
||||
/* lock again before entering the scheduler */
|
||||
sim_thread_lock(t);
|
||||
/* sim_thread_shutdown() will cause sim_do_exit() to be called via longjmp,
|
||||
* but only if we let the sdl thread scheduler exit the other threads */
|
||||
while(1) yield();
|
||||
}
|
||||
|
||||
void sim_do_exit()
|
||||
{
|
||||
sim_kernel_shutdown();
|
||||
sys_timer_quit();
|
||||
/* TODO: quit_everything() */
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
uintptr_t *stackbegin;
|
||||
uintptr_t *stackend;
|
||||
void system_init(void)
|
||||
{
|
||||
/* fake stack, OS manages size (and growth) */
|
||||
volatile uintptr_t stack = 0;
|
||||
stackbegin = stackend = (uintptr_t*) &stack;
|
||||
|
||||
/* disable sleep mode when lid is closed */
|
||||
aptSetSleepAllowed(false);
|
||||
|
||||
sys_console_init();
|
||||
sys_timer_init();
|
||||
|
||||
svcGetThreadPriority(&main_thread_priority, CUR_THREAD_HANDLE);
|
||||
if (main_thread_priority != 0x30) {
|
||||
DEBUGF("warning, main_thread_priority = 0x%x\n", main_thread_priority);
|
||||
}
|
||||
|
||||
/* check for New 3DS model */
|
||||
s64 dummyInfo;
|
||||
is_n3ds = svcGetSystemInfo(&dummyInfo, 0x10001, 0) == 0;
|
||||
|
||||
/* filesystem */
|
||||
sync_RWMutexInit(&file_internal_mrsw);
|
||||
Result res = FSUSER_OpenArchive(&sdmcArchive,
|
||||
ARCHIVE_SDMC,
|
||||
fsMakePath(PATH_ASCII, ""));
|
||||
if (R_FAILED(res)) {
|
||||
DEBUGF("FSUSER_OpenArchive failed\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
mcuhwc_init();
|
||||
cfguInit();
|
||||
}
|
||||
|
||||
|
||||
void system_reboot(void)
|
||||
{
|
||||
sim_thread_exception_wait();
|
||||
}
|
||||
|
||||
void system_exception_wait(void)
|
||||
{
|
||||
system_reboot();
|
||||
}
|
||||
|
||||
int hostfs_init(void)
|
||||
{
|
||||
/* stub */
|
||||
/* romfsInit(); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_STORAGE_FLUSH
|
||||
int hostfs_flush(void)
|
||||
{
|
||||
#ifdef __unix__
|
||||
sync();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_STORAGE_FLUSH */
|
||||
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 by Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef _SYSTEM_SDL_H_
|
||||
#define _SYSTEM_SDL_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdbool.h>
|
||||
#include "config.h"
|
||||
#include "gcc_extensions.h"
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include <3ds/svc.h>
|
||||
#include "sys_timer.h"
|
||||
|
||||
#define HIGHEST_IRQ_LEVEL 1
|
||||
|
||||
int set_irq_level(int level);
|
||||
|
||||
#define disable_irq() \
|
||||
((void)set_irq_level(HIGHEST_IRQ_LEVEL))
|
||||
|
||||
#define enable_irq() \
|
||||
((void)set_irq_level(0))
|
||||
|
||||
#define disable_irq_save() \
|
||||
set_irq_level(HIGHEST_IRQ_LEVEL)
|
||||
|
||||
#define restore_irq(level) \
|
||||
((void)set_irq_level(level))
|
||||
|
||||
#define wait_for_interrupt()
|
||||
|
||||
#include "system-hosted.h"
|
||||
|
||||
void sim_enter_irq_handler(void);
|
||||
void sim_exit_irq_handler(void);
|
||||
void sim_kernel_shutdown(void);
|
||||
void sys_poweroff(void);
|
||||
void sys_handle_argv(int argc, char *argv[]);
|
||||
void gui_message_loop(void);
|
||||
void sim_do_exit(void) NORETURN_ATTR;
|
||||
void sdl_sys_quit(void);
|
||||
|
||||
void mcuhwc_init(void);
|
||||
void mcuhwc_close(void);
|
||||
|
||||
extern bool background; /* True if the background image is enabled */
|
||||
extern bool showremote;
|
||||
extern double display_zoom;
|
||||
extern long start_tick;
|
||||
|
||||
#endif /* _SYSTEM_SDL_H_ */
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#include "system-ctru.h"
|
||||
|
||||
#define NEED_GENERIC_BYTESWAPS
|
||||
|
||||
|
|
@ -1,503 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2006 Dan Everton
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include "autoconf.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h> /* memset() */
|
||||
#include <setjmp.h>
|
||||
#include "system-ctru.h"
|
||||
#include "thread-ctru.h"
|
||||
#include "sys_thread.h"
|
||||
#include "../kernel-internal.h"
|
||||
#include "core_alloc.h"
|
||||
|
||||
/* Define this as 1 to show informational messages that are not errors. */
|
||||
#define THREAD_DEBUGF_ENABLED 0
|
||||
|
||||
#if THREAD_DEBUGF_ENABLED
|
||||
#define THREAD_DEBUGF(...) DEBUGF(__VA_ARGS__)
|
||||
static char __name[sizeof (((struct thread_debug_info *)0)->name)];
|
||||
#define THREAD_GET_NAME(thread) \
|
||||
({ format_thread_name(__name, sizeof (__name), thread); __name; })
|
||||
#else
|
||||
#define THREAD_DEBUGF(...)
|
||||
#define THREAD_GET_NAME(thread)
|
||||
#endif
|
||||
|
||||
#define THREAD_PANICF(str...) \
|
||||
({ fprintf(stderr, str); exit(-1); })
|
||||
|
||||
/* Jump buffers for graceful exit - kernel threads don't stay neatly
|
||||
* in their start routines responding to messages so this is the only
|
||||
* way to get them back in there so they may exit */
|
||||
static jmp_buf thread_jmpbufs[MAXTHREADS];
|
||||
/* this mutex locks out other Rockbox threads while one runs,
|
||||
* that enables us to simulate a cooperative environment even if
|
||||
* the host is preemptive */
|
||||
static RecursiveLock m;
|
||||
#define THREADS_RUN 0
|
||||
#define THREADS_EXIT 1
|
||||
#define THREADS_EXIT_COMMAND_DONE 2
|
||||
static volatile int threads_status = THREADS_RUN;
|
||||
|
||||
extern long start_tick;
|
||||
|
||||
void sim_thread_shutdown(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* This *has* to be a push operation from a thread not in the pool
|
||||
so that they may be dislodged from their blocking calls. */
|
||||
|
||||
/* Tell all threads jump back to their start routines, unlock and exit
|
||||
gracefully - we'll check each one in turn for it's status. Threads
|
||||
_could_ terminate via thread_exit or multiple threads could exit
|
||||
on each unlock but that is safe. */
|
||||
|
||||
/* Do this before trying to acquire lock */
|
||||
threads_status = THREADS_EXIT;
|
||||
|
||||
/* Take control */
|
||||
RecursiveLock_Lock(&m);
|
||||
|
||||
/* Signal all threads on delay or block */
|
||||
for (i = 0; i < MAXTHREADS; i++)
|
||||
{
|
||||
struct thread_entry *thread = __thread_slot_entry(i);
|
||||
if (thread->context.s == NULL)
|
||||
continue;
|
||||
LightSemaphore_Release(thread->context.s, 1);
|
||||
}
|
||||
|
||||
/* Wait for all threads to finish and cleanup old ones. */
|
||||
for (i = 0; i < MAXTHREADS; i++)
|
||||
{
|
||||
struct thread_entry *thread = __thread_slot_entry(i);
|
||||
sysThread *t = thread->context.t;
|
||||
|
||||
if (t != NULL)
|
||||
{
|
||||
RecursiveLock_Unlock(&m);
|
||||
/* Wait for it to finish */
|
||||
sys_wait_thread(t, NULL);
|
||||
/* Relock for next thread signal */
|
||||
RecursiveLock_Lock(&m);
|
||||
/* Already waited and exiting thread would have waited .told,
|
||||
* replacing it with t. */
|
||||
thread->context.told = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait on any previous thread in this location-- could be one not
|
||||
* quite finished exiting but has just unlocked the mutex. If it's
|
||||
* NULL, the call returns immediately.
|
||||
*
|
||||
* See thread_exit below for more information. */
|
||||
sys_wait_thread(thread->context.told, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
RecursiveLock_Unlock(&m);
|
||||
|
||||
/* Signal completion of operation */
|
||||
threads_status = THREADS_EXIT_COMMAND_DONE;
|
||||
}
|
||||
|
||||
void sim_thread_exception_wait(void)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
sys_delay(HZ/10);
|
||||
if (threads_status != THREADS_RUN)
|
||||
thread_exit();
|
||||
}
|
||||
}
|
||||
|
||||
/* A way to yield and leave the threading system for extended periods */
|
||||
void sim_thread_lock(void *me)
|
||||
{
|
||||
RecursiveLock_Lock(&m);
|
||||
__running_self_entry() = (struct thread_entry *)me;
|
||||
|
||||
if (threads_status != THREADS_RUN)
|
||||
thread_exit();
|
||||
}
|
||||
|
||||
void * sim_thread_unlock(void)
|
||||
{
|
||||
struct thread_entry *current = __running_self_entry();
|
||||
RecursiveLock_Unlock(&m);
|
||||
return current;
|
||||
}
|
||||
|
||||
void switch_thread(void)
|
||||
{
|
||||
struct thread_entry *current = __running_self_entry();
|
||||
|
||||
enable_irq();
|
||||
|
||||
switch (current->state)
|
||||
{
|
||||
case STATE_RUNNING:
|
||||
{
|
||||
RecursiveLock_Unlock(&m);
|
||||
/* Any other thread waiting already will get it first */
|
||||
RecursiveLock_Lock(&m);
|
||||
break;
|
||||
} /* STATE_RUNNING: */
|
||||
|
||||
case STATE_BLOCKED:
|
||||
{
|
||||
int oldlevel;
|
||||
|
||||
RecursiveLock_Unlock(&m);
|
||||
sys_sem_wait(current->context.s);
|
||||
RecursiveLock_Lock(&m);
|
||||
|
||||
oldlevel = disable_irq_save();
|
||||
current->state = STATE_RUNNING;
|
||||
restore_irq(oldlevel);
|
||||
break;
|
||||
} /* STATE_BLOCKED: */
|
||||
|
||||
case STATE_BLOCKED_W_TMO:
|
||||
{
|
||||
int result, oldlevel;
|
||||
|
||||
RecursiveLock_Unlock(&m);
|
||||
result = sys_sem_wait_timeout(current->context.s, current->tmo_tick);
|
||||
RecursiveLock_Lock(&m);
|
||||
|
||||
oldlevel = disable_irq_save();
|
||||
|
||||
current->state = STATE_RUNNING;
|
||||
|
||||
if (result == 1)
|
||||
{
|
||||
/* Other signals from an explicit wake could have been made before
|
||||
* arriving here if we timed out waiting for the semaphore. Make
|
||||
* sure the count is reset. */
|
||||
while (sys_sem_value(current->context.s) > 0)
|
||||
sys_sem_try_wait(current->context.s);
|
||||
}
|
||||
|
||||
restore_irq(oldlevel);
|
||||
break;
|
||||
} /* STATE_BLOCKED_W_TMO: */
|
||||
|
||||
case STATE_SLEEPING:
|
||||
{
|
||||
RecursiveLock_Unlock(&m);
|
||||
sys_sem_wait_timeout(current->context.s, current->tmo_tick);
|
||||
RecursiveLock_Lock(&m);
|
||||
current->state = STATE_RUNNING;
|
||||
break;
|
||||
} /* STATE_SLEEPING: */
|
||||
}
|
||||
|
||||
#ifdef BUFLIB_DEBUG_CHECK_VALID
|
||||
core_check_valid();
|
||||
#endif
|
||||
__running_self_entry() = current;
|
||||
|
||||
if (threads_status != THREADS_RUN)
|
||||
thread_exit();
|
||||
}
|
||||
|
||||
void sleep_thread(int ticks)
|
||||
{
|
||||
struct thread_entry *current = __running_self_entry();
|
||||
int rem;
|
||||
|
||||
current->state = STATE_SLEEPING;
|
||||
|
||||
rem = (sys_get_ticks() - start_tick) % (1000/HZ);
|
||||
if (rem < 0)
|
||||
rem = 0;
|
||||
|
||||
current->tmo_tick = (1000/HZ) * ticks + ((1000/HZ)-1) - rem;
|
||||
}
|
||||
|
||||
void block_thread_(struct thread_entry *current, int ticks)
|
||||
{
|
||||
if (ticks < 0)
|
||||
current->state = STATE_BLOCKED;
|
||||
else
|
||||
{
|
||||
current->state = STATE_BLOCKED_W_TMO;
|
||||
current->tmo_tick = (1000/HZ)*ticks;
|
||||
}
|
||||
|
||||
wait_queue_register(current);
|
||||
}
|
||||
|
||||
unsigned int wakeup_thread_(struct thread_entry *thread
|
||||
IF_PRIO(, enum wakeup_thread_protocol proto))
|
||||
{
|
||||
switch (thread->state)
|
||||
{
|
||||
case STATE_BLOCKED:
|
||||
case STATE_BLOCKED_W_TMO:
|
||||
wait_queue_remove(thread);
|
||||
thread->state = STATE_RUNNING;
|
||||
LightSemaphore_Release(thread->context.s, 1);
|
||||
return THREAD_OK;
|
||||
}
|
||||
|
||||
return THREAD_NONE;
|
||||
(void) proto;
|
||||
}
|
||||
|
||||
void thread_thaw(unsigned int thread_id)
|
||||
{
|
||||
struct thread_entry *thread = __thread_id_entry(thread_id);
|
||||
|
||||
if (thread->id == thread_id && thread->state == STATE_FROZEN)
|
||||
{
|
||||
thread->state = STATE_RUNNING;
|
||||
LightSemaphore_Release(thread->context.s, 1);
|
||||
}
|
||||
}
|
||||
|
||||
int runthread(void *data)
|
||||
{
|
||||
/* Cannot access thread variables before locking the mutex as the
|
||||
data structures may not be filled-in yet. */
|
||||
RecursiveLock_Lock(&m);
|
||||
|
||||
struct thread_entry *current = (struct thread_entry *)data;
|
||||
__running_self_entry() = current;
|
||||
|
||||
jmp_buf *current_jmpbuf = &thread_jmpbufs[THREAD_ID_SLOT(current->id)];
|
||||
|
||||
/* Setup jump for exit */
|
||||
if (setjmp(*current_jmpbuf) == 0)
|
||||
{
|
||||
/* Run the thread routine */
|
||||
if (current->state == STATE_FROZEN)
|
||||
{
|
||||
RecursiveLock_Unlock(&m);
|
||||
sys_sem_wait(current->context.s);
|
||||
RecursiveLock_Lock(&m);
|
||||
__running_self_entry() = current;
|
||||
}
|
||||
|
||||
if (threads_status == THREADS_RUN)
|
||||
{
|
||||
current->context.start();
|
||||
THREAD_DEBUGF("Thread Done: %d (%s)\n",
|
||||
THREAD_ID_SLOT(current->id),
|
||||
THREAD_GET_NAME(current));
|
||||
/* Thread routine returned - suicide */
|
||||
}
|
||||
|
||||
thread_exit();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Unlock and exit */
|
||||
RecursiveLock_Unlock(&m);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int create_thread(void (*function)(void),
|
||||
void* stack, size_t stack_size,
|
||||
unsigned flags, const char *name
|
||||
IF_PRIO(, int priority)
|
||||
IF_COP(, unsigned int core))
|
||||
{
|
||||
THREAD_DEBUGF("Creating thread: (%s)\n", name ? name : "");
|
||||
|
||||
struct thread_entry *thread = thread_alloc();
|
||||
if (thread == NULL)
|
||||
{
|
||||
DEBUGF("Failed to find thread slot\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
LightSemaphore *s = (LightSemaphore *) malloc(sizeof(LightSemaphore));
|
||||
if (s == NULL)
|
||||
{
|
||||
DEBUGF("Failed to create semaphore\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
LightSemaphore_Init(s, 0, 255);
|
||||
|
||||
sysThread *t = sys_create_thread(runthread,
|
||||
name,
|
||||
stack_size,
|
||||
thread
|
||||
IF_PRIO(, priority)
|
||||
IF_COP(, core));
|
||||
if (t == NULL)
|
||||
{
|
||||
DEBUGF("Failed to create thread\n");
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
thread->name = name;
|
||||
thread->state = (flags & CREATE_THREAD_FROZEN) ?
|
||||
STATE_FROZEN : STATE_RUNNING;
|
||||
thread->context.start = function;
|
||||
thread->context.t = t;
|
||||
thread->context.s = s;
|
||||
thread->priority = priority;
|
||||
|
||||
THREAD_DEBUGF("New Thread: %lu (%s)\n",
|
||||
(unsigned long)thread->id,
|
||||
THREAD_GET_NAME(thread));
|
||||
|
||||
return thread->id;
|
||||
(void)stack;
|
||||
}
|
||||
|
||||
void thread_exit(void)
|
||||
{
|
||||
struct thread_entry *current = __running_self_entry();
|
||||
|
||||
int oldlevel = disable_irq_save();
|
||||
|
||||
sysThread *t = current->context.t;
|
||||
LightSemaphore *s = current->context.s;
|
||||
|
||||
/* Wait the last thread here and keep this one or ctru will leak it since
|
||||
* it doesn't free its own library allocations unless a wait is performed.
|
||||
* Such behavior guards against the memory being invalid by the time
|
||||
* sys_wait_thread is reached and also against two different threads having
|
||||
* the same pointer. It also makes sys_wait_thread a non-concurrent function.
|
||||
*
|
||||
* However, see more below about sys_kill_thread.
|
||||
*/
|
||||
sys_wait_thread(current->context.told, NULL);
|
||||
|
||||
current->context.t = NULL;
|
||||
current->context.s = NULL;
|
||||
current->context.told = t;
|
||||
|
||||
unsigned int id = current->id;
|
||||
new_thread_id(current);
|
||||
current->state = STATE_KILLED;
|
||||
wait_queue_wake(¤t->queue);
|
||||
|
||||
free(s);
|
||||
|
||||
/* Do a graceful exit - perform the longjmp back into the thread
|
||||
function to return */
|
||||
restore_irq(oldlevel);
|
||||
|
||||
thread_free(current);
|
||||
|
||||
longjmp(thread_jmpbufs[THREAD_ID_SLOT(id)], 1);
|
||||
|
||||
/* This should never and must never be reached - if it is, the
|
||||
* state is corrupted */
|
||||
THREAD_PANICF("thread_exit->K:*R (ID: %d)", id);
|
||||
while (1);
|
||||
}
|
||||
|
||||
void thread_wait(unsigned int thread_id)
|
||||
{
|
||||
struct thread_entry *current = __running_self_entry();
|
||||
struct thread_entry *thread = __thread_id_entry(thread_id);
|
||||
|
||||
if (thread->id == thread_id && thread->state != STATE_KILLED)
|
||||
{
|
||||
block_thread(current, TIMEOUT_BLOCK, &thread->queue, NULL);
|
||||
switch_thread();
|
||||
}
|
||||
}
|
||||
|
||||
int thread_set_priority(unsigned int thread_id, int priority)
|
||||
{
|
||||
struct thread_entry *thread = __thread_id_entry(thread_id);
|
||||
sysThread *t = thread->context.t;
|
||||
thread->priority = sys_set_thread_priority(t, priority);
|
||||
return thread->priority;
|
||||
}
|
||||
|
||||
int thread_get_priority(unsigned int thread_id)
|
||||
{
|
||||
struct thread_entry *thread = __thread_id_entry(thread_id);
|
||||
return thread->priority;
|
||||
}
|
||||
|
||||
/* Initialize threading */
|
||||
void init_threads(void)
|
||||
{
|
||||
RecursiveLock_Init(&m);
|
||||
RecursiveLock_Lock(&m);
|
||||
|
||||
thread_alloc_init();
|
||||
|
||||
struct thread_entry *thread = thread_alloc();
|
||||
if (thread == NULL)
|
||||
{
|
||||
fprintf(stderr, "Main thread alloc failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Slot 0 is reserved for the main thread - initialize it here and
|
||||
then create the thread - it is possible to have a quick, early
|
||||
shutdown try to access the structure. */
|
||||
thread->name = __main_thread_name;
|
||||
thread->state = STATE_RUNNING;
|
||||
thread->context.s = (LightSemaphore *) malloc(sizeof(LightSemaphore));
|
||||
LightSemaphore_Init(thread->context.s, 0, 255);
|
||||
thread->context.t = NULL; /* NULL for the implicit main thread */
|
||||
__running_self_entry() = thread;
|
||||
|
||||
if (thread->context.s == NULL)
|
||||
{
|
||||
fprintf(stderr, "Failed to create main semaphore\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Tell all threads jump back to their start routines, unlock and exit
|
||||
gracefully - we'll check each one in turn for it's status. Threads
|
||||
_could_ terminate via thread_exit or multiple threads could exit
|
||||
on each unlock but that is safe. */
|
||||
|
||||
/* Setup jump for exit */
|
||||
if (setjmp(thread_jmpbufs[THREAD_ID_SLOT(thread->id)]) == 0)
|
||||
{
|
||||
THREAD_DEBUGF("Main Thread: %lu (%s)\n",
|
||||
(unsigned long)thread->id,
|
||||
THREAD_GET_NAME(thread));
|
||||
return;
|
||||
}
|
||||
|
||||
RecursiveLock_Unlock(&m);
|
||||
|
||||
/* Set to 'COMMAND_DONE' when other rockbox threads have exited. */
|
||||
while (threads_status < THREADS_EXIT_COMMAND_DONE)
|
||||
sys_delay(10);
|
||||
|
||||
/* We're the main thead - perform exit - doesn't return. */
|
||||
sim_do_exit();
|
||||
}
|
||||
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __THREAD_CTRU_H__
|
||||
#define __THREAD_CTRU_H__
|
||||
|
||||
/* extra thread functions that only apply when running on hosting platforms */
|
||||
void sim_thread_lock(void *me);
|
||||
void * sim_thread_unlock(void);
|
||||
void sim_thread_exception_wait(void);
|
||||
void sim_thread_shutdown(void); /* Shut down all kernel threads gracefully */
|
||||
|
||||
#endif /* #ifndef __THREAD_CTRU_H__ */
|
||||
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include "sys_timer.h"
|
||||
#include "timer.h"
|
||||
|
||||
static int timer_prio = -1;
|
||||
void (*global_timer_callback)(void);
|
||||
int timerId;
|
||||
|
||||
u32 _timer_callback(u32 interval, void *param){
|
||||
(void)param;
|
||||
global_timer_callback();
|
||||
return(interval);
|
||||
}
|
||||
|
||||
#define cycles_to_miliseconds(cycles) \
|
||||
((int)((1000*cycles)/TIMER_FREQ))
|
||||
|
||||
bool timer_register(int reg_prio, void (*unregister_callback)(void),
|
||||
long cycles, void (*timer_callback)(void))
|
||||
{
|
||||
(void)unregister_callback;
|
||||
if (reg_prio <= timer_prio || cycles == 0)
|
||||
return false;
|
||||
timer_prio=reg_prio;
|
||||
global_timer_callback=timer_callback;
|
||||
timerId=sys_add_timer(cycles_to_miliseconds(cycles), _timer_callback, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool timer_set_period(long cycles)
|
||||
{
|
||||
sys_remove_timer(timerId);
|
||||
timerId=sys_add_timer(cycles_to_miliseconds(cycles), _timer_callback, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void timer_unregister(void)
|
||||
{
|
||||
sys_remove_timer(timerId);
|
||||
timer_prio = -1;
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2025 Mauricio G.
|
||||
*
|
||||
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include "sys_timer.h"
|
||||
#include "timer.h"
|
||||
|
||||
static int timer_prio = -1;
|
||||
void (*global_timer_callback)(void);
|
||||
int timerId;
|
||||
|
||||
u32 _timer_callback(u32 interval, void *param){
|
||||
(void)param;
|
||||
global_timer_callback();
|
||||
return(interval);
|
||||
}
|
||||
|
||||
#define cycles_to_miliseconds(cycles) \
|
||||
((int)((1000*cycles)/TIMER_FREQ))
|
||||
|
||||
bool timer_register(int reg_prio, void (*unregister_callback)(void),
|
||||
long cycles, void (*timer_callback)(void))
|
||||
{
|
||||
(void)unregister_callback;
|
||||
if (reg_prio <= timer_prio || cycles == 0)
|
||||
return false;
|
||||
timer_prio=reg_prio;
|
||||
global_timer_callback=timer_callback;
|
||||
timerId=sys_add_timer(cycles_to_miliseconds(cycles), _timer_callback, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool timer_set_period(long cycles)
|
||||
{
|
||||
sys_remove_timer(timerId);
|
||||
timerId=sys_add_timer(cycles_to_miliseconds(cycles), _timer_callback, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void timer_unregister(void)
|
||||
{
|
||||
sys_remove_timer(timerId);
|
||||
timer_prio = -1;
|
||||
}
|
||||
|
|
@ -58,11 +58,7 @@ const char * handle_special_dirs(const char *dir, unsigned flags,
|
|||
#ifdef WIN32
|
||||
#include "filesystem-win32.h"
|
||||
#else /* !WIN32 */
|
||||
#if defined(CTRU) && !defined(SIMULATOR)
|
||||
#include "filesystem-ctru.h"
|
||||
#else
|
||||
#include "filesystem-unix.h"
|
||||
#endif
|
||||
#endif /* WIN32 */
|
||||
#include "filesystem-hosted.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include <sys/time.h>
|
||||
#if !defined(WIN32)
|
||||
#include <sys/ioctl.h>
|
||||
#if !defined(__APPLE__) && !defined(CTRU)
|
||||
#if !defined(__APPLE__)
|
||||
#include <linux/rtc.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
|
|
@ -49,7 +49,7 @@ int rtc_read_datetime(struct tm *tm)
|
|||
|
||||
int rtc_write_datetime(const struct tm *tm)
|
||||
{
|
||||
#if !defined(WIN32) && !defined(__APPLE__) && !defined(CTRU)
|
||||
#if !defined(WIN32) && !defined(__APPLE__)
|
||||
struct timeval tv;
|
||||
struct tm *tm_time;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,12 +31,6 @@ extern unsigned char plugin_end_addr[];
|
|||
* avoid warning with certain compilers */
|
||||
int _start(void) {return 0;}
|
||||
|
||||
#ifdef CTRU
|
||||
/* dummy undefined symbols */
|
||||
void __aeabi_unwind_cpp_pr0(void) {}
|
||||
struct _reent * _EXFUN(__getreent, (void)) {}
|
||||
#endif
|
||||
|
||||
enum codec_status codec_start(enum codec_entry_call_reason reason)
|
||||
{
|
||||
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
||||
|
|
|
|||
|
|
@ -137,10 +137,6 @@ ifeq ($(ARCH),arch_arm)
|
|||
$(SGCLIB) : CODECFLAGS += -O1
|
||||
$(VGMLIB) : CODECFLAGS += -O1
|
||||
$(WAVPACKLIB) : CODECFLAGS += -O3
|
||||
ifneq (,$(findstring ctru, $(MODELNAME)))
|
||||
# segfault with -O1
|
||||
$(SPCLIB) : CODECFLAGS += -O2
|
||||
endif
|
||||
else ifeq ($(ARCH),arch_m68k)
|
||||
$(CODECLIB) : CODECFLAGS += -O2
|
||||
$(A52LIB) : CODECFLAGS += -O2
|
||||
|
|
|
|||
|
|
@ -1,101 +0,0 @@
|
|||
# __________ __ ___.
|
||||
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
# \/ \/ \/ \/ \/
|
||||
# $Id$
|
||||
#
|
||||
|
||||
# 3ds_rules
|
||||
export DEVKITARM ?= /opt/devkitpro/devkitARM
|
||||
export DEVKITPRO ?= /opt/devkitpro
|
||||
|
||||
PORTLIBS := $(DEVKITPRO)/portlibs/3ds
|
||||
|
||||
CTRULIB ?= $(DEVKITPRO)/libctru
|
||||
|
||||
export PATH := $(DEVKITPRO)/portlibs/3ds/bin:$(PATH)
|
||||
|
||||
# base_rules
|
||||
export SHELL := /usr/bin/env bash
|
||||
|
||||
DEVKITPATH=$(shell echo "$(DEVKITPRO)" | sed -e 's/^\([a-zA-Z]\):/\/\1/')
|
||||
|
||||
export PATH := $(DEVKITPATH)/tools/bin:$(DEVKITPATH)/devkitARM/bin:$(PATH)
|
||||
|
||||
# 3DSX
|
||||
VERSION_MAJOR := 1
|
||||
VERSION_MINOR := 0
|
||||
VERSION_MICRO := 0
|
||||
|
||||
APP_TITLE := rockbox
|
||||
APP_DESCRIPTION := Open Source Jukebox Firmware
|
||||
APP_AUTHOR := rockbox.org
|
||||
APP_ICON := $(ROOTDIR)/packaging/ctru/res/icon.png
|
||||
|
||||
# CIA
|
||||
BANNER_AUDIO := $(ROOTDIR)/packaging/ctru/res/banner.wav
|
||||
BANNER_IMAGE := $(ROOTDIR)/packaging/ctru/res/banner.cgfx
|
||||
RSF_PATH := $(ROOTDIR)/packaging/ctru/res/app.rsf
|
||||
#LOGO := $(ROOTDIR)/packaging/ctru/logo.lz11
|
||||
UNIQUE_ID := 0xCB001
|
||||
PRODUCT_CODE := CTR-ROCKBOX
|
||||
ICON_FLAGS := nosavebackups,visible
|
||||
|
||||
# CIA Configuration
|
||||
USE_ON_SD := true
|
||||
APP_ENCRYPTED := false
|
||||
CATEGORY := Application
|
||||
USE_ON_SD := true
|
||||
MEMORY_TYPE := Application
|
||||
SYSTEM_MODE := 64MB
|
||||
SYSTEM_MODE_EXT := Legacy
|
||||
CPU_SPEED := 268MHz
|
||||
ENABLE_L2_CACHE := false
|
||||
|
||||
.SECONDEXPANSION: # $$(OBJ) is not populated until after this
|
||||
|
||||
MAKEROM ?= makerom
|
||||
MAKEROM_ARGS := -elf "$(BINARY).elf" -rsf "$(RSF_PATH)" -banner "$(BUILDDIR)/banner.bnr" -icon "$(BUILDDIR)/icon.icn" -DAPP_TITLE="$(APP_TITLE)" -DAPP_PRODUCT_CODE="$(PRODUCT_CODE)" -DAPP_UNIQUE_ID="$(UNIQUE_ID)" -DAPP_ENCRYPTED="$(APP_ENCRYPTED)" -DAPP_SYSTEM_MODE="$(SYSTEM_MODE)" -DAPP_SYSTEM_MODE_EXT="$(SYSTEM_MODE_EXT)" -DAPP_CATEGORY="$(CATEGORY)" -DAPP_USE_ON_SD="$(USE_ON_SD)" -DAPP_MEMORY_TYPE="$(MEMORY_TYPE)" -DAPP_CPU_SPEED="$(CPU_SPEED)" -DAPP_ENABLE_L2_CACHE="$(ENABLE_L2_CACHE)"
|
||||
MAKEROM_ARGS += -major $(VERSION_MAJOR) -minor $(VERSION_MINOR) -micro $(VERSION_MICRO)
|
||||
|
||||
ifneq ($(strip $(LOGO)),)
|
||||
MAKEROM_ARGS += -logo "$(LOGO)"
|
||||
endif
|
||||
ifneq ($(strip $(ROMFS)),)
|
||||
MAKEROM_ARGS += -DAPP_ROMFS="$(ROMFS)"
|
||||
endif
|
||||
|
||||
BANNERTOOL ?= bannertool
|
||||
|
||||
ifeq ($(suffix $(BANNER_IMAGE)),.cgfx)
|
||||
BANNER_IMAGE_ARG := -ci
|
||||
else
|
||||
BANNER_IMAGE_ARG := -i
|
||||
endif
|
||||
|
||||
ifeq ($(suffix $(BANNER_AUDIO)),.cwav)
|
||||
BANNER_AUDIO_ARG := -ca
|
||||
else
|
||||
BANNER_AUDIO_ARG := -a
|
||||
endif
|
||||
|
||||
# main binary
|
||||
$(BUILDDIR)/$(BINARY): $$(OBJ) $(FIRMLIB) $(VOICESPEEXLIB) $(CORE_LIBS)
|
||||
ifeq ($(UNAME), Darwin)
|
||||
$(call PRINTS,LD $(BINARY))$(CC) -o $@ $^ $(LDOPTS) $(GLOBAL_LDOPTS) -Wl,$(LDMAP_OPT),$(BUILDDIR)/rockbox.map
|
||||
else
|
||||
$(call PRINTS,LD $(BINARY))$(CC) -o $@ -Wl,--start-group $^ -Wl,--end-group $(LDOPTS) $(GLOBAL_LDOPTS) \
|
||||
-Wl,$(LDMAP_OPT),$(BUILDDIR)/rockbox-.map
|
||||
@mv $(BINARY) $(BINARY).elf
|
||||
smdhtool --create "$(APP_TITLE)" "$(APP_DESCRIPTION)" "$(APP_AUTHOR)" $(APP_ICON) "rockbox.smdh"
|
||||
3dsxtool $(BINARY).elf $(BINARY).3dsx --smdh="rockbox.smdh"
|
||||
$(BANNERTOOL) makebanner $(BANNER_IMAGE_ARG) "$(BANNER_IMAGE)" $(BANNER_AUDIO_ARG) "$(BANNER_AUDIO)" -o "$(BUILDDIR)/banner.bnr"
|
||||
$(BANNERTOOL) makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i "$(APP_ICON)" -f "$(ICON_FLAGS)" -o "$(BUILDDIR)/icon.icn"
|
||||
$(MAKEROM) -f cia -o "$(BINARY).cia" -target t -exefslogo $(MAKEROM_ARGS)
|
||||
ifndef DEBUG
|
||||
$(SILENT)rm $(BINARY).elf
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
@ -1,286 +0,0 @@
|
|||
BasicInfo:
|
||||
Title : $(APP_TITLE)
|
||||
ProductCode : $(APP_PRODUCT_CODE)
|
||||
Logo : Homebrew
|
||||
|
||||
RomFs:
|
||||
RootPath: $(APP_ROMFS)
|
||||
|
||||
TitleInfo:
|
||||
Category : $(APP_CATEGORY)
|
||||
UniqueId : $(APP_UNIQUE_ID)
|
||||
|
||||
Option:
|
||||
UseOnSD : $(APP_USE_ON_SD) # 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 : $(APP_ENCRYPTED) # Enables encryption for NCCH and CIA
|
||||
EnableCompress : true # Compresses where applicable (currently only exefs:/.code)
|
||||
|
||||
AccessControlInfo:
|
||||
CoreVersion : 2
|
||||
|
||||
# Exheader Format Version
|
||||
DescVersion : 2
|
||||
|
||||
# Minimum Required Kernel Version (below is for 4.5.0)
|
||||
ReleaseKernelMajor : "02"
|
||||
ReleaseKernelMinor : "33"
|
||||
|
||||
# ExtData
|
||||
UseExtSaveData : false # enables ExtData
|
||||
#ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId
|
||||
|
||||
# FS:USER Archive Access Permissions
|
||||
# Uncomment as required
|
||||
FileSystemAccess:
|
||||
- CategorySystemApplication
|
||||
- CategoryHardwareCheck
|
||||
- CategoryFileSystemTool
|
||||
- Debug
|
||||
- TwlCardBackup
|
||||
- TwlNandData
|
||||
- Boss
|
||||
- DirectSdmc
|
||||
- Core
|
||||
- CtrNandRo
|
||||
- CtrNandRw
|
||||
- CtrNandRoWrite
|
||||
- CategorySystemSettings
|
||||
- CardBoard
|
||||
- ExportImportIvs
|
||||
- DirectSdmcWrite
|
||||
- SwitchCleanup
|
||||
- SaveDataMove
|
||||
- Shop
|
||||
- Shell
|
||||
- CategoryHomeMenu
|
||||
- SeedDB
|
||||
IoAccessControl:
|
||||
- FsMountNand
|
||||
- FsMountNandRoWrite
|
||||
- FsMountTwln
|
||||
- FsMountWnand
|
||||
- FsMountCardSpi
|
||||
- UseSdif3
|
||||
- CreateSeed
|
||||
- UseCardSpi
|
||||
|
||||
# Process Settings
|
||||
MemoryType : $(APP_MEMORY_TYPE) # Application/System/Base
|
||||
SystemMode : $(APP_SYSTEM_MODE) # 64MB(Default)/96MB/80MB/72MB/32MB
|
||||
IdealProcessor : 0
|
||||
AffinityMask : 1
|
||||
Priority : 16
|
||||
MaxCpu : 0x9E # Default
|
||||
HandleTableSize : 0x200
|
||||
DisableDebug : false
|
||||
EnableForceDebug : false
|
||||
CanWriteSharedPage : true
|
||||
CanUsePrivilegedPriority : false
|
||||
CanUseNonAlphabetAndNumber : true
|
||||
PermitMainFunctionArgument : true
|
||||
CanShareDeviceMemory : true
|
||||
RunnableOnSleep : false
|
||||
SpecialMemoryArrange : true
|
||||
|
||||
# New3DS Exclusive Process Settings
|
||||
SystemModeExt : $(APP_SYSTEM_MODE_EXT) # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode
|
||||
CpuSpeed : $(APP_CPU_SPEED) # 268MHz(Default)/804MHz
|
||||
EnableL2Cache : $(APP_ENABLE_L2_CACHE) # false(default)/true
|
||||
CanAccessCore2 : true
|
||||
|
||||
# Virtual Address Mappings
|
||||
IORegisterMapping:
|
||||
- 1ff00000-1ff7ffff # DSP memory
|
||||
MemoryMapping:
|
||||
- 1f000000-1f5fffff:r # VRAM
|
||||
|
||||
# Accessible SVCs, <Name>:<ID>
|
||||
SystemCallAccess:
|
||||
ControlMemory: 1
|
||||
QueryMemory: 2
|
||||
ExitProcess: 3
|
||||
GetProcessAffinityMask: 4
|
||||
SetProcessAffinityMask: 5
|
||||
GetProcessIdealProcessor: 6
|
||||
SetProcessIdealProcessor: 7
|
||||
CreateThread: 8
|
||||
ExitThread: 9
|
||||
SleepThread: 10
|
||||
GetThreadPriority: 11
|
||||
SetThreadPriority: 12
|
||||
GetThreadAffinityMask: 13
|
||||
SetThreadAffinityMask: 14
|
||||
GetThreadIdealProcessor: 15
|
||||
SetThreadIdealProcessor: 16
|
||||
GetCurrentProcessorNumber: 17
|
||||
Run: 18
|
||||
CreateMutex: 19
|
||||
ReleaseMutex: 20
|
||||
CreateSemaphore: 21
|
||||
ReleaseSemaphore: 22
|
||||
CreateEvent: 23
|
||||
SignalEvent: 24
|
||||
ClearEvent: 25
|
||||
CreateTimer: 26
|
||||
SetTimer: 27
|
||||
CancelTimer: 28
|
||||
ClearTimer: 29
|
||||
CreateMemoryBlock: 30
|
||||
MapMemoryBlock: 31
|
||||
UnmapMemoryBlock: 32
|
||||
CreateAddressArbiter: 33
|
||||
ArbitrateAddress: 34
|
||||
CloseHandle: 35
|
||||
WaitSynchronization1: 36
|
||||
WaitSynchronizationN: 37
|
||||
SignalAndWait: 38
|
||||
DuplicateHandle: 39
|
||||
GetSystemTick: 40
|
||||
GetHandleInfo: 41
|
||||
GetSystemInfo: 42
|
||||
GetProcessInfo: 43
|
||||
GetThreadInfo: 44
|
||||
ConnectToPort: 45
|
||||
SendSyncRequest1: 46
|
||||
SendSyncRequest2: 47
|
||||
SendSyncRequest3: 48
|
||||
SendSyncRequest4: 49
|
||||
SendSyncRequest: 50
|
||||
OpenProcess: 51
|
||||
OpenThread: 52
|
||||
GetProcessId: 53
|
||||
GetProcessIdOfThread: 54
|
||||
GetThreadId: 55
|
||||
GetResourceLimit: 56
|
||||
GetResourceLimitLimitValues: 57
|
||||
GetResourceLimitCurrentValues: 58
|
||||
GetThreadContext: 59
|
||||
Break: 60
|
||||
OutputDebugString: 61
|
||||
ControlPerformanceCounter: 62
|
||||
CreatePort: 71
|
||||
CreateSessionToPort: 72
|
||||
CreateSession: 73
|
||||
AcceptSession: 74
|
||||
ReplyAndReceive1: 75
|
||||
ReplyAndReceive2: 76
|
||||
ReplyAndReceive3: 77
|
||||
ReplyAndReceive4: 78
|
||||
ReplyAndReceive: 79
|
||||
BindInterrupt: 80
|
||||
UnbindInterrupt: 81
|
||||
InvalidateProcessDataCache: 82
|
||||
StoreProcessDataCache: 83
|
||||
FlushProcessDataCache: 84
|
||||
StartInterProcessDma: 85
|
||||
StopDma: 86
|
||||
GetDmaState: 87
|
||||
RestartDma: 88
|
||||
DebugActiveProcess: 96
|
||||
BreakDebugProcess: 97
|
||||
TerminateDebugProcess: 98
|
||||
GetProcessDebugEvent: 99
|
||||
ContinueDebugEvent: 100
|
||||
GetProcessList: 101
|
||||
GetThreadList: 102
|
||||
GetDebugThreadContext: 103
|
||||
SetDebugThreadContext: 104
|
||||
QueryDebugProcessMemory: 105
|
||||
ReadProcessMemory: 106
|
||||
WriteProcessMemory: 107
|
||||
SetHardwareBreakPoint: 108
|
||||
GetDebugThreadParam: 109
|
||||
ControlProcessMemory: 112
|
||||
MapProcessMemory: 113
|
||||
UnmapProcessMemory: 114
|
||||
CreateCodeSet: 115
|
||||
CreateProcess: 117
|
||||
TerminateProcess: 118
|
||||
SetProcessResourceLimits: 119
|
||||
CreateResourceLimit: 120
|
||||
SetResourceLimitValues: 121
|
||||
AddCodeSegment: 122
|
||||
Backdoor: 123
|
||||
KernelSetState: 124
|
||||
QueryProcessMemory: 125
|
||||
|
||||
# Service List
|
||||
# Maximum 34 services (32 if firmware is prior to 9.6.0)
|
||||
ServiceAccessControl:
|
||||
- APT:U
|
||||
- ac:u
|
||||
- am:net
|
||||
- boss:U
|
||||
- cam:u
|
||||
- cecd:u
|
||||
- cfg:nor
|
||||
- cfg:u
|
||||
- csnd:SND
|
||||
- dsp::DSP
|
||||
- frd:u
|
||||
- fs:USER
|
||||
- gsp::Gpu
|
||||
- gsp::Lcd
|
||||
- hid:USER
|
||||
- http:C
|
||||
- ir:rst
|
||||
- ir:u
|
||||
- ir:USER
|
||||
- mic:u
|
||||
- mcu::HWC
|
||||
- ndm:u
|
||||
- news:s
|
||||
- nwm::EXT
|
||||
- nwm::UDS
|
||||
- ptm:sysm
|
||||
- ptm:u
|
||||
- pxi:dev
|
||||
- soc:U
|
||||
- ssl:C
|
||||
- y2r:u
|
||||
|
||||
|
||||
SystemControlInfo:
|
||||
SaveDataSize: 0KB # Change if the app uses savedata
|
||||
RemasterVersion: $(APP_VERSION_MAJOR)
|
||||
StackSize: 0x40000
|
||||
|
||||
# Modules that run services listed above should be included below
|
||||
# Maximum 48 dependencies
|
||||
# <module name>:<module titleid>
|
||||
Dependency:
|
||||
ac: 0x0004013000002402
|
||||
#act: 0x0004013000003802
|
||||
am: 0x0004013000001502
|
||||
boss: 0x0004013000003402
|
||||
camera: 0x0004013000001602
|
||||
cecd: 0x0004013000002602
|
||||
cfg: 0x0004013000001702
|
||||
codec: 0x0004013000001802
|
||||
csnd: 0x0004013000002702
|
||||
dlp: 0x0004013000002802
|
||||
dsp: 0x0004013000001a02
|
||||
friends: 0x0004013000003202
|
||||
gpio: 0x0004013000001b02
|
||||
gsp: 0x0004013000001c02
|
||||
hid: 0x0004013000001d02
|
||||
http: 0x0004013000002902
|
||||
i2c: 0x0004013000001e02
|
||||
ir: 0x0004013000003302
|
||||
mcu: 0x0004013000001f02
|
||||
mic: 0x0004013000002002
|
||||
ndm: 0x0004013000002b02
|
||||
news: 0x0004013000003502
|
||||
#nfc: 0x0004013000004002
|
||||
nim: 0x0004013000002c02
|
||||
nwm: 0x0004013000002d02
|
||||
pdn: 0x0004013000002102
|
||||
ps: 0x0004013000003102
|
||||
ptm: 0x0004013000002202
|
||||
#qtm: 0x0004013020004202
|
||||
ro: 0x0004013000003702
|
||||
socket: 0x0004013000002e02
|
||||
spi: 0x0004013000002302
|
||||
ssl: 0x0004013000002f02
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6 KiB |
|
|
@ -1,7 +0,0 @@
|
|||
<shortcut>
|
||||
<executable>/3ds/.rockbox/rockbox.3dsx</executable>
|
||||
<icon>.rockbox/icon.icn</icon>
|
||||
<name>rockbox</name>
|
||||
<description>Open Source Jukebox Firmware</description>
|
||||
<author>rockbox.org</author>
|
||||
</shortcut>
|
||||
|
|
@ -424,11 +424,6 @@ sub buildzip {
|
|||
glob_mkdir("$temp_dir/libertas");
|
||||
glob_copy("$ROOT/firmware/drivers/libertas/firmware/*", "$temp_dir/libertas/");
|
||||
}
|
||||
# add hbmenu shortcut's icon and 3dsx executable
|
||||
if ($modelname =~ /ctru/) {
|
||||
glob_copy("icon.icn", "$temp_dir/");
|
||||
glob_copy("rockbox.3dsx", "$temp_dir/");
|
||||
}
|
||||
|
||||
glob_mkdir("$temp_dir/langs");
|
||||
glob_mkdir("$temp_dir/rocks");
|
||||
|
|
@ -745,18 +740,6 @@ sub runone {
|
|||
move(".rockbox", $rbdir);
|
||||
print "mv .rockbox $rbdir\n" if $verbose;
|
||||
}
|
||||
|
||||
# add hbmenu shortcut and cia file to zip file
|
||||
if ($modelname =~ /ctru/) {
|
||||
move("rockbox.cia", "3ds");
|
||||
copy("$ROOT/packaging/ctru/rockbox.xml", "3ds");
|
||||
|
||||
system("$ziptool -u $output 3ds/rockbox.xml $target >/dev/null");
|
||||
print "$ziptool $output $ROOT/packaging/ctru/rockbox.xml $target >/dev/null\n" if $verbose;
|
||||
system("$ziptool -u $output 3ds/rockbox.cia $target >/dev/null");
|
||||
print "$ziptool $output rockbox.cia $target >/dev/null\n" if $verbose;
|
||||
}
|
||||
|
||||
system("$ziptool $output $rbdir $target >/dev/null");
|
||||
print "$ziptool $output $rbdir $target >/dev/null\n" if $verbose;
|
||||
rmtree("$rbdir");
|
||||
|
|
|
|||
112
tools/configure
vendored
112
tools/configure
vendored
|
|
@ -1009,90 +1009,6 @@ rgnanocc () {
|
|||
rbdir="/FunKey/.rockbox"
|
||||
}
|
||||
|
||||
devkitarmcc () {
|
||||
if [ -z "$DEVKITPRO" ]; then
|
||||
echo "ERROR: You need a devkitPro toolchain and libraries installed"
|
||||
echo "and have the DEVKITPRO environment variable point to the root"
|
||||
echo "of the devkitPro installation."
|
||||
echo "More info at https://devkitpro.org/wiki/Getting_Started"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -z "$DEVKITARM" ]; then
|
||||
echo "ERROR: You need devkitARM toolchain installed and have the DEVKITARM"
|
||||
echo "environment variable point to the root directory of the sdk."
|
||||
exit
|
||||
fi
|
||||
|
||||
# check for additional dependencies
|
||||
if [ ! -e "$DEVKITPRO/portlibs/3ds/lib/libCTRL.a" ]; then
|
||||
echo "ERROR: You need to install libCTRL utility library."
|
||||
echo "https://github.com/kynex7510/CTRL"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -e "$DEVKITPRO/portlibs/3ds/lib/libdl.a" ]; then
|
||||
echo "ERROR: You need to install libdl implementation for 3ds (CTRDL)"
|
||||
echo "https://github.com/kynex7510/CTRDL"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -n "`findtool makerom`" ]; then
|
||||
echo "ERROR: makerom not found, please install and run configure again."
|
||||
echo "https://github.com/3DSGuy/Project_CTR"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -n "`findtool bannertool`" ]; then
|
||||
echo "ERROR: bannertool not found, please install and run configure again."
|
||||
echo "https://github.com/carstene1ns/3ds-bannertool"
|
||||
exit
|
||||
fi
|
||||
|
||||
arch="arm"
|
||||
arch_version="6"
|
||||
arch_profile="classic"
|
||||
|
||||
CC=$DEVKITARM/bin/arm-none-eabi-gcc
|
||||
CPP=$DEVKITARM/bin/arm-none-eabi-cpp
|
||||
LD=$DEVKITARM/bin/arm-none-eabi-ld
|
||||
AR=$DEVKITARM/bin/arm-none-eabi-gcc-ar
|
||||
AS=$DEVKITARM/bin/arm-none-eabi-as
|
||||
OC=$DEVKITARM/bin/arm-none-eabi-objcopy
|
||||
WINDRES=windres
|
||||
DLLTOOL=dlltool
|
||||
DLLWRAP=dllwrap
|
||||
RANLIB=$DEVKITARM/bin/arm-none-eabi-gcc-ranlib
|
||||
|
||||
if [ "yes" = "$use_debug" ]; then
|
||||
GCCOPTS=`echo $GCCOPTS | sed -e s/\ -Os/\ -Og/`
|
||||
fi
|
||||
|
||||
GCCOPTS="$GCCOPTS -fno-builtin -g -Wno-unused-result"
|
||||
GCCOPTS="$GCCOPTS -I$DEVKITPRO/libctru/include -I$DEVKITPRO/portlibs/3ds/include"
|
||||
GCCOPTS="$GCCOPTS -mword-relocations -ffunction-sections -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft"
|
||||
GCCOPTS="$GCCOPTS -D_GNU_SOURCE=1 -D_REENTRANT -masm-syntax-unified"
|
||||
|
||||
SHARED_LDFLAGS="-shared"
|
||||
SHARED_CFLAGS="-fPIC -fvisibility=hidden"
|
||||
|
||||
LDOPTS="-specs=3dsx.specs -L$DEVKITPRO/libctru/lib -L$DEVKITPRO/portlibs/3ds/lib -ldl -lCTRL -lctru -lm"
|
||||
GLOBAL_LDOPTS="$GLOBAL_LDOPTS -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft"
|
||||
|
||||
# let's allow building shared libraries even if unresolved symbols are found,
|
||||
# CTRDL (libdl) implementation will use a custom resolver for resolve symbols at runtime.
|
||||
# TODO: use ResGen command to automatically detect unresolved symbols.
|
||||
GLOBAL_LDOPTS=`echo $GLOBAL_LDOPTS | sed -e s/\-Wl,-z,defs//`
|
||||
|
||||
# devkitarm already defines getcwd
|
||||
GLOBAL_LDOPTS="$GLOBAL_LDOPTS -Wl,-wrap,getcwd"
|
||||
|
||||
ARG_PREFIX="romfs"
|
||||
|
||||
extradefines="-D__3DS__"
|
||||
rbdir="/3ds/.rockbox"
|
||||
}
|
||||
|
||||
|
||||
do_bootloader() {
|
||||
appsdir='$(ROOTDIR)/bootloader'
|
||||
|
|
@ -1863,9 +1779,6 @@ cat <<EOF
|
|||
(hw4 bl only)
|
||||
==Echo project== ==Surfans==
|
||||
270) Echo R1 (WIP) 280) F28 (WIP)
|
||||
|
||||
==Nintendo==
|
||||
290) Nintendo 3DS (WIP)
|
||||
EOF
|
||||
|
||||
buildfor=`input`;
|
||||
|
|
@ -4558,28 +4471,6 @@ fi
|
|||
sysfontbl="16-Terminus"
|
||||
;;
|
||||
|
||||
290|ctru)
|
||||
target_id=122
|
||||
application="yes"
|
||||
modelname="ctru"
|
||||
app_type="ctru-app"
|
||||
target="CTRU"
|
||||
memory=16
|
||||
uname=`uname`
|
||||
devkitarmcc
|
||||
tool="cp "
|
||||
boottool="cp "
|
||||
bmp2rb_mono="$rootdir/tools/bmp2rb -f 0"
|
||||
bmp2rb_native="$rootdir/tools/bmp2rb -f 4"
|
||||
output="rockbox"
|
||||
bootoutput="rockbox"
|
||||
appextra="recorder:gui"
|
||||
plugins="no"
|
||||
t_cpu="hosted"
|
||||
t_manufacturer="ctru"
|
||||
t_model="app"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Please select a supported target platform!"
|
||||
exit 7
|
||||
|
|
@ -5133,9 +5024,6 @@ if test -n "$t_cpu"; then
|
|||
TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/hosted"
|
||||
elif [ "$t_manufacturer" = "ibasso" ]; then
|
||||
TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/hosted/ibasso/tinyalsa/include"
|
||||
elif [ "$application" = "yes" ] && [ "$t_manufacturer" = "ctru" ]; then
|
||||
TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/hosted/ctru/lib"
|
||||
TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/hosted/ctru/lib/bfile"
|
||||
fi
|
||||
|
||||
TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/$t_cpu/$t_manufacturer"
|
||||
|
|
|
|||
|
|
@ -40,8 +40,6 @@ ifndef APP_TYPE
|
|||
objcopy = $(OC) $(if $(filter yes, $(USE_ELF)), -S -x, -O binary) $(1) $(2) # objcopy native
|
||||
else ifneq (,$(findstring sdl-sim,$(APP_TYPE)))
|
||||
objcopy = cp $(1) $(1).tmp;mv -f $(1).tmp $(2) # objcopy simulator
|
||||
else ifneq (,$(findstring ctru,$(MODELNAME))) # 3dsxtool requires symbols
|
||||
objcopy = cp $(1) $(1).tmp;mv -f $(1).tmp $(2)
|
||||
else
|
||||
ifdef DEBUG
|
||||
objcopy = cp $(1) $(1).tmp;mv -f $(1).tmp $(2) # objcopy hosted (DEBUG)
|
||||
|
|
|
|||
|
|
@ -171,9 +171,6 @@ else # core
|
|||
ifneq (,$(findstring rgnano, $(MODELNAME)))
|
||||
include $(ROOTDIR)/packaging/rgnano/rgnano.make
|
||||
endif
|
||||
ifneq (,$(findstring ctru, $(APP_TYPE)))
|
||||
include $(ROOTDIR)/packaging/ctru/ctru.make
|
||||
endif
|
||||
|
||||
endif # bootloader
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue