merge with current dev branch

This commit is contained in:
mrdude2478 2022-05-05 11:27:05 +01:00
parent b5ebb98d04
commit 8086938e16
22 changed files with 196 additions and 68 deletions

View File

@ -44,7 +44,7 @@ DATA := data
INCLUDES := include include/ui include/data include/install include/nx include/nx/ipc include/util $(CURDIR)/include/Plutonium/Plutonium/include
APP_TITLE := TinWoo Installer
APP_AUTHOR := MrDude
APP_VERSION := 1.0.5
APP_VERSION := 1.0.7
ROMFS := romfs
APP_ICON := icon.jpg

View File

@ -1,51 +1,3 @@
# TinWoo
A No-Bullshit-No-Bullshit NSP, NSZ, XCI, and XCZ Installer for Nintendo Switch.
# TinWoo DEV Branch
![Main Page](https://i.imgur.com/QOi0Yvv.jpg)
## Features
- Installs NSP/NSZ/XCI/XCZ files and split NSP/XCI files from your SD card.
- Installs NSP/NSZ/XCI/XCZ files over LAN or USB from tools such as [NS-USBloader](https://github.com/developersu/ns-usbloader).
- Installs Split NSP/XCI/NSZ/XCZ over Lan or USB using [NS-USBloader(Mod)](https://mega.nz/file/I4p2gCCK#32GwAGtIcL3FVH-V-8Goae_hpnK8FQ0eS2PwLDOW6X4).
- Installs NSP/NSZ/XCI/XCZ files over the internet by URL or Google Drive.
- Installs NSP/NSZ/XCI/XCZ files from a Hard Drive (NTFS/Fat32/ExFat/EXT3/EXT4).
- Verifies NCAs by header signature before they're installed.
- Installs and manages the latest signature patches quickly and easily.
- Works on SX OS and Atmosphere.
- Able to theme, change install sounds.
## Thanks to
- Blawar, Hunterweb, DarkMatterCore, XorTroll
## Modified Code
This code was prominently modified by MrDude on 25/04/2022 to be able to build with new plutonium and up to date libnx.
## Building All componenets of TinWoo at once
cd into the tinwoo folder then "make".
## Build TinWoo components individually
First, build and install usb libs - "make libusb".\
Second, built Plutonium - "make plutonium".\
Third, Make Tinwoo - "make tinwwoo".
## Cleanup TinWoo once built
First, "make libusbclean".\
Second, "make cleanplutonium".\
Third, "make clean".
## Note
This is a work in progress and lets you build with new libnx, plutonium packages. Some stuff still needs fixed to work with the new plutonium and libnx changes.
## Stuff still to fix
~~All known bugs fixed~~ \
## Build Issues
Make sure you are using Libnx build at least 9865dbf9 version.
git clone --recursive https://github.com/switchbrew/libnx.git \
cd libnx \
git checkout 9865dbf9 \
make install
## Check libnx version
pacman -Q --info libnx
Don't use this branch - it's only for experimental purposes.

View File

@ -20,6 +20,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <vector>
extern bool netConnected;
namespace netInstStuff {
void installTitleNet(std::vector<std::string> ourUrlList, int ourStorage, std::vector<std::string> urlListAltNames, std::string ourSource);

View File

@ -5,10 +5,12 @@
namespace inst::config {
static const std::string appDir = "sdmc:/switch/tinwoo";
static const std::string configPath = appDir + "/config.json";
static const std::string appVersion = "1.0.5";
static const std::string appVersion = "1.0.7";
extern std::string gAuthKey;
extern std::string sigPatchesUrl;
extern std::string httpIndexUrl;
extern std::string httplastUrl;
extern std::vector<std::string> updateInfo;
extern int languageSetting;
extern bool ignoreReqVers;

View File

@ -59,6 +59,8 @@
"alt_name": "Google Drive Datei",
"source_string": " von Google Drive"
},
"index_error": "No games found!",
"index_error_info": "Did you type the correct URL?",
"top_info": "Wähle Dateien zur Installation vom Server aus und drücke dann \ue0b3 !",
"top_info1": "Warte auf eine Verbindung... Die IP Adresse deiner Switch ist: ",
"failed": "Ferninstallation fehlgeschlagen!",

View File

@ -59,12 +59,14 @@
"alt_name": "Google Drive File",
"source_string": " from Google Drive"
},
"index_error": "No games found!",
"index_error_info": "Did you type the correct URL?",
"top_info": "Select what files you want to install from the server, then press the Plus button!",
"top_info1": "Waiting for a connection... Your Switch's IP Address is: ",
"failed": "Failed to perform remote install!",
"transfer_interput": "An error occured during data transfer. Check your network connection.",
"source_string": " over local network",
"buttons": "\ue0e3 Install Over Internet \ue0e2 Help \ue0e1 Cancel",
"buttons": "\ue0e3 Install Over Internet \ue0f0 Install From HTTP Directory \ue0e2 Help \ue0e1 Cancel",
"buttons1": "\ue0e0 Select File \ue0e3 Select All \ue0ef Install File(s) \ue0e1 Cancel"
},
"sd": {

View File

@ -59,6 +59,8 @@
"alt_name": "Fichier Google Drive",
"source_string": " à partir de Google Drive"
},
"index_error": "No games found!",
"index_error_info": "Did you type the correct URL?",
"top_info": "Sélectionnez les fichiers que vous voulez installer à partir du serveur, puis appuyez sur le bouton Plus !",
"top_info1": "En attente d'une connexion... L'adresse IP de votre switch est : ",
"failed": "Echec de l'installation à distance !",

View File

@ -59,6 +59,8 @@
"alt_name": "File Google Drive",
"source_string": " da Google Drive"
},
"index_error": "No games found!",
"index_error_info": "Did you type the correct URL?",
"top_info": "Seleziona quali file vuoi installare dal server, poi premi il tasto Più!",
"top_info1": "Aspettando una connnessione... L'indirizzo IP è: ",
"failed": "Impossibile eseguire l'installazione remota!",

View File

@ -59,6 +59,8 @@
"alt_name": "Googleドライブファイル",
"source_string": " Googleドライブから"
},
"index_error": "No games found!",
"index_error_info": "Did you type the correct URL?",
"top_info": "サーバーからインストールするファイルを選択し、+ボタンを押してください!",
"top_info1": "接続を待機しています...スイッチのIPアドレスは次のとおりです。: ",
"failed": "リモートインストールを実行できませんでした!",

View File

@ -59,6 +59,8 @@
"alt_name": "Файл Google Drive",
"source_string": " из Google Drive"
},
"index_error": "No games found!",
"index_error_info": "Did you type the correct URL?",
"top_info": "Выберите какие файлы вы хотите установить из сервера и нажмите \"+\"!",
"top_info1": "Ожидание подключения... IP-адрес вашего Switch: ",
"failed": "Не удалось выполнить установку из удалённого источника!",

View File

@ -59,6 +59,8 @@
"alt_name": "Google網路硬碟檔案",
"source_string": "來源為Google網路硬碟"
},
"index_error": "No games found!",
"index_error_info": "Did you type the correct URL?",
"top_info": "選定要從伺服器安裝的檔案後,請按+鈕",
"top_info1": "正在等待連線中... 你的Switch主機IP是: ",
"failed": "遠端安裝失敗!",

View File

@ -49,6 +49,7 @@ const unsigned int MAX_URLS = 256;
const int REMOTE_INSTALL_PORT = 2000;
static int m_serverSocket = 0;
static int m_clientSocket = 0;
bool netConnected = false;
namespace inst::ui {
extern MainApplication *mainApp;
@ -245,18 +246,21 @@ namespace netInstStuff{
if (m_serverSocket <= 0)
{
THROW_FORMAT("Server socket failed to initialize.\n");
close(m_serverSocket); //close if already open.
m_serverSocket = 0; //reset so we can try again.
}
}
std::string ourIPAddress = inst::util::getIPAddress();
inst::ui::mainApp->netinstPage->pageInfoText->SetText("inst.net.top_info1"_lang + ourIPAddress);
inst::ui::mainApp->CallForRender();
netConnected = false;
LOG_DEBUG("%s %s\n", "Switch IP is ", ourIPAddress.c_str());
LOG_DEBUG("%s\n", "Waiting for network");
LOG_DEBUG("%s\n", "B to cancel");
std::vector<std::string> urls;
while (true)
{
padUpdate(&pad);
@ -283,6 +287,84 @@ namespace netInstStuff{
{
inst::ui::mainApp->CreateShowDialog("inst.net.help.title"_lang, "inst.net.help.desc"_lang, {"common.ok"_lang}, true);
}
if (kDown & HidNpadButton_Minus) {
std::string url = inst::util::softwareKeyboard("inst.net.url.hint"_lang, inst::config::httpIndexUrl, 500);
if(url == "") {
url = "http://127.0.0.1";
}
else {
inst::config::httpIndexUrl = url;
inst::config::setConfig();
std::string response;
if (inst::util::formatUrlString(url) == "" || url == "https://" || url == "http://")
inst::ui::mainApp->CreateShowDialog("inst.net.url.invalid"_lang, "", {"common.ok"_lang}, false);
else {
if (url[url.size() - 1] != '/')
url += '/';
response = inst::curl::downloadToBuffer(url);
}
if (!response.empty()) {
if (response[0] == '{')
try {
nlohmann::json j = nlohmann::json::parse(response);
for (const auto &file : j["files"]) {
urls.push_back(file["url"]);
}
return urls;
}
catch (const nlohmann::detail::exception& ex) {
LOG_DEBUG("Failed to parse JSON\n");
}
else if (response[0] == '<') {
std::size_t index = 0;
while (index < response.size()) {
std::string link;
auto found = response.find("href=\"", index);
if (found == std::string::npos)
break;
index = found + 6;
while (index < response.size()) {
if (response[index] == '"') {
if (link.find("../") == std::string::npos)
if (link.find(".nsp") != std::string::npos || link.find(".nsz") != std::string::npos || link.find(".xci") != std::string::npos || link.find(".xcz") != std::string::npos)
urls.push_back(link);
break;
}
link += response[index++];
}
}
if (urls.size() > 0){
/*
//debug
FILE * fp;
std::string debug = urls[0];
const char *info = debug.c_str();
fp = fopen ("debug.txt", "w+");
fprintf(fp, "%s", info);
fclose(fp);
*/
return urls;
}
LOG_DEBUG("Failed to parse games from HTML\n");
}
}
else {
LOG_DEBUG("Failed to fetch game list\n");
inst::ui::mainApp->CreateShowDialog("inst.net.index_error"_lang, "inst.net.index_error_info"_lang, {"common.ok"_lang}, true);
}
}
}
struct sockaddr_in client;
socklen_t clientLen = sizeof(client);
@ -329,6 +411,8 @@ namespace netInstStuff{
}
catch (std::runtime_error& e)
{
close(m_serverSocket);
m_serverSocket = 0;
LOG_DEBUG("Failed to perform remote install!\n");
LOG_DEBUG("%s", e.what());
fprintf(stdout, "%s", e.what());

View File

@ -238,12 +238,13 @@ public:
u64 processChunk(const u8* ptr, u64 sz)
{
ZSTD_inBuffer input = { ptr, sz, 0 };
ZSTD_outBuffer output = { buffOut, buffOutSize, 0 };
m_deflateBuffer.resize(sz);
m_deflateBuffer.resize(0);
while (input.pos < input.size)
while (input.pos < input.size || output.pos > 0)
{
ZSTD_outBuffer output = { buffOut, buffOutSize, 0 };
output = { buffOut, buffOutSize, 0 };
size_t const ret = ZSTD_decompressStream(dctx, &output, &input);
if (ZSTD_isError(ret))

View File

@ -192,5 +192,11 @@ namespace inst::ui {
}
if (this->selectedTitles.size() > 0) this->startInstall();
}
if (Down & HidNpadButton_ZL)
this->menu->SetSelectedIndex(std::max(0, this->menu->GetSelectedIndex() - 6));
if (Down & HidNpadButton_ZR)
this->menu->SetSelectedIndex(std::min((s32)this->menu->GetItems().size() - 1, this->menu->GetSelectedIndex() + 6));
}
}

View File

@ -16,7 +16,7 @@ namespace inst::ui {
extern MainApplication *mainApp;
s32 xxx=0;
std::string lastUrl = "https://";
std::string httplastUrl = "http://";
std::string lastFileID = "";
std::string sourceString = "";
@ -39,11 +39,9 @@ namespace inst::ui {
this->appVersionText = TextBlock::New(1210, 680, "");
}
this->appVersionText->SetColor(COLOR("#FFFFFFFF"));
//this->pageInfoText = TextBlock::New(10, 109, "", 30);
this->pageInfoText = TextBlock::New(10, 109, "");
this->pageInfoText->SetFont(pu::ui::MakeDefaultFontName(30));
this->pageInfoText->SetColor(COLOR("#FFFFFFFF"));
//this->butText = TextBlock::New(10, 678, "", 24);
this->butText = TextBlock::New(10, 678, "");
this->butText->SetColor(COLOR("#FFFFFFFF"));
this->menu = pu::ui::elm::Menu::New(0, 156, 1280, COLOR("#FFFFFF00"), COLOR("#4f4f4d33"), 84, (506 / 84));
@ -95,21 +93,34 @@ namespace inst::ui {
void netInstPage::startNetwork() {
this->butText->SetText("inst.net.buttons"_lang);
//this->butText->SetText("inst.net.buttons"_lang + " \ue0f0 Install From HTTP Directory");
this->menu->SetVisible(false);
this->menu->ClearItems();
this->infoImage->SetVisible(true);
mainApp->LoadLayout(mainApp->netinstPage);
this->ourUrls = netInstStuff::OnSelected();
if (!this->ourUrls.size()) {
mainApp->LoadLayout(mainApp->mainPage);
return;
} else if (this->ourUrls[0] == "supplyUrl") {
}
else if (this->ourUrls[0] == "supplyUrl") {
std::string keyboardResult;
switch (mainApp->CreateShowDialog("inst.net.src.title"_lang, "common.cancel_desc"_lang, {"inst.net.src.opt0"_lang, "inst.net.src.opt1"_lang}, false)) {
case 0:
keyboardResult = inst::util::softwareKeyboard("inst.net.url.hint"_lang, lastUrl, 500);
keyboardResult = inst::util::softwareKeyboard("inst.net.url.hint"_lang, inst::config::httplastUrl, 500);
if (keyboardResult.size() > 0) {
lastUrl = keyboardResult;
httplastUrl = keyboardResult;
if(keyboardResult == "") {
keyboardResult = "http://127.0.0.1";
}
else {
inst::config::httplastUrl = keyboardResult;
inst::config::setConfig();
}
if (inst::util::formatUrlString(keyboardResult) == "" || keyboardResult == "https://" || keyboardResult == "http://") {
mainApp->CreateShowDialog("inst.net.url.invalid"_lang, "", {"common.ok"_lang}, false);
break;
@ -139,6 +150,7 @@ namespace inst::ui {
} else {
mainApp->CallForRender(); // If we re-render a few times during this process the main screen won't flicker
sourceString = "inst.net.source_string"_lang;
netConnected = true;
this->pageInfoText->SetText("inst.net.top_info"_lang);
this->butText->SetText("inst.net.buttons1"_lang);
this->drawMenuItems(true);
@ -177,7 +189,28 @@ namespace inst::ui {
if (hidGetTouchScreenStates(&state, 1)) {
if ((Down & HidNpadButton_A) || (state.count != xxx))
if (netConnected) {
if ((Down & HidNpadButton_A) || (state.count != xxx))
{
xxx = state.count;
if (xxx != 1) {
int var = this->menu->GetItems().size();
auto s = std::to_string(var);
if (s == "0") {
//do nothing here because there's no items in the list, that way the app won't freeze
}
else {
this->selectTitle(this->menu->GetSelectedIndex());
if (this->menu->GetItems().size() == 1 && this->selectedUrls.size() == 1) {
this->startInstall(false);
}
}
}
}
}
if ((Down & HidNpadButton_Minus) || (state.count != xxx))
{
xxx = state.count;
@ -226,8 +259,14 @@ namespace inst::ui {
this->startInstall(false);
return;
}
this->startInstall(false);
this->startInstall(false);
}
}
if (Down & HidNpadButton_ZL)
this->menu->SetSelectedIndex(std::max(0, this->menu->GetSelectedIndex() - 6));
if (Down & HidNpadButton_ZR)
this->menu->SetSelectedIndex(std::min((s32)this->menu->GetItems().size() - 1, this->menu->GetSelectedIndex() + 6));
}
}

View File

@ -167,6 +167,12 @@ namespace inst::ui {
mainApp->LoadLayout(mainApp->mainPage);
}
if (Down & HidNpadButton_ZL)
this->menu->SetSelectedIndex(std::max(0, this->menu->GetSelectedIndex() - 6));
if (Down & HidNpadButton_ZR)
this->menu->SetSelectedIndex(std::min((s32)this->menu->GetItems().size() - 1, this->menu->GetSelectedIndex() + 6));
HidTouchScreenState state={0};
if (hidGetTouchScreenStates(&state, 1)) {

View File

@ -215,5 +215,11 @@ namespace inst::ui {
if (this->selectedTitles.size() > 0) this->startInstall();
}
}
if (Down & HidNpadButton_ZL)
this->menu->SetSelectedIndex(std::max(0, this->menu->GetSelectedIndex() - 6));
if (Down & HidNpadButton_ZR)
this->menu->SetSelectedIndex(std::min((s32)this->menu->GetItems().size() - 1, this->menu->GetSelectedIndex() + 6));
}
}

View File

@ -170,5 +170,11 @@ namespace inst::ui {
this->startInstall();
}
}
if (Down & HidNpadButton_ZL)
this->menu->SetSelectedIndex(std::max(0, this->menu->GetSelectedIndex() - 6));
if (Down & HidNpadButton_ZR)
this->menu->SetSelectedIndex(std::min((s32)this->menu->GetItems().size() - 1, this->menu->GetSelectedIndex() + 6));
}
}

View File

@ -6,6 +6,8 @@
namespace inst::config {
std::string gAuthKey;
std::string sigPatchesUrl;
std::string httpIndexUrl;
std::string httplastUrl;
std::vector<std::string> updateInfo;
int languageSetting;
bool autoUpdate;
@ -29,7 +31,9 @@ namespace inst::config {
{"overClock", overClock},
{"sigPatchesUrl", sigPatchesUrl},
{"usbAck", usbAck},
{"validateNCAs", validateNCAs}
{"validateNCAs", validateNCAs},
{"httpIndexUrl", httpIndexUrl},
{"httplastUrl", httplastUrl}
};
std::ofstream file(inst::config::configPath);
file << std::setw(4) << j << std::endl;
@ -49,6 +53,8 @@ namespace inst::config {
languageSetting = j["languageSetting"].get<int>();
overClock = j["overClock"].get<bool>();
sigPatchesUrl = j["sigPatchesUrl"].get<std::string>();
httpIndexUrl = j["httpIndexUrl"].get<std::string>();
httplastUrl = j["httplastUrl"].get<std::string>();
usbAck = j["usbAck"].get<bool>();
validateNCAs = j["validateNCAs"].get<bool>();
}
@ -56,7 +62,9 @@ namespace inst::config {
// If loading values from the config fails, we just load the defaults and overwrite the old config
gAuthKey = {0x41,0x49,0x7a,0x61,0x53,0x79,0x42,0x4d,0x71,0x76,0x34,0x64,0x58,0x6e,0x54,0x4a,0x4f,0x47,0x51,0x74,0x5a,0x5a,0x53,0x33,0x43,0x42,0x6a,0x76,0x66,0x37,0x34,0x38,0x51,0x76,0x78,0x53,0x7a,0x46,0x30};
sigPatchesUrl = "https://github.com/mrdude2478/patches/releases/download/1/patches.zip";
languageSetting = 99;
languageSetting = 0;
httpIndexUrl = "http://";
httplastUrl = "http://";
autoUpdate = true;
deletePrompt = true;
gayMode = false;

View File

@ -42,6 +42,9 @@ namespace Language {
case 6:
languagePath = "romfs:/lang/zh-rTW.json";
break;
case 99:
languagePath = "romfs:/lang/en.json";
break;
default:
if (std::filesystem::exists(inst::config::appDir + "/lang/custom.json")) {
languagePath = (inst::config::appDir + "/lang/custom.json");

View File

@ -48,7 +48,7 @@ bool _makeDirectoryParents(std::string path)
//Done!
bSuccess = true;
break;
//std::string getHost("");
//std::string getHost();
default:
bSuccess = false;
break;

View File

@ -300,7 +300,7 @@ namespace inst::util {
return;
}
std::vector<std::string> checkForAppUpdate () {
std::vector<std::string> checkForAppUpdate() {
try {
std::string giturl = "https://api.github.com/repos/mrdude2478/TinWoo/releases/latest";
std::string jsonData = inst::curl::downloadToBuffer(giturl, 0, 0, 1000L);