mirror of
https://github.com/DarkStore-3DS/DarkStore.git
synced 2026-07-03 08:39:05 +00:00
First
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "animation.hpp"
|
||||
#include "common.hpp"
|
||||
#include "stringutils.hpp"
|
||||
#include <curl/curl.h>
|
||||
|
||||
extern int filesExtracted, extractFilesCount;
|
||||
extern std::string extractingFile;
|
||||
char progressBarMsg[128] = "";
|
||||
bool showProgressBar = false;
|
||||
ProgressBar progressbarType = ProgressBar::Downloading;
|
||||
|
||||
extern u32 extractSize, writeOffset;
|
||||
extern u32 installSize, installOffset;
|
||||
extern u32 copyOffset, copySize;
|
||||
|
||||
extern curl_off_t downloadTotal;
|
||||
extern curl_off_t downloadNow;
|
||||
|
||||
/*
|
||||
Dessinez la barre de progression.
|
||||
u64 current Progress : Le progrès actuel.
|
||||
u64 totalProgrès : Le progrès total.
|
||||
*/
|
||||
void Animation::DrawProgressBar(u64 currentProgress, u64 totalProgress) {
|
||||
Gui::Draw_Rect(30, 120, 340, 30, PROGRESSBAR_OUT_COLOR);
|
||||
Gui::Draw_Rect(31, 121, (int)(((float)currentProgress / (float)totalProgress) * 338.0f), 28, PROGRESSBAR_IN_COLOR);
|
||||
}
|
||||
|
||||
/*
|
||||
Afficher la barre de progression.
|
||||
*/
|
||||
void Animation::displayProgressBar() {
|
||||
char str[256];
|
||||
|
||||
while(showProgressBar) {
|
||||
switch(progressbarType) {
|
||||
case ProgressBar::Downloading:
|
||||
if (downloadTotal < 1.0f) downloadTotal = 1.0f;
|
||||
if (downloadTotal < downloadNow) downloadTotal = downloadNow;
|
||||
|
||||
snprintf(str, sizeof(str), "%s / %s (%.2f%%)",
|
||||
StringUtils::formatBytes(downloadNow).c_str(),
|
||||
StringUtils::formatBytes(downloadTotal).c_str(),
|
||||
((float)downloadNow/(float)downloadTotal) * 100.0f);
|
||||
break;
|
||||
|
||||
case ProgressBar::Extracting:
|
||||
snprintf(str, sizeof(str), "%s / %s (%.2f%%)",
|
||||
StringUtils::formatBytes(writeOffset).c_str(),
|
||||
StringUtils::formatBytes(extractSize).c_str(),
|
||||
((float)writeOffset/(float)extractSize) * 100.0f);
|
||||
break;
|
||||
|
||||
case ProgressBar::Installing:
|
||||
snprintf(str, sizeof(str), "%s / %s (%.2f%%)",
|
||||
StringUtils::formatBytes(installOffset).c_str(),
|
||||
StringUtils::formatBytes(installSize).c_str(),
|
||||
((float)installOffset/(float)installSize) * 100.0f);
|
||||
break;
|
||||
|
||||
case ProgressBar::Copying:
|
||||
snprintf(str, sizeof(str), "%s / %s (%.2f%%)",
|
||||
StringUtils::formatBytes(copyOffset).c_str(),
|
||||
StringUtils::formatBytes(copySize).c_str(),
|
||||
((float)copyOffset/(float)copySize) * 100.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
Gui::clearTextBufs();
|
||||
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
|
||||
C2D_TargetClear(Top, TRANSPARENT);
|
||||
C2D_TargetClear(Bottom, TRANSPARENT);
|
||||
GFX::DrawTop();
|
||||
Gui::DrawStringCentered(0, 1, 0.7f, TEXT_COLOR, progressBarMsg, 390, 0, font);
|
||||
|
||||
switch(progressbarType) {
|
||||
case ProgressBar::Downloading:
|
||||
Gui::DrawStringCentered(0, 80, 0.6f, TEXT_COLOR, str, 390, 0, font);
|
||||
Animation::DrawProgressBar(downloadNow, downloadTotal);
|
||||
break;
|
||||
|
||||
case ProgressBar::Extracting:
|
||||
Gui::DrawStringCentered(0, 180, 0.6f, TEXT_COLOR, str, 390, 0, font);
|
||||
Gui::DrawStringCentered(0, 100, 0.6f, TEXT_COLOR, std::to_string(filesExtracted) + " / " + std::to_string(extractFilesCount) + " " + (filesExtracted == 1 ? (Lang::get("FILE_EXTRACTED")).c_str() :(Lang::get("FILES_EXTRACTED"))), 390, 0, font);
|
||||
Gui::DrawStringCentered(0, 40, 0.6f, TEXT_COLOR, Lang::get("CURRENTLY_EXTRACTING"), 390, 0, font);
|
||||
Gui::DrawStringCentered(0, 70, 0.6f, TEXT_COLOR, extractingFile, 390, 0, font);
|
||||
Animation::DrawProgressBar(writeOffset, extractSize);
|
||||
break;
|
||||
|
||||
case ProgressBar::Installing:
|
||||
Gui::DrawStringCentered(0, 80, 0.6f, TEXT_COLOR, str, 390, 0, font);
|
||||
Animation::DrawProgressBar(installOffset, installSize);
|
||||
break;
|
||||
|
||||
case ProgressBar::Copying:
|
||||
Gui::DrawStringCentered(0, 80, 0.6f, TEXT_COLOR, str, 390, 0, font);
|
||||
Animation::DrawProgressBar(copyOffset, copySize);
|
||||
break;
|
||||
}
|
||||
|
||||
GFX::DrawBottom();
|
||||
C3D_FrameEnd(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int frame = 0; // 0 - 7.
|
||||
static int advanceFrame = 0; // Only animate every 4 frames.
|
||||
extern bool QueueRuns;
|
||||
|
||||
void Animation::DrawQueue(int x, int y) { GFX::DrawSprite(sprites_queue0_idx + frame, x, y); };
|
||||
void Animation::QueueAnimHandle() {
|
||||
if (QueueRuns) {
|
||||
advanceFrame = (advanceFrame + 1) % 4;
|
||||
if (advanceFrame == 0) frame = (frame + 1) % 8;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "argumentParser.hpp"
|
||||
#include "common.hpp"
|
||||
#include "scriptUtils.hpp"
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
Le constructeur de l’analyseur d’arguments.
|
||||
const std::string &file : Const Référence au fichier.
|
||||
const std::string &entry : Const Référence au nom du titre de l’entrée.
|
||||
int dlIndex : L’index de téléchargement.
|
||||
*/
|
||||
ArgumentParser::ArgumentParser(const std::string &file, const std::string &entry, int dlIndex) {
|
||||
if (dlIndex != -1 && file != "") {
|
||||
this->file = file;
|
||||
this->entry = entry;
|
||||
this->dlIndex = dlIndex;
|
||||
|
||||
this->Load();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Préparez eShop et obtenez un état valide.
|
||||
*/
|
||||
void ArgumentParser::Load() {
|
||||
if (access((std::string(_STORE_PATH) + this->file).c_str(), F_OK) != 0) return;
|
||||
|
||||
this->store = std::make_unique<Store>(_STORE_PATH + this->file, this->file, true);
|
||||
if (!this->store->GetValid()) return;
|
||||
|
||||
for (int i = 0; i < this->store->GetStoreSize(); i++) {
|
||||
if (this->store->GetTitleEntry(i) == this->entry) {
|
||||
this->entryIndex = i;
|
||||
const std::vector<std::string> dlList = this->store->GetDownloadList(this->entryIndex);
|
||||
|
||||
if (dlList.empty()) return;
|
||||
|
||||
if ((int)dlList.size() >= this->dlIndex) {
|
||||
this->executeEntry = dlList[this->dlIndex];
|
||||
this->isValid = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Exécutez l’entrée de l’argument, si elle est valide.
|
||||
*/
|
||||
void ArgumentParser::Execute() {
|
||||
if (this->isValid) {
|
||||
if (Msg::promptMsg(Lang::get("EXECUTE_ENTRY") + "\n\n" + this->executeEntry)) {
|
||||
ScriptUtils::runFunctions(this->store->GetJson(), this->entryIndex, this->executeEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "cia.hpp"
|
||||
#include "files.hpp"
|
||||
|
||||
Result Title::Launch(u64 titleId, FS_MediaType mediaType) {
|
||||
Result ret = 0;
|
||||
u8 param[0x300];
|
||||
u8 hmac[0x20];
|
||||
|
||||
if (R_FAILED(ret = APT_PrepareToDoApplicationJump(0, titleId, mediaType))) {
|
||||
printf("Error In:\nAPT_PrepareToDoApplicationJump");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (R_FAILED(ret = APT_DoApplicationJump(param, sizeof(param), hmac))) {
|
||||
printf("Error In:\nAPT_DoApplicationJump");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result Title::DeletePrevious(u64 titleid, FS_MediaType media) {
|
||||
Result ret = 0;
|
||||
u32 titles_amount = 0;
|
||||
|
||||
ret = AM_GetTitleCount(media, &titles_amount);
|
||||
if (R_FAILED(ret)) {
|
||||
printf("Error in:\nAM_GetTitleCount\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 read_titles = 0;
|
||||
u64 *titleIDs = (u64 *)malloc(titles_amount * sizeof(u64));
|
||||
|
||||
ret = AM_GetTitleList(&read_titles, media, titles_amount, titleIDs);
|
||||
if (R_FAILED(ret)) {
|
||||
free(titleIDs);
|
||||
printf("Error in:\nAM_GetTitleList\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < read_titles; i++) {
|
||||
if (titleIDs[i] == titleid) {
|
||||
ret = AM_DeleteAppTitle(media, titleid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(titleIDs);
|
||||
|
||||
if (R_FAILED(ret)) {
|
||||
printf("Error in:\nAM_DeleteAppTitle\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static FS_MediaType getTitleDestination(u64 titleId) {
|
||||
u16 platform = (u16) ((titleId >> 48) & 0xFFFF);
|
||||
u16 category = (u16) ((titleId >> 32) & 0xFFFF);
|
||||
u8 variation = (u8) (titleId & 0xFF);
|
||||
|
||||
// DSiWare 3DS DSiWare, System, DLP Application Titre Système
|
||||
return platform == 0x0003 || (platform == 0x0004 && ((category & 0x8011) != 0 || (category == 0x0000 && variation == 0x02))) ? MEDIATYPE_NAND : MEDIATYPE_SD;
|
||||
}
|
||||
|
||||
u32 installSize = 0, installOffset = 0;
|
||||
|
||||
Result Title::Install(const char *ciaPath, bool updatingSelf) {
|
||||
u32 bytes_read = 0, bytes_written;
|
||||
installSize = 0, installOffset = 0; u64 size = 0;
|
||||
Handle ciaHandle, fileHandle;
|
||||
AM_TitleEntry info;
|
||||
Result ret = 0;
|
||||
FS_MediaType media = MEDIATYPE_SD;
|
||||
|
||||
ret = openFile(&fileHandle, ciaPath, false);
|
||||
if (R_FAILED(ret)) {
|
||||
printf("Error in:\nopenFile\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = AM_GetCiaFileInfo(media, &info, fileHandle);
|
||||
if (R_FAILED(ret)) {
|
||||
printf("Error in:\nAM_GetCiaFileInfo\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
media = getTitleDestination(info.titleID);
|
||||
|
||||
if (!updatingSelf) {
|
||||
ret = Title::DeletePrevious(info.titleID, media);
|
||||
if (R_FAILED(ret)) return ret;
|
||||
}
|
||||
|
||||
ret = FSFILE_GetSize(fileHandle, &size);
|
||||
if (R_FAILED(ret)) {
|
||||
printf("Error in:\nFSFILE_GetSize\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = AM_StartCiaInstall(media, &ciaHandle);
|
||||
if (R_FAILED(ret)) {
|
||||
printf("Error in:\nAM_StartCiaInstall\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 toRead = 0x200000;
|
||||
u8 *buf = new u8[toRead];
|
||||
|
||||
if (!buf) return -1;
|
||||
|
||||
installSize = size;
|
||||
do {
|
||||
FSFILE_Read(fileHandle, &bytes_read, installOffset, buf, toRead);
|
||||
FSFILE_Write(ciaHandle, &bytes_written, installOffset, buf, toRead, FS_WRITE_FLUSH);
|
||||
installOffset += bytes_read;
|
||||
} while(installOffset < installSize);
|
||||
delete[] buf;
|
||||
|
||||
ret = AM_FinishCiaInstall(ciaHandle);
|
||||
if (R_FAILED(ret)) {
|
||||
printf("Error in:\nAM_FinishCiaInstall\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = FSFILE_Close(fileHandle);
|
||||
if (R_FAILED(ret)) {
|
||||
printf("Error in:\nFSFILE_Close\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (updatingSelf) {
|
||||
if (R_FAILED(ret = Title::Launch(info.titleID, MEDIATYPE_SD))) return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "common.hpp"
|
||||
#include "config.hpp"
|
||||
#include "json.hpp"
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
Détecte la langue du système et est utilisé plus tard pour définir la langue de l’application à la langue du système.
|
||||
*/
|
||||
void Config::sysLang() {
|
||||
u8 language = 1;
|
||||
CFGU_GetSystemLanguage(&language);
|
||||
|
||||
switch(language) {
|
||||
case 0:
|
||||
this->language("jp"); // Japonais (日本人), traduit.
|
||||
break;
|
||||
|
||||
case 1:
|
||||
this->language("en"); // Anglais (English), traduit.
|
||||
break;
|
||||
|
||||
case 2:
|
||||
this->language("fr"); // Français (Français), traduit.
|
||||
break;
|
||||
|
||||
case 3:
|
||||
this->language("de"); // Allemand (Deutsche), traduit.
|
||||
break;
|
||||
|
||||
case 4:
|
||||
this->language("it"); // Italien (Italiano), traduit.
|
||||
break;
|
||||
|
||||
case 5:
|
||||
this->language("es"); // Espagnol (Español), traduit.
|
||||
break;
|
||||
|
||||
case 6:
|
||||
this->language("cn-SI"); // Chinois simplifié (简体中文), traduit.
|
||||
break;
|
||||
|
||||
case 7:
|
||||
this->language("kr"); // Coréen (한국어), traduit.
|
||||
break;
|
||||
|
||||
case 8:
|
||||
this->language("nl"); // Néerlandais (Nederlands), pas totalement traduit.
|
||||
break;
|
||||
|
||||
case 9:
|
||||
this->language("pt"); // Portugais (Português), pas totalement traduit.
|
||||
break;
|
||||
|
||||
case 10:
|
||||
this->language("ru"); // Russe (русский), pas totalement traduit.
|
||||
break;
|
||||
|
||||
case 11:
|
||||
this->language("cn-TR"); // chinois traditionnel (繁體中文), traduit.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Au cas où ça n’existe pas.
|
||||
*/
|
||||
void Config::initialize() {
|
||||
FILE *temp = fopen("sdmc:/3ds/GhosteShop/Config.json", "w");
|
||||
char tmp[2] = { '{', '}' };
|
||||
fwrite(tmp, sizeof(tmp), 1, temp);
|
||||
fclose(temp);
|
||||
}
|
||||
|
||||
/*
|
||||
Constructeur de la configuration.
|
||||
*/
|
||||
Config::Config() {
|
||||
if (access("sdmc:/3ds/GhosteShop/Config.json", F_OK) != 0) {
|
||||
this->initialize();
|
||||
}
|
||||
|
||||
FILE *file = fopen("sdmc:/3ds/GhosteShop/Config.json", "r");
|
||||
this->json = nlohmann::json::parse(file, nullptr, false);
|
||||
fclose(file);
|
||||
|
||||
/* Créons-en une nouvelle. */
|
||||
if (!this->json.contains("Version")) this->initialize();
|
||||
|
||||
if (!this->json.contains("Language")) this->sysLang();
|
||||
else this->language(this->getString("Language"));
|
||||
|
||||
if (this->json.contains("LastStore")) this->lastStore(this->getString("LastStore"));
|
||||
if (this->json.contains("List")) this->list(this->getBool("List"));
|
||||
if (this->json.contains("AutoUpdate")) this->autoupdate(this->getBool("AutoUpdate"));
|
||||
if (this->json.contains("_3DSX_Path")) this->_3dsxPath(this->getString("_3DSX_Path"));
|
||||
if (this->json.contains("NDS_Path")) this->ndsPath(this->getString("NDS_Path"));
|
||||
if (this->json.contains("Archive_Path")) this->archPath(this->getString("Archive_Path"));
|
||||
if (this->json.contains("MetaData")) this->metadata(this->getBool("MetaData"));
|
||||
if (this->json.contains("UpdateCheck")) this->updatecheck(this->getBool("UpdateCheck"));
|
||||
if (this->json.contains("UseBG")) this->usebg(this->getBool("UseBG"));
|
||||
if (this->json.contains("CustomFont")) this->customfont(this->getBool("CustomFont"));
|
||||
if (this->json.contains("Shortcut_Path")) this->shortcut(this->getString("Shortcut_Path"));
|
||||
if (this->json.contains("Display_Changelog")) this->changelog(this->getBool("Display_Changelog"));
|
||||
|
||||
this->changesMade = false; // Aucune modification n’a encore été apportée.
|
||||
}
|
||||
|
||||
/* Ecrivez à config si changesMade. */
|
||||
void Config::save() {
|
||||
if (this->changesMade) {
|
||||
FILE *file = fopen("sdmc:/3ds/GhosteShop/Config.json", "w");
|
||||
|
||||
/* Valeurs Théoriques. */
|
||||
this->setString("Language", this->language());
|
||||
this->setInt("Version", 1);
|
||||
this->setString("LastStore", this->lastStore());
|
||||
this->setBool("List", this->list());
|
||||
this->setBool("AutoUpdate", this->autoupdate());
|
||||
this->setString("_3DSX_Path", this->_3dsxPath());
|
||||
this->setString("NDS_Path", this->ndsPath());
|
||||
this->setString("Archive_Path", this->archPath());
|
||||
this->setBool("MetaData", this->metadata());
|
||||
this->setBool("UpdateCheck", this->updatecheck());
|
||||
this->setBool("UseBG", this->usebg());
|
||||
this->setBool("CustomFont", this->customfont());
|
||||
this->setString("Shortcut_Path", this->shortcut());
|
||||
this->setBool("Display_Changelog", this->changelog());
|
||||
|
||||
/* Écrire les modifications au fichier. */
|
||||
const std::string dump = this->json.dump(1, '\t');
|
||||
fwrite(dump.c_str(), 1, this->json.dump(1, '\t').size(), file);
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fonctions d’aide. */
|
||||
bool Config::getBool(const std::string &key) {
|
||||
if (!this->json.contains(key)) return false;
|
||||
|
||||
return this->json.at(key).get_ref<const bool &>();
|
||||
}
|
||||
void Config::setBool(const std::string &key, bool v) { this->json[key] = v; };
|
||||
|
||||
int Config::getInt(const std::string &key) {
|
||||
if (!this->json.contains(key)) return 0;
|
||||
|
||||
return this->json.at(key).get_ref<const int64_t &>();
|
||||
}
|
||||
void Config::setInt(const std::string &key, int v) { this->json[key] = v; };
|
||||
|
||||
std::string Config::getString(const std::string &key) {
|
||||
if (!this->json.contains(key)) return "";
|
||||
|
||||
return this->json.at(key).get_ref<const std::string &>();
|
||||
}
|
||||
void Config::setString(const std::string &key, const std::string &v) { this->json[key] = v; };
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "extract.hpp"
|
||||
#include "scriptUtils.hpp"
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#include <regex>
|
||||
|
||||
int filesExtracted = 0, extractFilesCount = 0;
|
||||
std::string extractingFile = "";
|
||||
|
||||
/* C’est notre variable File Progressbar. */
|
||||
u32 extractSize = 0, writeOffset = 0;
|
||||
|
||||
Result getExtractedSize(const std::string &archivePath, const std::string &wantedFile) {
|
||||
extractSize = 0, writeOffset = 0, filesExtracted = 0, extractFilesCount = 0;
|
||||
|
||||
archive *a = archive_read_new();
|
||||
archive_entry *entry;
|
||||
|
||||
archive_read_support_format_all(a);
|
||||
|
||||
if (archive_read_open_filename(a, archivePath.c_str(), 0x4000) != ARCHIVE_OK) return EXTRACT_ERROR_OPENFILE;
|
||||
|
||||
while(archive_read_next_header(a, &entry) == ARCHIVE_OK) {
|
||||
int size = archive_entry_size(entry);
|
||||
if (size > 0) { /* Ignorer les dossiers. */
|
||||
std::smatch match;
|
||||
std::string entryName(archive_entry_pathname(entry));
|
||||
if (std::regex_search(entryName, match, std::regex(wantedFile))) {
|
||||
extractSize += size;
|
||||
extractFilesCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
archive_read_close(a);
|
||||
archive_read_free(a);
|
||||
return EXTRACT_ERROR_NONE;
|
||||
}
|
||||
|
||||
Result extractArchive(const std::string &archivePath, const std::string &wantedFile, const std::string &outputPath) {
|
||||
archive *a = archive_read_new();
|
||||
archive_entry *entry;
|
||||
|
||||
a = archive_read_new();
|
||||
archive_read_support_format_all(a);
|
||||
|
||||
if (archive_read_open_filename(a, archivePath.c_str(), 0x4000) != ARCHIVE_OK) {
|
||||
archive_read_close(a);
|
||||
archive_read_free(a);
|
||||
return EXTRACT_ERROR_OPENFILE;
|
||||
}
|
||||
|
||||
while(archive_read_next_header(a, &entry) == ARCHIVE_OK) {
|
||||
if (archive_entry_size(entry) > 0) { /* Ignorer les dossiers.. */
|
||||
std::smatch match;
|
||||
std::string entryName(archive_entry_pathname(entry));
|
||||
if (std::regex_search(entryName, match, std::regex(wantedFile))) {
|
||||
extractingFile = outputPath + match.suffix().str();
|
||||
|
||||
/* Faire les répertoires. */
|
||||
for (char *slashpos = strchr(extractingFile.c_str() + 1, '/'); slashpos != NULL; slashpos = strchr(slashpos + 1, '/')) {
|
||||
char bak = *(slashpos);
|
||||
*(slashpos) = '\0';
|
||||
|
||||
mkdir(extractingFile.c_str(), 0777);
|
||||
|
||||
*(slashpos) = bak;
|
||||
}
|
||||
|
||||
uint sizeLeft = archive_entry_size(entry);
|
||||
|
||||
FILE *file = fopen(extractingFile.c_str(), "wb");
|
||||
if (!file) {
|
||||
archive_read_close(a);
|
||||
archive_read_free(a);
|
||||
return EXTRACT_ERROR_WRITEFILE;
|
||||
}
|
||||
|
||||
u8 *buf = new u8[0x30000];
|
||||
if (!buf) {
|
||||
fclose(file);
|
||||
archive_read_close(a);
|
||||
archive_read_free(a);
|
||||
return EXTRACT_ERROR_ALLOC;
|
||||
}
|
||||
|
||||
while(sizeLeft > 0) {
|
||||
u64 toRead = std::min(0x30000u, sizeLeft);
|
||||
ssize_t size = archive_read_data(a, buf, toRead);
|
||||
// Archive error, stop extracting
|
||||
if(size < 0) {
|
||||
fclose(file);
|
||||
delete[] buf;
|
||||
archive_read_close(a);
|
||||
archive_read_free(a);
|
||||
return EXTRACT_ERROR_ARCHIVE;
|
||||
}
|
||||
fwrite(buf, 1, size, file);
|
||||
sizeLeft -= size;
|
||||
writeOffset += size;
|
||||
}
|
||||
|
||||
filesExtracted++;
|
||||
fclose(file);
|
||||
delete[] buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
archive_read_close(a);
|
||||
archive_read_free(a);
|
||||
return EXTRACT_ERROR_NONE;
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "fileBrowse.hpp"
|
||||
#include "json.hpp"
|
||||
#include "structs.hpp"
|
||||
#include <3ds.h>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <unistd.h>
|
||||
|
||||
bool nameEndsWith(const std::string &name, const std::vector<std::string> &extensionList) {
|
||||
if (name.substr(0, 2) == "._") return false;
|
||||
|
||||
if (name.size() == 0) return false;
|
||||
|
||||
if (extensionList.size() == 0) return true;
|
||||
|
||||
for(int i = 0; i < (int)extensionList.size(); i++) {
|
||||
const std::string ext = extensionList.at(i);
|
||||
if (strcasecmp(name.c_str() + name.size() - ext.size(), ext.c_str()) == 0) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dirEntryPredicate(const DirEntry &lhs, const DirEntry &rhs) {
|
||||
if (!lhs.isDirectory && rhs.isDirectory) return false;
|
||||
if (lhs.isDirectory && !rhs.isDirectory) return true;
|
||||
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
|
||||
}
|
||||
|
||||
void getDirectoryContents(std::vector<DirEntry> &dirContents, const std::vector<std::string> &extensionList) {
|
||||
struct stat st;
|
||||
|
||||
dirContents.clear();
|
||||
|
||||
DIR *pdir = opendir(".");
|
||||
|
||||
if (pdir != nullptr) {
|
||||
while(true) {
|
||||
DirEntry dirEntry;
|
||||
|
||||
struct dirent *pent = readdir(pdir);
|
||||
if (pent == NULL) break;
|
||||
|
||||
stat(pent->d_name, &st);
|
||||
dirEntry.name = pent->d_name;
|
||||
dirEntry.isDirectory = (st.st_mode & S_IFDIR) ? true : false;
|
||||
|
||||
if (dirEntry.name.compare(".") != 0 && (dirEntry.isDirectory || nameEndsWith(dirEntry.name, extensionList))) {
|
||||
dirContents.push_back(dirEntry);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(pdir);
|
||||
}
|
||||
|
||||
sort(dirContents.begin(), dirContents.end(), dirEntryPredicate);
|
||||
}
|
||||
|
||||
void getDirectoryContents(std::vector<DirEntry> &dirContents) {
|
||||
getDirectoryContents(dirContents, {});
|
||||
}
|
||||
|
||||
std::vector<std::string> getContents(const std::string &name, const std::vector<std::string> &extensionList) {
|
||||
std::vector<std::string> dirContents;
|
||||
DIR *pdir = opendir(name.c_str());
|
||||
struct dirent *pent;
|
||||
|
||||
while ((pent = readdir(pdir)) != NULL) {
|
||||
if (nameEndsWith(pent->d_name, extensionList)) dirContents.push_back(pent->d_name);
|
||||
}
|
||||
|
||||
closedir(pdir);
|
||||
return dirContents;
|
||||
}
|
||||
|
||||
/*
|
||||
Retourner les informations eShop.
|
||||
const std::string &file : Const Référence au chemin du fichier.
|
||||
const std::string &fieName : Const Référence au nom de fichier, sans chemin.
|
||||
*/
|
||||
EshopInfo GetInfo(const std::string &file, const std::string &fileName) {
|
||||
EshopInfo Temp = { "", "", "", "", fileName, "", -1, -1, -1 }; // Titre, Auteur, URL, Fichier (pour vérifier s’il n’existe pas de barre oblique), FileName, Desc, Version, Révision, Entrées.
|
||||
nlohmann::json JSON = nullptr;
|
||||
|
||||
FILE *temp = fopen(file.c_str(), "r");
|
||||
JSON = nlohmann::json::parse(temp, nullptr, false);
|
||||
fclose(temp);
|
||||
|
||||
if (!JSON.contains("storeInfo")) return Temp; // storeInfo n'existe pas.
|
||||
|
||||
if (JSON["storeInfo"].contains("title") && JSON["storeInfo"]["title"].is_string()) {
|
||||
Temp.Title = JSON["storeInfo"]["title"];
|
||||
}
|
||||
|
||||
if (JSON["storeInfo"].contains("file") && JSON["storeInfo"]["file"].is_string()) {
|
||||
Temp.File = JSON["storeInfo"]["file"];
|
||||
}
|
||||
|
||||
if (JSON["storeInfo"].contains("author") && JSON["storeInfo"]["author"].is_string()) {
|
||||
Temp.Author = JSON["storeInfo"]["author"];
|
||||
}
|
||||
|
||||
if (JSON["storeInfo"].contains("url") && JSON["storeInfo"]["url"].is_string()) {
|
||||
Temp.URL = JSON["storeInfo"]["url"];
|
||||
}
|
||||
|
||||
if (JSON["storeInfo"].contains("description") && JSON["storeInfo"]["description"].is_string()) {
|
||||
Temp.Description = JSON["storeInfo"]["description"];
|
||||
}
|
||||
|
||||
if (JSON["storeInfo"].contains("version") && JSON["storeInfo"]["version"].is_number()) {
|
||||
Temp.Version = JSON["storeInfo"]["version"];
|
||||
}
|
||||
|
||||
if (JSON["storeInfo"].contains("revision") && JSON["storeInfo"]["revision"].is_number()) {
|
||||
Temp.Revision = JSON["storeInfo"]["revision"];
|
||||
}
|
||||
|
||||
if (JSON.contains("storeContent")) Temp.StoreSize = JSON["storeContent"].size();
|
||||
|
||||
return Temp;
|
||||
}
|
||||
|
||||
/*
|
||||
Renvoie le vecteur d’informations eShop.
|
||||
const std::string &path : Const Référence au chemin, où vérifier.
|
||||
*/
|
||||
std::vector<EshopInfo> GetEshopInfo(const std::string &path) {
|
||||
std::vector<EshopInfo> info;
|
||||
std::vector<DirEntry> dirContents;
|
||||
|
||||
if (access(path.c_str(), F_OK) != 0) return {}; // Le dossier n’existe pas.
|
||||
|
||||
chdir(path.c_str());
|
||||
getDirectoryContents(dirContents, { "eshop" });
|
||||
|
||||
for(uint i = 0; i < dirContents.size(); i++) {
|
||||
/* Assurez-vous de SEULEMENT pousser .eshop, et pas de dossiers. Évite les plantages dans ce cas aussi. */
|
||||
if ((path + dirContents[i].name).find(".eshop") != std::string::npos) {
|
||||
info.push_back( GetInfo(path + dirContents[i].name, dirContents[i].name) );
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
#define copyBufSize 0x8000
|
||||
u32 copyBuf[copyBufSize];
|
||||
|
||||
/*
|
||||
Copiez un répertoire.
|
||||
DirEntry *entry : pointeur vers un DirEntry.
|
||||
const char *destinationPath : pointeur vers le chemin de destination.
|
||||
const char *sourcePath : pointeur vers le chemin source.
|
||||
*/
|
||||
void dirCopy(DirEntry *entry, const char *destinationPath, const char *sourcePath) {
|
||||
std::vector<DirEntry> dirContents;
|
||||
dirContents.clear();
|
||||
if (entry->isDirectory) chdir((sourcePath + ("/" + entry->name)).c_str());
|
||||
getDirectoryContents(dirContents);
|
||||
if (((int)dirContents.size()) == 1) mkdir((destinationPath + ("/" + entry->name)).c_str(), 0777);
|
||||
if (((int)dirContents.size()) != 1) fcopy((sourcePath + ("/" + entry->name)).c_str(), (destinationPath + ("/" + entry->name)).c_str());
|
||||
}
|
||||
|
||||
u32 copyOffset = 0, copySize = 0;
|
||||
/*
|
||||
L’opération de copie.
|
||||
const char *destinationPath : pointeur vers le chemin de destination.
|
||||
const char *sourcePath : pointeur vers le chemin source.
|
||||
*/
|
||||
int fcopy(const char *sourcePath, const char *destinationPath) {
|
||||
copyOffset = 0, copySize = 0;
|
||||
|
||||
DIR *isDir = opendir(sourcePath);
|
||||
|
||||
if (isDir != NULL) {
|
||||
closedir(isDir);
|
||||
|
||||
/* Le chemin source est un répertoire. */
|
||||
chdir(sourcePath);
|
||||
std::vector<DirEntry> dirContents;
|
||||
getDirectoryContents(dirContents);
|
||||
DirEntry *entry = &dirContents.at(1);
|
||||
mkdir(destinationPath, 0777);
|
||||
|
||||
for(int i = 1; i < ((int)dirContents.size()); i++) {
|
||||
chdir(sourcePath);
|
||||
entry = &dirContents.at(i);
|
||||
dirCopy(entry, destinationPath, sourcePath);
|
||||
}
|
||||
|
||||
chdir(destinationPath);
|
||||
chdir("..");
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
closedir(isDir);
|
||||
|
||||
/* Le chemin source est un fichier. */
|
||||
FILE *sourceFile = fopen(sourcePath, "rb");
|
||||
copySize = 0, copyOffset = 0;
|
||||
|
||||
if (sourceFile) {
|
||||
fseek(sourceFile, 0, SEEK_END);
|
||||
copySize = ftell(sourceFile); // Obtenir la taille du fichier source.
|
||||
fseek(sourceFile, 0, SEEK_SET);
|
||||
|
||||
} else {
|
||||
fclose(sourceFile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *destinationFile = fopen(destinationPath, "wb");
|
||||
//if (destinationFile) {
|
||||
fseek(destinationFile, 0, SEEK_SET);
|
||||
/*} else {
|
||||
fclose(sourceFile);
|
||||
fclose(destinationFile);
|
||||
return -1;
|
||||
}*/
|
||||
|
||||
int numr;
|
||||
while(1) {
|
||||
scanKeys();
|
||||
if (keysHeld() & KEY_B) {
|
||||
/* Annuler la copie. */
|
||||
fclose(sourceFile);
|
||||
fclose(destinationFile);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
printf("\x1b[16;0H");
|
||||
printf("Progress:\n");
|
||||
printf("%i/%i Bytes ", (int)copyOffset, (int)copySize);
|
||||
|
||||
/* Copier le fichier vers le chemin de destination. */
|
||||
numr = fread(copyBuf, 2, copyBufSize, sourceFile);
|
||||
fwrite(copyBuf, 2, numr, destinationFile);
|
||||
copyOffset += copyBufSize;
|
||||
|
||||
if (copyOffset > copySize) {
|
||||
fclose(sourceFile);
|
||||
fclose(destinationFile);
|
||||
|
||||
printf("\x1b[17;0H");
|
||||
printf("%i/%i Bytes ", (int)copyOffset, (int)copySize);
|
||||
for(int i = 0; i < 30; i++) gspWaitForVBlank();
|
||||
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "files.hpp"
|
||||
|
||||
FS_Path getPathInfo(const char *path, FS_ArchiveID *archive) {
|
||||
*archive = ARCHIVE_SDMC;
|
||||
FS_Path filePath = { PATH_INVALID, 0, nullptr };
|
||||
unsigned int prefixlen = 0;
|
||||
|
||||
if (!strncmp(path, "sdmc:/", 6)) {
|
||||
prefixlen = 5;
|
||||
|
||||
} else if (*path != '/') {
|
||||
/*
|
||||
Si le chemin est local (ne pas commencer par une barre oblique),
|
||||
il doit être ajouté au répertoire de travail pour être valide.
|
||||
*/
|
||||
char *actualPath = NULL;
|
||||
asprintf(&actualPath, "%s%s", "/", path);
|
||||
filePath = fsMakePath(PATH_ASCII, actualPath);
|
||||
free(actualPath);
|
||||
}
|
||||
|
||||
/* Si la valeur filePath est définie ci-dessus, définissez-la. */
|
||||
if (filePath.size == 0) filePath = fsMakePath(PATH_ASCII, path + prefixlen);
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
Result makeDirs(const char *path) {
|
||||
Result ret = 0;
|
||||
FS_ArchiveID archiveID;
|
||||
FS_Path filePath = getPathInfo(path, &archiveID);
|
||||
FS_Archive archive;
|
||||
|
||||
ret = FSUSER_OpenArchive(&archive, archiveID, fsMakePath(PATH_EMPTY, ""));
|
||||
|
||||
for (char *slashpos = strchr(path + 1, '/'); slashpos != NULL; slashpos = strchr(slashpos + 1, '/')) {
|
||||
char bak = *(slashpos);
|
||||
*(slashpos) = '\0';
|
||||
Handle dirHandle;
|
||||
|
||||
ret = FSUSER_OpenDirectory(&dirHandle, archive, filePath);
|
||||
if (R_SUCCEEDED(ret)) FSDIR_Close(dirHandle);
|
||||
else ret = FSUSER_CreateDirectory(archive, filePath, FS_ATTRIBUTE_DIRECTORY);
|
||||
|
||||
*(slashpos) = bak;
|
||||
}
|
||||
|
||||
FSUSER_CloseArchive(archive);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result openFile(Handle *fileHandle, const char *path, bool write) {
|
||||
FS_ArchiveID archive;
|
||||
FS_Path filePath = getPathInfo(path, &archive);
|
||||
u32 flags = (write ? (FS_OPEN_CREATE | FS_OPEN_WRITE) : FS_OPEN_READ);
|
||||
|
||||
Result ret = 0;
|
||||
ret = makeDirs(strdup(path));
|
||||
ret = FSUSER_OpenFileDirectly(fileHandle, archive, fsMakePath(PATH_EMPTY, ""), filePath, flags, 0);
|
||||
if (write) ret = FSFILE_SetSize(*fileHandle, 0); // Tronquer le fichier pour supprimer le contenu précédent avant l’écriture.
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result deleteFile(const char *path) {
|
||||
FS_ArchiveID archiveID;
|
||||
FS_Path filePath = getPathInfo(path, &archiveID);
|
||||
|
||||
FS_Archive archive;
|
||||
|
||||
Result ret = FSUSER_OpenArchive(&archive, archiveID, fsMakePath(PATH_EMPTY, ""));
|
||||
if (R_FAILED(ret)) return ret;
|
||||
ret = FSUSER_DeleteFile(archive, filePath);
|
||||
FSUSER_CloseArchive(archive);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result removeDir(const char *path) {
|
||||
FS_ArchiveID archiveID;
|
||||
FS_Path filePath = getPathInfo(path, &archiveID);
|
||||
FS_Archive archive;
|
||||
|
||||
Result ret = FSUSER_OpenArchive(&archive, archiveID, fsMakePath(PATH_EMPTY, ""));
|
||||
if (R_FAILED(ret)) return ret;
|
||||
ret = FSUSER_DeleteDirectory(archive, filePath);
|
||||
FSUSER_CloseArchive(archive);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result removeDirRecursive(const char *path) {
|
||||
FS_ArchiveID archiveID;
|
||||
FS_Path filePath = getPathInfo(path, &archiveID);
|
||||
FS_Archive archive;
|
||||
|
||||
Result ret = FSUSER_OpenArchive(&archive, archiveID, fsMakePath(PATH_EMPTY, ""));
|
||||
if (R_FAILED(ret)) return ret;
|
||||
ret = FSUSER_DeleteDirectoryRecursively(archive, filePath);
|
||||
FSUSER_CloseArchive(archive);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "lang.hpp"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static nlohmann::json appJson;
|
||||
|
||||
std::string Lang::get(const std::string &key) {
|
||||
if (!appJson.contains(key)) return key;
|
||||
|
||||
return appJson.at(key).get_ref<const std::string&>();
|
||||
}
|
||||
|
||||
void Lang::load(const std::string &lang) {
|
||||
FILE *values;
|
||||
|
||||
/* Vérifier si il existe. */
|
||||
if (access(("romfs:/lang/" + lang + "/app.json").c_str(), F_OK) == 0) {
|
||||
values = fopen(std::string(("romfs:/lang/" + lang + "/app.json")).c_str(), "rt");
|
||||
appJson = nlohmann::json::parse(values, nullptr, false);
|
||||
fclose(values);
|
||||
return;
|
||||
|
||||
} else {
|
||||
values = fopen(("romfs:/lang/en/app.json"), "rt");
|
||||
appJson = nlohmann::json::parse(values, nullptr, false);
|
||||
fclose(values);
|
||||
return;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "files.hpp"
|
||||
#include "queueSystem.hpp"
|
||||
#include "scriptUtils.hpp"
|
||||
#include <unistd.h>
|
||||
|
||||
std::deque<std::unique_ptr<Queue>> queueEntries;
|
||||
bool QueueRuns = false;
|
||||
static Thread queueThread = nullptr;
|
||||
|
||||
LightLock QueueSystem::lock;
|
||||
|
||||
/*
|
||||
Adds an entry to the queue.
|
||||
nlohmann::json obj: The object.
|
||||
C2D_Image icn: The icon.
|
||||
*/
|
||||
void QueueSystem::AddToQueue(nlohmann::json obj, C2D_Image icn, std::string name) {
|
||||
LightLock_Lock(&lock);
|
||||
queueEntries.push_back( std::make_unique<Queue>(obj, icn, name) );
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
/* If not already running, let it run!! */
|
||||
if (!QueueRuns) {
|
||||
QueueRuns = true; // We enable the queue run state here.
|
||||
s32 prio = 0;
|
||||
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
|
||||
queueThread = threadCreate((ThreadFunc)QueueSystem::QueueHandle, NULL, 64 * 1024, prio - 1, -2, false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Clears the queue.
|
||||
*/
|
||||
void QueueSystem::ClearQueue() {
|
||||
QueueRuns = false;
|
||||
queueEntries.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
The whole handle.
|
||||
*/
|
||||
void QueueSystem::QueueHandle() {
|
||||
while(QueueRuns) {
|
||||
Result ret = NONE; // No Error as of yet.
|
||||
|
||||
LightLock_Lock(&lock);
|
||||
queueEntries[0]->total = queueEntries[0]->obj.size();
|
||||
queueEntries[0]->current = 0;
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
for(int i = 0; i < (int)queueEntries[0]->obj.size(); i++) {
|
||||
LightLock_Lock(&lock);
|
||||
queueEntries[0]->current++;
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
if (ret == NONE) {
|
||||
std::string type = "";
|
||||
|
||||
LightLock_Lock(&lock);
|
||||
if (queueEntries[0]->obj[i].contains("type") && queueEntries[0]->obj[i]["type"].is_string()) {
|
||||
type = queueEntries[0]->obj[i]["type"];
|
||||
|
||||
} else {
|
||||
ret = SYNTAX_ERROR;
|
||||
}
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
/* Deleting a file. */
|
||||
if (type == "deleteFile") {
|
||||
bool missing = false;
|
||||
std::string file = "";
|
||||
|
||||
|
||||
LightLock_Lock(&lock);
|
||||
if (queueEntries[0]->obj[i].contains("file") && queueEntries[0]->obj[i]["file"].is_string()) {
|
||||
file = queueEntries[0]->obj[i]["file"];
|
||||
} else missing = true;
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
|
||||
if (!missing) ret = ScriptUtils::removeFile(file, "");
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
/* Downloading from a URL. */
|
||||
} else if (type == "downloadFile") {
|
||||
LightLock_Lock(&lock);
|
||||
queueEntries[0]->status = QueueStatus::Downloading;
|
||||
|
||||
bool missing = false;
|
||||
std::string file = "", output = "";
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("file") && queueEntries[0]->obj[i]["file"].is_string()) {
|
||||
file = queueEntries[0]->obj[i]["file"];
|
||||
} else missing = true;
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("output") && queueEntries[0]->obj[i]["output"].is_string()) {
|
||||
output = queueEntries[0]->obj[i]["output"];
|
||||
} else missing = true;
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
if (!missing) ret = ScriptUtils::downloadFile(file, output, "");
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
/* Download from a GitHub Release. */
|
||||
} else if (type == "downloadRelease") {
|
||||
LightLock_Lock(&lock);
|
||||
queueEntries[0]->status = QueueStatus::Downloading;
|
||||
bool missing = false, includePrereleases = false;
|
||||
std::string repo = "", file = "", output = "";
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("repo") && queueEntries[0]->obj[i]["repo"].is_string()) {
|
||||
repo = queueEntries[0]->obj[i]["repo"];
|
||||
} else missing = true;
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("file") && queueEntries[0]->obj[i]["file"].is_string()) {
|
||||
file = queueEntries[0]->obj[i]["file"];
|
||||
} else missing = true;
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("output") && queueEntries[0]->obj[i]["output"].is_string()) {
|
||||
output = queueEntries[0]->obj[i]["output"];
|
||||
} else missing = true;
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("includePrereleases") && queueEntries[0]->obj[i]["includePrereleases"].is_boolean())
|
||||
includePrereleases = queueEntries[0]->obj[i]["includePrereleases"];
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
if (!missing) ret = ScriptUtils::downloadRelease(repo, file, output, includePrereleases, "");
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
/* Extracting files. */
|
||||
} else if (type == "extractFile") {
|
||||
LightLock_Lock(&lock);
|
||||
queueEntries[0]->status = QueueStatus::Extracting;
|
||||
bool missing = false;
|
||||
std::string file = "", input = "", output = "";
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("file") && queueEntries[0]->obj[i]["file"].is_string()) {
|
||||
file = queueEntries[0]->obj[i]["file"];
|
||||
} else missing = true;
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("input") && queueEntries[0]->obj[i]["input"].is_string()) {
|
||||
input = queueEntries[0]->obj[i]["input"];
|
||||
} else missing = true;
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("output") && queueEntries[0]->obj[i]["output"].is_string()) {
|
||||
output = queueEntries[0]->obj[i]["output"];
|
||||
} else missing = true;
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
if (!missing) ScriptUtils::extractFile(file, input, output, "");
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
/* Installing CIAs. */
|
||||
} else if (type == "installCia") {
|
||||
LightLock_Lock(&lock);
|
||||
queueEntries[0]->status = QueueStatus::Installing;
|
||||
bool missing = false, updateSelf = false;
|
||||
std::string file = "";
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("file") && queueEntries[0]->obj[i]["file"].is_string()) {
|
||||
file = queueEntries[0]->obj[i]["file"];
|
||||
} else missing = true;
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("updateSelf") && queueEntries[0]->obj[i]["updateSelf"].is_boolean()) {
|
||||
updateSelf = queueEntries[0]->obj[i]["updateSelf"];
|
||||
}
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
if (!missing) ScriptUtils::installFile(file, updateSelf, "");
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "mkdir") {
|
||||
bool missing = false;
|
||||
std::string directory = "";
|
||||
|
||||
LightLock_Lock(&lock);
|
||||
if (queueEntries[0]->obj[i].contains("directory") && queueEntries[0]->obj[i]["directory"].is_string()) {
|
||||
directory = queueEntries[0]->obj[i]["directory"];
|
||||
} else missing = true;
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
if (!missing) makeDirs(directory.c_str());
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "rmdir") {
|
||||
bool missing = false;
|
||||
std::string directory = "", message = "", promptmsg = "";
|
||||
|
||||
LightLock_Lock(&lock);
|
||||
if (queueEntries[0]->obj[i].contains("directory") && queueEntries[0]->obj[i]["directory"].is_string()) {
|
||||
directory = queueEntries[0]->obj[i]["directory"];
|
||||
} else missing = true;
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
promptmsg = Lang::get("DELETE_PROMPT") + "\n" + directory;
|
||||
if (!missing && directory != "") {
|
||||
if (access(directory.c_str(), F_OK) != 0) ret = DELETE_ERROR;
|
||||
else {
|
||||
if (Msg::promptMsg(promptmsg)) removeDirRecursive(directory.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "promptMessage" || type == "promptMsg") {
|
||||
std::string Message = "";
|
||||
int skipCount = -1;
|
||||
|
||||
LightLock_Lock(&lock);
|
||||
if (queueEntries[0]->obj[i].contains("message") && queueEntries[0]->obj[i]["message"].is_string()) {
|
||||
Message = queueEntries[0]->obj[i]["message"];
|
||||
}
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("count") && queueEntries[0]->obj[i]["count"].is_number()) {
|
||||
skipCount = queueEntries[0]->obj[i]["count"];
|
||||
}
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
Result res = ScriptUtils::prompt(Message);
|
||||
|
||||
if (skipCount > -1 && res == SCRIPT_CANCELED) i += skipCount; // Skip.
|
||||
|
||||
} else if (type == "exit") {
|
||||
ret = SCRIPT_CANCELED;
|
||||
|
||||
} else if (type == "copy") {
|
||||
std::string source = "", destination = "";
|
||||
bool missing = false;
|
||||
|
||||
LightLock_Lock(&lock);
|
||||
if (queueEntries[0]->obj[i].contains("source") && queueEntries[0]->obj[i]["source"].is_string()) {
|
||||
source = queueEntries[0]->obj[i]["source"];
|
||||
} else missing = true;
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("destination") && queueEntries[0]->obj[i]["destination"].is_string()) {
|
||||
destination = queueEntries[0]->obj[i]["destination"];
|
||||
} else missing = true;
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
if (!missing) ret = ScriptUtils::copyFile(source, destination, "");
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "move") {
|
||||
std::string oldFile = "", newFile = "";
|
||||
bool missing = false;
|
||||
|
||||
LightLock_Lock(&lock);
|
||||
if (queueEntries[0]->obj[i].contains("old") && queueEntries[0]->obj[i]["old"].is_string()) {
|
||||
oldFile = queueEntries[0]->obj[i]["old"];
|
||||
} else missing = true;
|
||||
|
||||
if (queueEntries[0]->obj[i].contains("new") && queueEntries[0]->obj[i]["new"].is_string()) {
|
||||
newFile = queueEntries[0]->obj[i]["new"];
|
||||
} else missing = true;
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
if (!missing) ret = ScriptUtils::renameFile(oldFile, newFile, "");
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "skip") {
|
||||
int skipCount = -1;
|
||||
|
||||
LightLock_Lock(&lock);
|
||||
if (queueEntries[0]->obj[i].contains("count") && queueEntries[0]->obj[i]["count"].is_number()) {
|
||||
skipCount = queueEntries[0]->obj[i]["count"];
|
||||
}
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
if (skipCount > 0) i += skipCount; // Skip.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LightLock_Lock(&lock);
|
||||
|
||||
/* Canceled or None is for me -> Done. */
|
||||
if (ret == NONE || ret == SCRIPT_CANCELED) {
|
||||
queueEntries[0]->status = QueueStatus::Done;
|
||||
|
||||
} else { // Else it failed..
|
||||
queueEntries[0]->status = QueueStatus::Failed;
|
||||
}
|
||||
|
||||
queueEntries.pop_front();
|
||||
if (queueEntries.empty()) QueueRuns = false; // The queue ended.
|
||||
ret = NONE; // Reset.
|
||||
|
||||
LightLock_Unlock(&lock);
|
||||
|
||||
/* TODO: Display a message if something ends? */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "lodepng.h"
|
||||
#include "msg.hpp"
|
||||
#include "screenshot.hpp"
|
||||
|
||||
C2D_Image Screenshot::Convert(const std::string &filename) {
|
||||
std::vector<u8> ImageBuffer;
|
||||
unsigned width, height;
|
||||
C2D_Image img;
|
||||
lodepng::decode(ImageBuffer, width, height, filename.c_str());
|
||||
|
||||
img.tex = new C3D_Tex;
|
||||
img.subtex = new Tex3DS_SubTexture({(u16)width, (u16)height, 0.0f, 1.0f, width / 512.0f, 1.0f - (height / 512.0f)});
|
||||
|
||||
C3D_TexInit(img.tex, 512, 512, GPU_RGBA8);
|
||||
C3D_TexSetFilter(img.tex, GPU_LINEAR, GPU_LINEAR);
|
||||
img.tex->border = 0xFFFFFFFF;
|
||||
C3D_TexSetWrap(img.tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER);
|
||||
|
||||
for (u32 x = 0; x < width && x < 512; x++) {
|
||||
for (u32 y = 0; y < height && y < 512; y++) {
|
||||
const u32 dstPos = ((((y >> 3) * (512 >> 3) + (x >> 3)) << 6) +
|
||||
((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) |
|
||||
((x & 4) << 2) | ((y & 4) << 3))) * 4;
|
||||
|
||||
const u32 srcPos = (y * width + x) * 4;
|
||||
((uint8_t *)img.tex->data)[dstPos + 0] = ImageBuffer.data()[srcPos + 3];
|
||||
((uint8_t *)img.tex->data)[dstPos + 1] = ImageBuffer.data()[srcPos + 2];
|
||||
((uint8_t *)img.tex->data)[dstPos + 2] = ImageBuffer.data()[srcPos + 1];
|
||||
((uint8_t *)img.tex->data)[dstPos + 3] = ImageBuffer.data()[srcPos + 0];
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
C2D_Image Screenshot::ConvertFromBuffer(const std::vector<u8> &buffer) {
|
||||
std::vector<u8> ImageBuffer;
|
||||
unsigned width, height;
|
||||
C2D_Image img;
|
||||
lodepng::decode(ImageBuffer, width, height, buffer);
|
||||
|
||||
img.tex = new C3D_Tex;
|
||||
img.subtex = new Tex3DS_SubTexture({(u16)width, (u16)height, 0.0f, 1.0f, width / 512.0f, 1.0f - (height / 512.0f)});
|
||||
|
||||
C3D_TexInit(img.tex, 512, 512, GPU_RGBA8);
|
||||
C3D_TexSetFilter(img.tex, GPU_LINEAR, GPU_LINEAR);
|
||||
img.tex->border = 0xFFFFFFFF;
|
||||
C3D_TexSetWrap(img.tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER);
|
||||
|
||||
for (u32 x = 0; x < width && x < 512; x++) {
|
||||
for (u32 y = 0; y < height && y < 512; y++) {
|
||||
const u32 dstPos = ((((y >> 3) * (512 >> 3) + (x >> 3)) << 6) +
|
||||
((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) |
|
||||
((x & 4) << 2) | ((y & 4) << 3))) * 4;
|
||||
|
||||
const u32 srcPos = (y * width + x) * 4;
|
||||
((uint8_t *)img.tex->data)[dstPos + 0] = ImageBuffer.data()[srcPos + 3];
|
||||
((uint8_t *)img.tex->data)[dstPos + 1] = ImageBuffer.data()[srcPos + 2];
|
||||
((uint8_t *)img.tex->data)[dstPos + 2] = ImageBuffer.data()[srcPos + 1];
|
||||
((uint8_t *)img.tex->data)[dstPos + 3] = ImageBuffer.data()[srcPos + 0];
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
@@ -0,0 +1,565 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "animation.hpp"
|
||||
#include "cia.hpp"
|
||||
#include "download.hpp"
|
||||
#include "extract.hpp"
|
||||
#include "fileBrowse.hpp"
|
||||
#include "files.hpp"
|
||||
#include "scriptUtils.hpp"
|
||||
#include <regex>
|
||||
#include <unistd.h>
|
||||
|
||||
extern bool showProgressBar;
|
||||
extern ProgressBar progressbarType;
|
||||
extern char progressBarMsg[128];
|
||||
extern int filesExtracted;
|
||||
|
||||
extern void downloadFailed();
|
||||
static Thread thread;
|
||||
|
||||
bool ScriptUtils::matchPattern(const std::string &pattern, const std::string &tested) {
|
||||
std::regex patternRegex(pattern);
|
||||
return regex_match(tested, patternRegex);
|
||||
}
|
||||
|
||||
/* Remove a File. */
|
||||
Result ScriptUtils::removeFile(const std::string &file, const std::string &message) {
|
||||
std::string out;
|
||||
out = std::regex_replace(file, std::regex("%ARCHIVE_DEFAULT%"), config->archPath());
|
||||
out = std::regex_replace(out, std::regex("%3DSX%"), config->_3dsxPath());
|
||||
out = std::regex_replace(out, std::regex("%NDS%"), config->ndsPath());
|
||||
|
||||
Result ret = NONE;
|
||||
if (access(out.c_str(), F_OK) != 0) return DELETE_ERROR;
|
||||
|
||||
Msg::DisplayMsg(message);
|
||||
deleteFile(out.c_str());
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Boot a title. */
|
||||
void ScriptUtils::bootTitle(const std::string &TitleID, bool isNAND, const std::string &message) {
|
||||
std::string MSG = Lang::get("BOOT_TITLE") + "\n\n";
|
||||
if (isNAND) MSG += Lang::get("MEDIATYPE_NAND") + "\n" + TitleID;
|
||||
else MSG += Lang::get("MEDIATYPE_SD") + "\n" + TitleID;
|
||||
|
||||
const u64 ID = std::stoull(TitleID, 0, 16);
|
||||
if (Msg::promptMsg(MSG)) {
|
||||
Msg::DisplayMsg(message);
|
||||
Title::Launch(ID, isNAND ? MEDIATYPE_NAND : MEDIATYPE_SD);
|
||||
}
|
||||
}
|
||||
|
||||
/* Prompt message. */
|
||||
Result ScriptUtils::prompt(const std::string &message) {
|
||||
Result ret = NONE;
|
||||
if (!Msg::promptMsg(message)) ret = SCRIPT_CANCELED;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Copy. */
|
||||
Result ScriptUtils::copyFile(const std::string &source, const std::string &destination, const std::string &message, bool isARG) {
|
||||
Result ret = NONE;
|
||||
if (access(source.c_str(), F_OK) != 0) return COPY_ERROR;
|
||||
|
||||
std::string _source, _dest;
|
||||
_source = std::regex_replace(source, std::regex("%ARCHIVE_DEFAULT%"), config->archPath());
|
||||
_source = std::regex_replace(_source, std::regex("%3DSX%"), config->_3dsxPath());
|
||||
_source = std::regex_replace(_source, std::regex("%NDS%"), config->ndsPath());
|
||||
_dest = std::regex_replace(destination, std::regex("%ARCHIVE_DEFAULT%"), config->archPath());
|
||||
_dest = std::regex_replace(_dest, std::regex("%3DSX%"), config->_3dsxPath());
|
||||
_dest = std::regex_replace(_dest, std::regex("%NDS%"), config->ndsPath());
|
||||
|
||||
if (isARG) {
|
||||
snprintf(progressBarMsg, sizeof(progressBarMsg), message.c_str());
|
||||
showProgressBar = true;
|
||||
progressbarType = ProgressBar::Copying;
|
||||
|
||||
s32 prio = 0;
|
||||
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
|
||||
thread = threadCreate((ThreadFunc)Animation::displayProgressBar, NULL, 64 * 1024, prio - 1, -2, false);
|
||||
}
|
||||
|
||||
/* If destination does not exist, create dirs. */
|
||||
if (access(_dest.c_str(), F_OK) != 0) makeDirs(_dest.c_str());
|
||||
ret = fcopy(_source.c_str(), _dest.c_str());
|
||||
|
||||
if (ret == -1) ret = COPY_ERROR;
|
||||
else if (ret == 1) ret = NONE;
|
||||
|
||||
if (isARG) {
|
||||
showProgressBar = false;
|
||||
threadJoin(thread, U64_MAX);
|
||||
threadFree(thread);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
Rename / Move a file.
|
||||
*/
|
||||
Result ScriptUtils::renameFile(const std::string &oldName, const std::string &newName, const std::string &message) {
|
||||
|
||||
Result ret = NONE;
|
||||
if (access(oldName.c_str(), F_OK) != 0) return MOVE_ERROR;
|
||||
|
||||
std::string old, _new;
|
||||
old = std::regex_replace(oldName, std::regex("%ARCHIVE_DEFAULT%"), config->archPath());
|
||||
old = std::regex_replace(old, std::regex("%3DSX%"), config->_3dsxPath());
|
||||
old = std::regex_replace(old, std::regex("%NDS%"), config->ndsPath());
|
||||
_new = std::regex_replace(newName, std::regex("%ARCHIVE_DEFAULT%"), config->archPath());
|
||||
_new = std::regex_replace(_new, std::regex("%3DSX%"), config->_3dsxPath());
|
||||
_new = std::regex_replace(_new, std::regex("%NDS%"), config->ndsPath());
|
||||
|
||||
Msg::DisplayMsg(message);
|
||||
|
||||
/* TODO: Kinda avoid that? */
|
||||
makeDirs(_new.c_str());
|
||||
rename(old.c_str(), _new.c_str());
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
Download from GitHub Release.
|
||||
*/
|
||||
Result ScriptUtils::downloadRelease(const std::string &repo, const std::string &file, const std::string &output, bool includePrereleases, const std::string &message, bool isARG) {
|
||||
std::string out;
|
||||
out = std::regex_replace(output, std::regex("%3DSX%"), config->_3dsxPath());
|
||||
out = std::regex_replace(out, std::regex("%NDS%"), config->ndsPath());
|
||||
out = std::regex_replace(out, std::regex("%ARCHIVE_DEFAULT%"), config->archPath());
|
||||
|
||||
Result ret = NONE;
|
||||
|
||||
if (isARG) {
|
||||
snprintf(progressBarMsg, sizeof(progressBarMsg), message.c_str());
|
||||
showProgressBar = true;
|
||||
progressbarType = ProgressBar::Downloading;
|
||||
|
||||
s32 prio = 0;
|
||||
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
|
||||
thread = threadCreate((ThreadFunc)Animation::displayProgressBar, NULL, 64 * 1024, prio - 1, -2, false);
|
||||
}
|
||||
|
||||
if (downloadFromRelease("https://github.com/" + repo, file, out, includePrereleases) != 0) {
|
||||
if (isARG) showProgressBar = false;
|
||||
|
||||
downloadFailed();
|
||||
ret = FAILED_DOWNLOAD;
|
||||
|
||||
if (isARG) {
|
||||
threadJoin(thread, U64_MAX);
|
||||
threadFree(thread);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (isARG) {
|
||||
showProgressBar = false;
|
||||
threadJoin(thread, U64_MAX);
|
||||
threadFree(thread);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
Download a file.
|
||||
*/
|
||||
Result ScriptUtils::downloadFile(const std::string &file, const std::string &output, const std::string &message, bool isARG) {
|
||||
std::string out;
|
||||
out = std::regex_replace(output, std::regex("%3DSX%"), config->_3dsxPath());
|
||||
out = std::regex_replace(out, std::regex("%NDS%"), config->ndsPath());
|
||||
out = std::regex_replace(out, std::regex("%ARCHIVE_DEFAULT%"), config->archPath());
|
||||
|
||||
Result ret = NONE;
|
||||
|
||||
if (isARG) {
|
||||
snprintf(progressBarMsg, sizeof(progressBarMsg), message.c_str());
|
||||
showProgressBar = true;
|
||||
progressbarType = ProgressBar::Downloading;
|
||||
|
||||
s32 prio = 0;
|
||||
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
|
||||
thread = threadCreate((ThreadFunc)Animation::displayProgressBar, NULL, 64 * 1024, prio - 1, -2, false);
|
||||
}
|
||||
|
||||
if (downloadToFile(file, out) != 0) {
|
||||
if (isARG) showProgressBar = false;
|
||||
|
||||
downloadFailed();
|
||||
ret = FAILED_DOWNLOAD;
|
||||
|
||||
if (isARG) {
|
||||
threadJoin(thread, U64_MAX);
|
||||
threadFree(thread);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (isARG) {
|
||||
showProgressBar = false;
|
||||
threadJoin(thread, U64_MAX);
|
||||
threadFree(thread);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
Install CIA files.
|
||||
*/
|
||||
void ScriptUtils::installFile(const std::string &file, bool updatingSelf, const std::string &message, bool isARG) {
|
||||
std::string in;
|
||||
in = std::regex_replace(file, std::regex("%ARCHIVE_DEFAULT%"), config->archPath());
|
||||
in = std::regex_replace(in, std::regex("%3DSX%"), config->_3dsxPath());
|
||||
in = std::regex_replace(in, std::regex("%NDS%"), config->ndsPath());
|
||||
|
||||
if (isARG) {
|
||||
snprintf(progressBarMsg, sizeof(progressBarMsg), message.c_str());
|
||||
showProgressBar = true;
|
||||
progressbarType = ProgressBar::Installing;
|
||||
|
||||
s32 prio = 0;
|
||||
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
|
||||
thread = threadCreate((ThreadFunc)Animation::displayProgressBar, NULL, 64 * 1024, prio - 1, -2, false);
|
||||
}
|
||||
|
||||
Title::Install(in.c_str(), updatingSelf);
|
||||
|
||||
if (isARG) {
|
||||
showProgressBar = false;
|
||||
threadJoin(thread, U64_MAX);
|
||||
threadFree(thread);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Extract files.
|
||||
*/
|
||||
void ScriptUtils::extractFile(const std::string &file, const std::string &input, const std::string &output, const std::string &message, bool isARG) {
|
||||
std::string out, in;
|
||||
in = std::regex_replace(file, std::regex("%ARCHIVE_DEFAULT%"), config->archPath());
|
||||
in = std::regex_replace(in, std::regex("%3DSX%"), config->_3dsxPath());
|
||||
in = std::regex_replace(in, std::regex("%NDS%"), config->ndsPath());
|
||||
out = std::regex_replace(output, std::regex("%ARCHIVE_DEFAULT%"), config->archPath());
|
||||
out = std::regex_replace(out, std::regex("%3DSX%"), config->_3dsxPath());
|
||||
out = std::regex_replace(out, std::regex("%NDS%"), config->ndsPath());
|
||||
|
||||
if (isARG) {
|
||||
snprintf(progressBarMsg, sizeof(progressBarMsg), message.c_str());
|
||||
showProgressBar = true;
|
||||
progressbarType = ProgressBar::Extracting;
|
||||
|
||||
s32 prio = 0;
|
||||
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
|
||||
thread = threadCreate((ThreadFunc)Animation::displayProgressBar, NULL, 64 * 1024, prio - 1, -2, false);
|
||||
}
|
||||
|
||||
filesExtracted = 0;
|
||||
|
||||
getExtractedSize(in, input);
|
||||
extractArchive(in, input, out);
|
||||
|
||||
if (isARG) {
|
||||
showProgressBar = false;
|
||||
threadJoin(thread, U64_MAX);
|
||||
threadFree(thread);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
NOTE: This is for the argument system for now. This might get replaced completely with the Queue System in the future.
|
||||
*/
|
||||
Result ScriptUtils::runFunctions(nlohmann::json storeJson, int selection, const std::string &entry) {
|
||||
Result ret = NONE; // No Error as of yet.
|
||||
|
||||
if (!storeJson.contains("storeContent")) { Msg::waitMsg(Lang::get("SYNTAX_ERROR")); return SYNTAX_ERROR; };
|
||||
if ((int)storeJson["storeContent"].size() < selection) { Msg::waitMsg(Lang::get("SYNTAX_ERROR")); return SYNTAX_ERROR; };
|
||||
if (!storeJson["storeContent"][selection].contains(entry)) { Msg::waitMsg(Lang::get("SYNTAX_ERROR")); return SYNTAX_ERROR; };
|
||||
|
||||
nlohmann::json Script = nullptr;
|
||||
|
||||
/* Detect if array or new object thing. Else return Syntax error. :P */
|
||||
if (storeJson["storeContent"][selection][entry].type() == nlohmann::json::value_t::array) {
|
||||
Script = storeJson["storeContent"][selection][entry];
|
||||
|
||||
} else if (storeJson["storeContent"][selection][entry].type() == nlohmann::json::value_t::object) {
|
||||
if (storeJson["storeContent"][selection][entry].contains("script") && storeJson["storeContent"][selection][entry]["script"].is_array()) {
|
||||
Script = storeJson["storeContent"][selection][entry]["script"];
|
||||
|
||||
} else {
|
||||
Msg::waitMsg(Lang::get("SYNTAX_ERROR"));
|
||||
return SYNTAX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(int i = 0; i < (int)Script.size(); i++) {
|
||||
if (ret == NONE) {
|
||||
std::string type = "";
|
||||
|
||||
if (Script[i].contains("type") && Script[i]["type"].is_string()) {
|
||||
type = Script[i]["type"];
|
||||
|
||||
} else {
|
||||
ret = SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
if (type == "deleteFile") {
|
||||
bool missing = false;
|
||||
std::string file = "", message = "";
|
||||
|
||||
|
||||
if (Script[i].contains("file") && Script[i]["file"].is_string()) {
|
||||
file = Script[i]["file"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("message") && Script[i]["message"].is_string()) {
|
||||
message = Script[i]["message"];
|
||||
}
|
||||
|
||||
if (!missing) ret = ScriptUtils::removeFile(file, message);
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "downloadFile") {
|
||||
bool missing = false;
|
||||
std::string file = "", output = "", message = "";
|
||||
|
||||
if (Script[i].contains("file") && Script[i]["file"].is_string()) {
|
||||
file = Script[i]["file"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("output") && Script[i]["output"].is_string()) {
|
||||
output = Script[i]["output"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("message") && Script[i]["message"].is_string()) {
|
||||
message = Script[i]["message"];
|
||||
}
|
||||
|
||||
if (!missing) ret = ScriptUtils::downloadFile(file, output, message, true);
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "downloadRelease") {
|
||||
bool missing = false, includePrereleases = false;
|
||||
std::string repo = "", file = "", output = "", message = "";
|
||||
|
||||
if (Script[i].contains("repo") && Script[i]["repo"].is_string()) {
|
||||
repo = Script[i]["repo"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("file") && Script[i]["file"].is_string()) {
|
||||
file = Script[i]["file"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("output") && Script[i]["output"].is_string()) {
|
||||
output = Script[i]["output"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("includePrereleases") && Script[i]["includePrereleases"].is_boolean())
|
||||
includePrereleases = Script[i]["includePrereleases"];
|
||||
|
||||
if (Script[i].contains("message") && Script[i]["message"].is_string()) {
|
||||
message = Script[i]["message"];
|
||||
}
|
||||
|
||||
if (!missing) ret = ScriptUtils::downloadRelease(repo, file, output, includePrereleases, message, true);
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "extractFile") {
|
||||
bool missing = false;
|
||||
std::string file = "", input = "", output = "", message = "";
|
||||
|
||||
if (Script[i].contains("file") && Script[i]["file"].is_string()) {
|
||||
file = Script[i]["file"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("input") && Script[i]["input"].is_string()) {
|
||||
input = Script[i]["input"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("output") && Script[i]["output"].is_string()) {
|
||||
output = Script[i]["output"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("message") && Script[i]["message"].is_string()) {
|
||||
message = Script[i]["message"];
|
||||
}
|
||||
|
||||
if (!missing) ScriptUtils::extractFile(file, input, output, message, true);
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "installCia") {
|
||||
bool missing = false, updateSelf = false;
|
||||
std::string file = "", message = "";
|
||||
|
||||
if (Script[i].contains("file") && Script[i]["file"].is_string()) {
|
||||
file = Script[i]["file"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("updateSelf") && Script[i]["updateSelf"].is_boolean()) {
|
||||
updateSelf = Script[i]["updateSelf"];
|
||||
}
|
||||
|
||||
if (Script[i].contains("message") && Script[i]["message"].is_string()) {
|
||||
message = Script[i]["message"];
|
||||
}
|
||||
|
||||
if (!missing) ScriptUtils::installFile(file, updateSelf, message, true);
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "mkdir") {
|
||||
bool missing = false;
|
||||
std::string directory = "", message = "";
|
||||
|
||||
if (Script[i].contains("directory") && Script[i]["directory"].is_string()) {
|
||||
directory = Script[i]["directory"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (!missing) makeDirs(directory.c_str());
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "rmdir") {
|
||||
bool missing = false;
|
||||
std::string directory = "", message = "", promptmsg = "";
|
||||
|
||||
if (Script[i].contains("directory") && Script[i]["directory"].is_string()) {
|
||||
directory = Script[i]["directory"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
promptmsg = Lang::get("DELETE_PROMPT") + "\n" + directory;
|
||||
if (!missing && directory != "") {
|
||||
if (access(directory.c_str(), F_OK) != 0) ret = DELETE_ERROR;
|
||||
else {
|
||||
if (Msg::promptMsg(promptmsg)) removeDirRecursive(directory.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "promptMessage" || type == "promptMsg") {
|
||||
std::string Message = "";
|
||||
int skipCount = -1;
|
||||
|
||||
if (Script[i].contains("message") && Script[i]["message"].is_string()) {
|
||||
Message = Script[i]["message"];
|
||||
}
|
||||
|
||||
if (Script[i].contains("count") && Script[i]["count"].is_number()) {
|
||||
skipCount = Script[i]["count"];
|
||||
}
|
||||
|
||||
ret = ScriptUtils::prompt(Message);
|
||||
|
||||
if (skipCount > -1 && ret == SCRIPT_CANCELED) {
|
||||
ret = NONE;
|
||||
i += skipCount; // Skip.
|
||||
}
|
||||
|
||||
} else if (type == "exit") {
|
||||
break;
|
||||
|
||||
} else if (type == "copy") {
|
||||
std::string Message = "", source = "", destination = "";
|
||||
bool missing = false;
|
||||
|
||||
if (Script[i].contains("source") && Script[i]["source"].is_string()) {
|
||||
source = Script[i]["source"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("destination") && Script[i]["destination"].is_string()) {
|
||||
destination = Script[i]["destination"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("message") && Script[i]["message"].is_string()) {
|
||||
Message = Script[i]["message"];
|
||||
}
|
||||
|
||||
if (!missing) ret = ScriptUtils::copyFile(source, destination, Message);
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "move") {
|
||||
std::string Message = "", oldFile = "", newFile = "";
|
||||
bool missing = false;
|
||||
|
||||
if (Script[i].contains("old") && Script[i]["old"].is_string()) {
|
||||
oldFile = Script[i]["old"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("new") && Script[i]["new"].is_string()) {
|
||||
newFile = Script[i]["new"];
|
||||
}
|
||||
else missing = true;
|
||||
|
||||
if (Script[i].contains("message") && Script[i]["message"].is_string()) {
|
||||
Message = Script[i]["message"];
|
||||
}
|
||||
|
||||
if (!missing) ret = ScriptUtils::renameFile(oldFile, newFile, Message);
|
||||
else ret = SYNTAX_ERROR;
|
||||
|
||||
} else if (type == "skip") {
|
||||
int skipCount = -1;
|
||||
|
||||
if (Script[i].contains("count") && Script[i]["count"].is_number()) {
|
||||
skipCount = Script[i]["count"];
|
||||
}
|
||||
|
||||
if (skipCount > 0) {
|
||||
i += skipCount; // Skip.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == NONE || ret == SCRIPT_CANCELED) doneMsg();
|
||||
else if (ret == FAILED_DOWNLOAD) Msg::waitMsg(Lang::get("DOWNLOAD_ERROR"));
|
||||
else if (ret == SYNTAX_ERROR) Msg::waitMsg(Lang::get("SYNTAX_ERROR"));
|
||||
else if (ret == COPY_ERROR) Msg::waitMsg(Lang::get("COPY_ERROR"));
|
||||
else if (ret == MOVE_ERROR) Msg::waitMsg(Lang::get("MOVE_ERROR"));
|
||||
else if (ret == DELETE_ERROR) Msg::waitMsg(Lang::get("DELETE_ERROR"));
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "sound.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
/*
|
||||
Reference: http://yannesposito.com/Scratch/en/blog/2010-10-14-Fun-with-wav/
|
||||
*/
|
||||
typedef struct _WavHeader {
|
||||
char magic[4]; // "RIFF"
|
||||
u32 totallength; // Total file length, minus 8.
|
||||
char wavefmt[8]; // Should be "WAVEfmt ".
|
||||
u32 format; // 16 for PCM format.
|
||||
u16 pcm; // 1 for PCM format.
|
||||
u16 channels; // Channels.
|
||||
u32 frequency; // Sampling frequency.
|
||||
u32 bytes_per_second;
|
||||
u16 bytes_by_capture;
|
||||
u16 bits_per_sample;
|
||||
char data[4]; // "data".
|
||||
u32 bytes_in_data;
|
||||
} WavHeader;
|
||||
static_assert(sizeof(WavHeader) == 44, "WavHeader size is not 44 bytes.");
|
||||
|
||||
#define _MAX_SIZE 10485760 // 10 MiB.
|
||||
|
||||
/*
|
||||
Initialize the sound.
|
||||
|
||||
const std::string &path: Path to the file to play.
|
||||
const int channel: The channel to use. 1 by default.
|
||||
const bool toloop: If should loop or not. True by default.
|
||||
*/
|
||||
Sound::Sound(const std::string &path, const int channel, const bool toloop) {
|
||||
ndspSetOutputMode(NDSP_OUTPUT_MONO);
|
||||
ndspSetOutputCount(2); // Amount of buffers.
|
||||
|
||||
/* Reading wav file. */
|
||||
FILE *file = fopen(path.c_str(), "rb");
|
||||
|
||||
if (!file) {
|
||||
printf("Could not open the WAV file: %s.\n", path.c_str());
|
||||
this->good = false;
|
||||
return;
|
||||
}
|
||||
|
||||
WavHeader wavHeader;
|
||||
size_t read = fread(&wavHeader, 1, sizeof(wavHeader), file);
|
||||
if (read != sizeof(wavHeader)) {
|
||||
/* Short read. */
|
||||
printf("WAV file header is too short: %s.\n", path.c_str());
|
||||
fclose(file);
|
||||
this->good = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Verify the header. */
|
||||
static const char RIFF_magic[4] = { 'R','I','F','F' };
|
||||
if (memcmp(wavHeader.magic, RIFF_magic, sizeof(wavHeader.magic)) != 0) {
|
||||
/* Incorrect magic number. */
|
||||
printf("Wrong file format.\n");
|
||||
fclose(file);
|
||||
this->good = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (wavHeader.totallength == 0 ||
|
||||
(wavHeader.channels != 1 && wavHeader.channels != 2) ||
|
||||
(wavHeader.bits_per_sample != 8 && wavHeader.bits_per_sample != 16)) {
|
||||
/* Unsupported WAV file. */
|
||||
printf("Corrupted wav file.\n");
|
||||
fclose(file);
|
||||
this->good = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the file size. */
|
||||
fseek(file, 0, SEEK_END);
|
||||
this->dataSize = ftell(file) - sizeof(wavHeader);
|
||||
|
||||
if (this->dataSize > _MAX_SIZE) {
|
||||
fclose(file);
|
||||
this->good = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocating and reading samples. */
|
||||
this->data = reinterpret_cast<u8 *>(linearAlloc(this->dataSize));
|
||||
fseek(file, 44, SEEK_SET);
|
||||
fread(this->data, 1, this->dataSize, file);
|
||||
fclose(file);
|
||||
|
||||
//if (wavHeader.bits_per_sample == 16) this->dataSize /= 2; // Not sure.. if that is actually needed at all.
|
||||
|
||||
this->chnl = channel;
|
||||
ndspChnReset(this->chnl);
|
||||
ndspChnSetInterp(this->chnl, NDSP_INTERP_NONE);
|
||||
ndspChnSetRate(this->chnl, float(wavHeader.frequency));
|
||||
ndspChnSetFormat(this->chnl, ((wavHeader.bits_per_sample == 8) ? NDSP_FORMAT_MONO_PCM8 : NDSP_FORMAT_MONO_PCM16));
|
||||
|
||||
/* Create and play a wav buffer. */
|
||||
memset(&this->waveBuf, 0, sizeof(this->waveBuf));
|
||||
|
||||
this->waveBuf.data_vaddr = reinterpret_cast<u32 *>(this->data);
|
||||
this->waveBuf.nsamples = this->dataSize / (wavHeader.bits_per_sample >> 3);
|
||||
this->waveBuf.looping = toloop;
|
||||
this->waveBuf.status = NDSP_WBUF_FREE;
|
||||
}
|
||||
|
||||
Sound::~Sound() {
|
||||
if (this->good) {
|
||||
this->waveBuf.data_vaddr = 0;
|
||||
this->waveBuf.nsamples = 0;
|
||||
this->waveBuf.looping = false;
|
||||
this->waveBuf.status = 0;
|
||||
ndspChnWaveBufClear(this->chnl);
|
||||
|
||||
if (this->data) linearFree(this->data);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Plays the sound.
|
||||
*/
|
||||
void Sound::play() {
|
||||
if (!this->data || !this->good) return;
|
||||
|
||||
DSP_FlushDataCache(this->data, this->dataSize);
|
||||
ndspChnWaveBufAdd(this->chnl, &this->waveBuf);
|
||||
}
|
||||
|
||||
/*
|
||||
Stops the sound.
|
||||
*/
|
||||
void Sound::stop() {
|
||||
if (!this->data || !this->good) return;
|
||||
|
||||
ndspChnWaveBufClear(this->chnl);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* This file is part of Universal-Updater
|
||||
* Copyright (C) 2019-2020 Universal-Team
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "common.hpp"
|
||||
#include "stringutils.hpp"
|
||||
|
||||
/*
|
||||
Pour la conversion en minuscules.
|
||||
const std::string &str : La chaîne qui doit être convertie.
|
||||
*/
|
||||
std::string StringUtils::lower_case(const std::string &str) {
|
||||
std::string lower;
|
||||
transform(str.begin(), str.end(), std::back_inserter(lower), tolower); // Transformez la chaîne en minuscules.
|
||||
|
||||
return lower;
|
||||
}
|
||||
|
||||
/*
|
||||
Récupère les chaînes d’un vecteur et les retourne comme une seule chaîne.
|
||||
std::vector<std::string> fetch : Le vecteur.
|
||||
*/
|
||||
std::string StringUtils::FetchStringsFromVector(const std::vector<std::string> &fetch) {
|
||||
std::string temp;
|
||||
|
||||
if (fetch.size() < 1) return ""; // Plus petit que 1 --> Retour vide.
|
||||
|
||||
for (int i = 0; i < (int)fetch.size(); i++) {
|
||||
if (i != (int)fetch.size() - 1) {
|
||||
temp += fetch[i] + ", ";
|
||||
|
||||
} else {
|
||||
temp += fetch[i];
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/*
|
||||
Adapté de l’analyse d’octets de GM9i.
|
||||
*/
|
||||
std::string StringUtils::formatBytes(int bytes) {
|
||||
char out[32];
|
||||
|
||||
if (bytes == 1) snprintf(out, sizeof(out), "%d Byte", bytes);
|
||||
else if (bytes < 1024) snprintf(out, sizeof(out), "%d Bytes", bytes);
|
||||
else if (bytes < 1024 * 1024) snprintf(out, sizeof(out), "%.1f KB", (float)bytes / 1024);
|
||||
else if (bytes < 1024 * 1024 * 1024) snprintf(out, sizeof(out), "%.1f MB", (float)bytes / 1024 / 1024);
|
||||
else snprintf(out, sizeof(out), "%.1f GB", (float)bytes / 1024 / 1024 / 1024);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
Renvoie un vecteur de toutes les marques.
|
||||
*/
|
||||
std::vector<std::string> StringUtils::GetMarks(int marks) {
|
||||
std::vector<std::string> out;
|
||||
|
||||
if (marks & favoriteMarks::STAR) out.push_back( "★" );
|
||||
if (marks & favoriteMarks::HEART) out.push_back( "♥" );
|
||||
if (marks & favoriteMarks::DIAMOND) out.push_back( "♦" );
|
||||
if (marks & favoriteMarks::CLUBS) out.push_back( "♣" );
|
||||
if (marks & favoriteMarks::SPADE) out.push_back( "♠" );
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
Renvoie une chaîne de toutes les marques.
|
||||
*/
|
||||
std::string StringUtils::GetMarkString(int marks) {
|
||||
std::string out;
|
||||
|
||||
if (marks & favoriteMarks::STAR) out += "★";
|
||||
if (marks & favoriteMarks::HEART) out += "♥";
|
||||
if (marks & favoriteMarks::DIAMOND) out += "♦";
|
||||
if (marks & favoriteMarks::CLUBS) out += "♣";
|
||||
if (marks & favoriteMarks::SPADE) out += "♠";
|
||||
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user