Compare commits
2 Commits
877e40b073
...
dev70
| Author | SHA1 | Date | |
|---|---|---|---|
|
54a6dfddad
|
|||
|
e3683a12b7
|
67
.vscode/tasks.json
vendored
67
.vscode/tasks.json
vendored
@@ -1,6 +1,24 @@
|
|||||||
{
|
{
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Clean build artifacts",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "rm",
|
||||||
|
"args": [
|
||||||
|
"-rf",
|
||||||
|
"${workspaceFolder}/build/*",
|
||||||
|
"${workspaceFolder}/output/3ds-arm/*"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
"problemMatcher": [],
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "shared"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Build 3DS ROM",
|
"label": "Build 3DS ROM",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
@@ -12,10 +30,59 @@
|
|||||||
"problemMatcher": [
|
"problemMatcher": [
|
||||||
"$gcc"
|
"$gcc"
|
||||||
],
|
],
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
"isDefault": true
|
"isDefault": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Build (full)",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make",
|
||||||
|
"args": [],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
],
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "shared"
|
||||||
|
},
|
||||||
|
"group": "build"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Serve (dev)",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm",
|
||||||
|
"args": [ "run", "dev" ],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/server"
|
||||||
|
},
|
||||||
|
"isBackground": true,
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "dedicated"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Serve (prod)",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm",
|
||||||
|
"args": [ "start" ],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/server"
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "shared"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#define mice_main_h
|
#define mice_main_h
|
||||||
|
|
||||||
/* Application version */
|
/* Application version */
|
||||||
#define MICE_VERSION "dev63"
|
#define MICE_VERSION "dev68"
|
||||||
|
|
||||||
/* Default folder */
|
/* Default folder */
|
||||||
#define DEFAULT_DIR "sdmc:/"
|
#define DEFAULT_DIR "sdmc:/"
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ struct metadata_t
|
|||||||
char title[METADATA_TITLE_MAX];
|
char title[METADATA_TITLE_MAX];
|
||||||
char artist[METADATA_ARTIST_MAX];
|
char artist[METADATA_ARTIST_MAX];
|
||||||
char album[METADATA_ALBUM_MAX];
|
char album[METADATA_ALBUM_MAX];
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
## audio.wav
|
## audio.wav
|
||||||
|
|
||||||
Ctrmus uses a modified version of [Rad Adventure by Scott Holmes](http://freemusicarchive.org/music/Scott_Holmes/~/Rad_Adventure) of which is licensed under a [Attribution-NonCommercial License](https://creativecommons.org/licenses/by-nc/4.0/).
|
Mice uses [Internal Monologues by Jon Shuemaker](https://freemusicarchive.org/music/jon-shuemaker/anoka-vol2/internal-monologues/) of which is licensed under a [Attribution-NonCommercial 4.0 International License.](https://creativecommons.org/licenses/by-nc/4.0/).
|
||||||
Permission for use in ctrmus is granted.
|
Permission for use in mice is granted.
|
||||||
|
|||||||
BIN
meta/audio.wav
BIN
meta/audio.wav
Binary file not shown.
33
source/gui.c
33
source/gui.c
@@ -83,6 +83,12 @@ void guiClearBottomScreen(void)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw a simple text string at specified position
|
* Draw a simple text string at specified position
|
||||||
|
* @param screen Screen to draw on (GFX_TOP or GFX_BOTTOM)
|
||||||
|
* @param x X position
|
||||||
|
* @param y Y position
|
||||||
|
* @param text Text string to draw
|
||||||
|
* @param color Text color
|
||||||
|
* @param scale Text scale
|
||||||
*/
|
*/
|
||||||
void guiDrawText(gfxScreen_t screen, float x, float y, const char* text, u32 color, float scale)
|
void guiDrawText(gfxScreen_t screen, float x, float y, const char* text, u32 color, float scale)
|
||||||
{
|
{
|
||||||
@@ -101,6 +107,8 @@ void guiDrawText(gfxScreen_t screen, float x, float y, const char* text, u32 col
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Display metadata on the top screen
|
* Display metadata on the top screen
|
||||||
|
* @param metadata Pointer to metadata structure to display
|
||||||
|
* @param filename Filename to display if no title is available
|
||||||
*/
|
*/
|
||||||
void guiDisplayMetadata(struct metadata_t* metadata, const char* filename)
|
void guiDisplayMetadata(struct metadata_t* metadata, const char* filename)
|
||||||
{
|
{
|
||||||
@@ -185,6 +193,9 @@ void guiDisplayMetadata(struct metadata_t* metadata, const char* filename)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Display log messages on the top screen
|
* Display log messages on the top screen
|
||||||
|
* @param messages Array of log message strings
|
||||||
|
* @param count Number of messages in the array
|
||||||
|
* @param scroll Index of first visible message for scrolling
|
||||||
*/
|
*/
|
||||||
void guiDisplayLog(const char** messages, int count, int scroll)
|
void guiDisplayLog(const char** messages, int count, int scroll)
|
||||||
{
|
{
|
||||||
@@ -215,6 +226,10 @@ void guiDisplayLog(const char** messages, int count, int scroll)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Display file list on the bottom screen
|
* Display file list on the bottom screen
|
||||||
|
* @param files Array of file/folder names
|
||||||
|
* @param count Number of entries in the array
|
||||||
|
* @param selected Index of currently selected entry
|
||||||
|
* @param scroll Index of first visible entry for scrolling
|
||||||
*/
|
*/
|
||||||
void guiDisplayFileList(const char** files, int count, int selected, int scroll)
|
void guiDisplayFileList(const char** files, int count, int selected, int scroll)
|
||||||
{
|
{
|
||||||
@@ -269,6 +284,10 @@ void guiDisplayFileList(const char** files, int count, int selected, int scroll)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Display playback controls and status on the top screen
|
* Display playback controls and status on the top screen
|
||||||
|
* @param isPlaying Whether playback is active
|
||||||
|
* @param isPaused Whether playback is paused
|
||||||
|
* @param position Current playback position in seconds
|
||||||
|
* @param duration Total duration in seconds
|
||||||
*/
|
*/
|
||||||
void guiDisplayPlaybackStatus(bool isPlaying, bool isPaused, float position, float duration)
|
void guiDisplayPlaybackStatus(bool isPlaying, bool isPaused, float position, float duration)
|
||||||
{
|
{
|
||||||
@@ -319,6 +338,7 @@ void guiDisplayPlaybackStatus(bool isPlaying, bool isPaused, float position, flo
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Display version text and credits at bottom of bottom screen
|
* Display version text and credits at bottom of bottom screen
|
||||||
|
* @param version Version string to display
|
||||||
*/
|
*/
|
||||||
void guiDisplayVersion(const char* version)
|
void guiDisplayVersion(const char* version)
|
||||||
{
|
{
|
||||||
@@ -331,16 +351,20 @@ void guiDisplayVersion(const char* version)
|
|||||||
C2D_TextBufClear(textBuf);
|
C2D_TextBufClear(textBuf);
|
||||||
|
|
||||||
/* Display "mice - by sillyangel" at bottom center */
|
/* Display "mice - by sillyangel" at bottom center */
|
||||||
const char* credits = "mice - by sillyangel";
|
char credits[64];
|
||||||
|
if(version && version[0])
|
||||||
|
snprintf(credits, sizeof(credits), "mice %s - by sillyangel", version);
|
||||||
|
else
|
||||||
|
snprintf(credits, sizeof(credits), "mice - by sillyangel");
|
||||||
C2D_TextParse(&text, textBuf, credits);
|
C2D_TextParse(&text, textBuf, credits);
|
||||||
C2D_TextOptimize(&text);
|
C2D_TextOptimize(&text);
|
||||||
C2D_DrawText(&text, C2D_WithColor, 80.0f, 220.0f, 0.5f, 0.45f, 0.45f, GUI_COLOR_TEXT_DIM);
|
C2D_DrawText(&text, C2D_WithColor, 80.0f, 220.0f, 0.5f, 0.45f, 0.45f, GUI_COLOR_TEXT_DIM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Display progress bar on the top screen
|
||||||
/**
|
* @param position Current playback position in seconds
|
||||||
* Display progress bar on top screen
|
* @param duration Total duration in seconds
|
||||||
*/
|
*/
|
||||||
void guiDisplayProgressBar(float position, float duration)
|
void guiDisplayProgressBar(float position, float duration)
|
||||||
{
|
{
|
||||||
@@ -372,6 +396,7 @@ void guiDisplayProgressBar(float position, float duration)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Display current directory path on bottom screen
|
* Display current directory path on bottom screen
|
||||||
|
* @param path Current directory path
|
||||||
*/
|
*/
|
||||||
void guiDisplayCurrentPath(const char* path)
|
void guiDisplayCurrentPath(const char* path)
|
||||||
{
|
{
|
||||||
|
|||||||
120
source/main.c
120
source/main.c
@@ -11,6 +11,7 @@
|
|||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -146,6 +147,125 @@ static int cmpstringp(const void *p1, const void *p2)
|
|||||||
return strcasecmp(* (char * const *) p1, * (char * const *) p2);
|
return strcasecmp(* (char * const *) p1, * (char * const *) p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check filename extension against supported audio types. Case-insensitive. */
|
||||||
|
static bool isMusicFilename(const char *name)
|
||||||
|
{
|
||||||
|
if(name == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const char *ext = strrchr(name, '.');
|
||||||
|
if(ext == NULL || *(ext + 1) == '\0')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ext++; /* skip dot */
|
||||||
|
char lext[16];
|
||||||
|
size_t i = 0;
|
||||||
|
for(; i < sizeof(lext)-1 && ext[i]; i++)
|
||||||
|
lext[i] = tolower((unsigned char)ext[i]);
|
||||||
|
lext[i] = '\0';
|
||||||
|
|
||||||
|
const char *allowed[] = {"mp3","wav","flac","ogg","opus","sid","m4a","aac", NULL};
|
||||||
|
for(int j = 0; allowed[j] != NULL; j++)
|
||||||
|
if(strcmp(lext, allowed[j]) == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build a list of music filenames (basenames) in the given directory. Caller must free array and each string. */
|
||||||
|
static int buildMusicFileListInDir(const char *dirpath, char ***outFiles)
|
||||||
|
{
|
||||||
|
DIR *dp;
|
||||||
|
struct dirent *ep;
|
||||||
|
char **files = NULL;
|
||||||
|
int fileNum = 0;
|
||||||
|
|
||||||
|
if((dp = opendir(dirpath)) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while((ep = readdir(dp)) != NULL)
|
||||||
|
{
|
||||||
|
if(ep->d_type == DT_DIR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!isMusicFilename(ep->d_name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
files = realloc(files, (fileNum + 1) * sizeof(char*));
|
||||||
|
files[fileNum] = strdup(ep->d_name);
|
||||||
|
fileNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fileNum > 0)
|
||||||
|
qsort(files, fileNum, sizeof(char*), cmpstringp);
|
||||||
|
|
||||||
|
closedir(dp);
|
||||||
|
*outFiles = files;
|
||||||
|
return fileNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Play next file in the same folder as the currently-playing file. Returns 0 on success, -1 on failure or no-next. */
|
||||||
|
static int playNextFromPath(struct playbackInfo_t *playbackInfo)
|
||||||
|
{
|
||||||
|
if(playbackInfo == NULL || playbackInfo->file[0] == '\0')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char fullcpy[PATH_MAX];
|
||||||
|
strncpy(fullcpy, playbackInfo->file, sizeof(fullcpy));
|
||||||
|
fullcpy[sizeof(fullcpy)-1] = '\0';
|
||||||
|
|
||||||
|
/* find last slash */
|
||||||
|
char *slash = strrchr(fullcpy, '/');
|
||||||
|
char dirpath[PATH_MAX];
|
||||||
|
char *basename = NULL;
|
||||||
|
|
||||||
|
if(slash == NULL)
|
||||||
|
{
|
||||||
|
/* No directory component; assume current dir */
|
||||||
|
if(getcwd(dirpath, sizeof(dirpath)) == NULL)
|
||||||
|
return -1;
|
||||||
|
basename = fullcpy;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t dirlen = slash - fullcpy;
|
||||||
|
if(dirlen >= sizeof(dirpath))
|
||||||
|
return -1;
|
||||||
|
memcpy(dirpath, fullcpy, dirlen);
|
||||||
|
dirpath[dirlen] = '\0';
|
||||||
|
basename = slash + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **files = NULL;
|
||||||
|
int count = buildMusicFileListInDir(dirpath, &files);
|
||||||
|
if(count <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int index = -1;
|
||||||
|
for(int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if(strcmp(files[i], basename) == 0)
|
||||||
|
{
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = -1;
|
||||||
|
if(index >= 0 && index + 1 < count)
|
||||||
|
{
|
||||||
|
char nextpath[PATH_MAX];
|
||||||
|
snprintf(nextpath, sizeof(nextpath), "%s/%s", dirpath, files[index+1]);
|
||||||
|
result = changeFile(nextpath, playbackInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < count; i++)
|
||||||
|
free(files[i]);
|
||||||
|
free(files);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the list of files and folders in current directory to an array.
|
* Store the list of files and folders in current directory to an array.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user