Files
mice-3ds/source/main.c
Mahyar Koshkouei 59688aaf0e Remove error on going up directory
Removed an error that would briefly appear on the screen if the user
were to attempt to change to the above directory whilst being in the
root directory.

Signed-off-by: Mahyar Koshkouei <deltabeard@users.noreply.github.com>
2017-02-15 16:53:33 +00:00

492 lines
9.3 KiB
C

/**
* ctrmus - 3DS Music Player
* Copyright (C) 2016 Mahyar Koshkouei
*
* This program comes with ABSOLUTELY NO WARRANTY and is free software. You are
* welcome to redistribute it under certain conditions; for details see the
* LICENSE file.
*/
#include <3ds.h>
#include <dirent.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "all.h"
#include "error.h"
#include "main.h"
#include "playback.h"
volatile bool runThreads = true;
/**
* Prints the current key mappings to stdio.
*/
static void showControls(void)
{
printf("Button mappings:\n"
"Pause: L+R or L+Up\n"
"Stop: L+B\n"
"A: Open File\n"
"B: Go up folder\n"
"Start: Exit\n");
}
/**
* Allows the playback thread to return any error messages that it may
* encounter.
*
* \param infoIn Struct containing addresses of the event, the error code,
* and an optional error string.
*/
void playbackWatchdog(void* infoIn)
{
struct watchdogInfo* info = infoIn;
while(runThreads)
{
svcWaitSynchronization(*info->errInfo->failEvent, U64_MAX);
svcClearEvent(*info->errInfo->failEvent);
consoleSelect(info->screen);
if(*info->errInfo->error != 0)
{
printf("Error %d: %s", *info->errInfo->error,
ctrmus_strerror(*info->errInfo->error));
if(info->errInfo->errstr != NULL)
{
printf(" %s", info->errInfo->errstr);
delete(info->errInfo->errstr);
}
printf("\n");
}
}
return;
}
/**
* Stop the currently playing file (if there is one) and play another file.
*
* \param ep_file File to play.
* \param playbackInfo Information that the playback thread requires to
* play file.
*/
static int changeFile(const char* ep_file, struct playbackInfo_t* playbackInfo)
{
s32 prio;
static Thread thread = NULL;
if(ep_file != NULL && getFileType(ep_file) == FILE_TYPE_ERROR)
{
*playbackInfo->errInfo->error = errno;
svcSignalEvent(*playbackInfo->errInfo->failEvent);
return -1;
}
/**
* If music is playing, stop it. Only one playback thread should be playing
* at any time.
*/
if(thread != NULL)
{
/* Tell the thread to stop playback before we join it */
stopPlayback();
threadJoin(thread, U64_MAX);
threadFree(thread);
thread = NULL;
}
if(ep_file == NULL || playbackInfo == NULL)
return 0;
playbackInfo->file = strdup(ep_file);
printf("Playing: %s\n", playbackInfo->file);
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
thread = threadCreate(playFile, playbackInfo, 32 * 1024, prio - 1, -2, false);
return 0;
}
/**
* List current directory.
*
* \param from First entry in directory to list.
* \param max Maximum number of entries to list. Must be > 0.
* \param select File to show as selected. Must be > 0.
* \return Number of entries listed or negative on error.
*/
static int listDir(int from, int max, int select)
{
DIR *dp;
struct dirent *ep;
int fileNum = 0;
int listed = 0;
char* wd = getcwd(NULL, 0);
if(wd == NULL)
goto err;
consoleClear();
printf("Dir: %.33s\n", wd);
if((dp = opendir(wd)) == NULL)
goto err;
if(from == 0)
{
printf("%c../\n", select == 0 ? '>' : ' ');
listed++;
}
while((ep = readdir(dp)) != NULL)
{
fileNum++;
if(fileNum <= from)
continue;
listed++;
printf("%c%s%.37s%s\n",
select == fileNum ? '>' : ' ',
ep->d_type == DT_DIR ? "\x1b[34;1m" : "",
ep->d_name,
ep->d_type == DT_DIR ? "/\x1b[0m" : "");
if(fileNum == max + from)
break;
}
if(closedir(dp) != 0)
goto err;
out:
free(wd);
return listed;
err:
listed = -1;
goto out;
}
/**
* Get number of files in current working folder
*
* \return Number of files in current working folder, -1 on failure with
* errno set.
*/
int getNumberFiles(void)
{
DIR *dp;
struct dirent *ep;
int ret = 0;
if((dp = opendir(".")) == NULL)
goto err;
while((ep = readdir(dp)) != NULL)
ret++;
closedir(dp);
out:
return ret;
err:
ret = -1;
goto out;
}
int main(int argc, char **argv)
{
PrintConsole topScreen;
PrintConsole bottomScreen;
int fileMax;
int fileNum = 0;
int from = 0;
Thread watchdogThread;
Handle playbackFailEvent;
struct watchdogInfo watchdogInfoIn;
struct errInfo_t errInfo;
struct playbackInfo_t playbackInfo;
volatile int error = 0;
gfxInitDefault();
sdmcInit();
consoleInit(GFX_TOP, &topScreen);
consoleInit(GFX_BOTTOM, &bottomScreen);
consoleSelect(&bottomScreen);
svcCreateEvent(&playbackFailEvent, RESET_ONESHOT);
errInfo.error = &error;
errInfo.failEvent = &playbackFailEvent;
errInfo.errstr = NULL;
watchdogInfoIn.screen = &topScreen;
watchdogInfoIn.errInfo = &errInfo;
watchdogThread = threadCreate(playbackWatchdog,
&watchdogInfoIn, 4 * 1024, 0x20, -2, true);
playbackInfo.file = NULL;
playbackInfo.errInfo = &errInfo;
chdir(DEFAULT_DIR);
chdir("MUSIC");
if(listDir(from, MAX_LIST, 0) < 0)
{
err_print("Unable to list directory.");
goto err;
}
fileMax = getNumberFiles();
/**
* This allows for music to continue playing through the headphones whilst
* the 3DS is closed.
*/
aptSetSleepAllowed(false);
while(aptMainLoop())
{
u32 kDown;
u32 kHeld;
static u64 mill = 0;
gfxFlushBuffers();
gspWaitForVBlank();
gfxSwapBuffers();
hidScanInput();
kDown = hidKeysDown();
kHeld = hidKeysHeld();
consoleSelect(&bottomScreen);
/* Exit ctrmus */
if(kDown & KEY_START)
break;
#ifdef DEBUG
consoleSelect(&topScreen);
printf("\rNum: %d, Max: %d, from: %d ", fileNum, fileMax, from);
consoleSelect(&bottomScreen);
#endif
if(kDown)
mill = osGetTime();
if(kHeld & KEY_L)
{
/* Pause/Play */
if(kDown & (KEY_R | KEY_UP))
{
if(isPlaying() == false)
continue;
consoleSelect(&topScreen);
if(togglePlayback() == true)
puts("Paused");
else
puts("Playing");
continue;
}
/* Show controls */
if(kDown & KEY_LEFT)
{
consoleSelect(&topScreen);
showControls();
continue;
}
/* Stop */
if(kDown & KEY_B)
{
stopPlayback();
changeFile(NULL, &playbackInfo);
consoleSelect(&topScreen);
puts("Stopped");
continue;
}
}
if((kDown & KEY_UP ||
((kHeld & KEY_UP) && (osGetTime() - mill > 500))) &&
fileNum > 0)
{
fileNum--;
/* 26 is the maximum number of entries that can be printed */
if(fileMax - fileNum > 26 && from != 0)
from--;
if(listDir(from, MAX_LIST, fileNum) < 0)
err_print("Unable to list directory.");
}
if((kDown & KEY_DOWN ||
((kHeld & KEY_DOWN) && (osGetTime() - mill > 500))) &&
fileNum < fileMax)
{
fileNum++;
if(fileNum >= MAX_LIST && fileMax - fileNum >= 0 &&
from < fileMax - MAX_LIST)
from++;
if(listDir(from, MAX_LIST, fileNum) < 0)
err_print("Unable to list directory.");
}
if((kDown & KEY_LEFT ||
((kHeld & KEY_LEFT) && (osGetTime() - mill > 500))) &&
fileNum > 0)
{
int skip = MAX_LIST / 2;
if(fileNum < skip)
skip = fileNum;
fileNum -= skip;
/* 26 is the maximum number of entries that can be printed */
if(fileMax - fileNum > 26 && from != 0)
{
from -= skip;
if(from < 0)
from = 0;
}
if(listDir(from, MAX_LIST, fileNum) < 0)
err_print("Unable to list directory.");
}
if((kDown & KEY_RIGHT ||
((kHeld & KEY_RIGHT) && (osGetTime() - mill > 500))) &&
fileNum < fileMax)
{
int skip = fileMax - fileNum;
if(skip > MAX_LIST / 2)
skip = MAX_LIST / 2;
fileNum += skip;
if(fileNum >= MAX_LIST && fileMax - fileNum >= 0 &&
from < fileMax - MAX_LIST)
{
from += skip;
if(from > fileMax - MAX_LIST)
from = fileMax - MAX_LIST;
}
if(listDir(from, MAX_LIST, fileNum) < 0)
err_print("Unable to list directory.");
}
/*
* Pressing B goes up a folder, as well as pressing A or R when ".."
* is selected.
*/
if((kDown & KEY_B) ||
((kDown & KEY_A) && (from == 0 && fileNum == 0)))
{
chdir("..");
fileNum = 0;
from = 0;
fileMax = getNumberFiles();
if(listDir(from, MAX_LIST, fileNum) < 0)
err_print("Unable to list directory.");
continue;
}
if(kDown & KEY_A)
{
int audioFileNum = 0;
DIR *dp;
struct dirent *ep;
dp = opendir(".");
if(dp != NULL)
{
while((ep = readdir(dp)) != NULL)
{
if(audioFileNum == fileNum - 1)
break;
audioFileNum++;
}
if(ep->d_type == DT_DIR)
{
/* file not allocated yet, so no need to clear it */
if(chdir(ep->d_name) != 0)
err_print("chdir");
fileNum = 0;
from = 0;
fileMax = getNumberFiles();
if(listDir(from, MAX_LIST, fileNum) < 0)
err_print("Unable to list directory.");
closedir(dp);
continue;
}
consoleSelect(&topScreen);
changeFile(ep->d_name, &playbackInfo);
consoleSelect(&bottomScreen);
if(closedir(dp) != 0)
err_print("Closing directory failed.");
}
else
err_print("Unable to open directory.");
}
}
#ifdef DEBUG
consoleSelect(&topScreen);
printf("\rNum: %d, Max: %d, from: %d ", fileNum, fileMax, from);
consoleSelect(&bottomScreen);
#endif
out:
puts("Exiting...");
runThreads = false;
svcSignalEvent(playbackFailEvent);
changeFile(NULL, &playbackInfo);
gfxExit();
sdmcExit();
return 0;
err:
puts("A fatal error occurred. Press START to exit.");
while(true)
{
u32 kDown;
hidScanInput();
kDown = hidKeysDown();
if(kDown & KEY_START)
break;
}
goto out;
}