This commit is contained in:
Ghost0159
2021-02-06 20:40:11 +01:00
parent cec3330443
commit c1e86c9d82
255 changed files with 54213 additions and 0 deletions
+142
View File
@@ -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;
}
}
+82
View File
@@ -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 lanalyseur darguments.
const std::string &file : Const Référence au fichier.
const std::string &entry : Const Référence au nom du titre de lentrée.
int dlIndex : Lindex 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 lentrée de largument, 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);
}
}
}
+165
View File
@@ -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;
}
+183
View File
@@ -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 lapplication à 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 nexiste 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 na 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 daide. */
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
+139
View File
@@ -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 = "";
/* Cest 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;
}
+284
View File
@@ -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 sil nexiste 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 dinformations 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 nexiste 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;
/*
Lopé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;
}
}
+130
View File
@@ -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;
}
+55
View File
@@ -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
+318
View File
@@ -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? */
}
}
+91
View File
@@ -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;
}
+565
View File
@@ -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;
}
+166
View File
@@ -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);
}
+105
View File
@@ -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 dun 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 lanalyse doctets 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;
}