Initial XCI stuff

This commit is contained in:
Huntereb 2019-11-29 03:23:11 -05:00
parent d0cb5951fc
commit ce9d3acf00
21 changed files with 978 additions and 107 deletions

79
include/install/hfs0.hpp Executable file
View file

@ -0,0 +1,79 @@
/*
Copyright (c) 2017-2018 Adubbz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <switch/types.h>
#define MAGIC_HFS0 0x30534648
namespace tin::install
{
struct HFS0FileEntry
{
u64 dataOffset;
u64 fileSize;
u32 stringTableOffset;
u32 hashedSize;
u64 padding;
unsigned char hash[0x20];
} PACKED;
static_assert(sizeof(HFS0FileEntry) == 0x40, "HFS0FileEntry must be 0x18");
struct HFS0BaseHeader
{
u32 magic;
u32 numFiles;
u32 stringTableSize;
u32 reserved;
} PACKED;
static_assert(sizeof(HFS0BaseHeader) == 0x10, "HFS0BaseHeader must be 0x10");
NX_CONSTEXPR const HFS0FileEntry *hfs0GetFileEntry(const HFS0BaseHeader *header, u32 i)
{
if (i >= header->numFiles)
return NULL;
return (const HFS0FileEntry*)(header + 0x1 + i * 0x4);
}
NX_CONSTEXPR const char *hfs0GetStringTable(const HFS0BaseHeader *header)
{
return (const char*)(header + 0x1 + header->numFiles * 0x4);
}
NX_CONSTEXPR u64 hfs0GetHeaderSize(const HFS0BaseHeader *header)
{
return 0x1 + header->numFiles * 0x4 + header->stringTableSize;
}
NX_CONSTEXPR const char *hfs0GetFileName(const HFS0BaseHeader *header, u32 i)
{
return hfs0GetStringTable(header) + hfs0GetFileEntry(header, i)->stringTableOffset;
}
NX_CONSTEXPR const char *hfs0GetFileName(const HFS0BaseHeader *header, const HFS0FileEntry *entry)
{
return hfs0GetStringTable(header) + entry->stringTableOffset;
}
}

42
include/install/http_xci.hpp Executable file
View file

@ -0,0 +1,42 @@
/*
Copyright (c) 2017-2018 Adubbz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include "install/xci.hpp"
#include "util/network_util.hpp"
#include <memory>
namespace tin::install::xci
{
class HTTPXCI : public XCI
{
public:
tin::network::HTTPDownload m_download;
HTTPXCI(std::string url);
virtual bool CanStream() override;
virtual void StreamToPlaceholder(std::shared_ptr<nx::ncm::ContentStorage>& contentStorage, NcmContentId placeholderId) override;
virtual void BufferData(void* buf, off_t offset, size_t size) override;
};
}

View file

@ -46,15 +46,15 @@ namespace tin::install
bool m_ignoreReqFirmVersion = false;
bool declinedValidation = false;
nx::ncm::ContentMeta m_contentMeta;
std::vector<nx::ncm::ContentMeta> m_contentMeta;
Install(NcmStorageId destStorageId, bool ignoreReqFirmVersion);
virtual ~Install();
virtual std::tuple<nx::ncm::ContentMeta, NcmContentInfo> ReadCNMT() = 0;
virtual std::vector<std::tuple<nx::ncm::ContentMeta, NcmContentInfo>> ReadCNMT() = 0;
virtual void InstallContentMetaRecords(tin::data::ByteBuffer& installContentMetaBuf);
virtual void InstallApplicationRecord();
virtual void InstallContentMetaRecords(tin::data::ByteBuffer& installContentMetaBuf, int i);
virtual void InstallApplicationRecord(int i);
virtual void InstallTicketCert() = 0;
virtual void InstallNCA(const NcmContentId &ncaId) = 0;
@ -62,8 +62,8 @@ namespace tin::install
virtual void Prepare();
virtual void Begin();
virtual u64 GetTitleId();
virtual NcmContentMetaType GetContentMetaType();
virtual u64 GetTitleId(int i = 0);
virtual NcmContentMetaType GetContentMetaType(int i = 0);
virtual void DebugPrintInstallData();
};

View file

@ -36,7 +36,7 @@ namespace tin::install::nsp
tin::install::nsp::SimpleFileSystem* const m_simpleFileSystem;
protected:
std::tuple<nx::ncm::ContentMeta, NcmContentInfo> ReadCNMT() override;
std::vector<std::tuple<nx::ncm::ContentMeta, NcmContentInfo>> ReadCNMT() override;
void InstallNCA(const NcmContentId& ncaId) override;
void InstallTicketCert() override;

View file

@ -35,7 +35,7 @@ namespace tin::install::nsp
RemoteNSP* m_remoteNSP;
protected:
std::tuple<nx::ncm::ContentMeta, NcmContentInfo> ReadCNMT() override;
std::vector<std::tuple<nx::ncm::ContentMeta, NcmContentInfo>> ReadCNMT() override;
void InstallNCA(const NcmContentId& ncaId) override;
void InstallTicketCert() override;

47
include/install/install_xci.hpp Executable file
View file

@ -0,0 +1,47 @@
/*
Copyright (c) 2017-2018 Adubbz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <switch.h>
#include "install/install.hpp"
#include "install/xci.hpp"
#include "nx/content_meta.hpp"
#include "nx/ipc/tin_ipc.h"
namespace tin::install::xci
{
class XCIInstallTask : public Install
{
private:
tin::install::xci::XCI* const m_xci;
protected:
std::vector<std::tuple<nx::ncm::ContentMeta, NcmContentInfo>> ReadCNMT() override;
void InstallNCA(const NcmContentId& ncaId) override;
void InstallTicketCert() override;
public:
XCIInstallTask(tin::install::xci::XCI& xci, NcmStorageId destStorageId, bool ignoreReqFirmVersion);
};
};

41
include/install/local_xci.hpp Executable file
View file

@ -0,0 +1,41 @@
/*
Copyright (c) 2017-2018 Adubbz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include "install/xci.hpp"
namespace tin::install::xci
{
class LocalXCI : public XCI
{
public:
LocalXCI(std::string path);
~LocalXCI();
virtual bool CanStream() override;
virtual void StreamToPlaceholder(std::shared_ptr<nx::ncm::ContentStorage>& contentStorage, NcmContentId placeholderId) override;
virtual void BufferData(void* buf, off_t offset, size_t size) override;
private:
FILE* m_xciFile;
};
}

59
include/install/xci.hpp Executable file
View file

@ -0,0 +1,59 @@
/*
Copyright (c) 2017-2018 Adubbz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <functional>
#include <vector>
#include <switch/types.h>
#include "install/hfs0.hpp"
#include "nx/ncm.hpp"
#include <memory>
namespace tin::install::xci
{
class XCI
{
protected:
u64 m_secureHeaderOffset;
std::vector<u8> m_secureHeaderBytes;
XCI();
public:
virtual bool CanStream() = 0;
virtual void StreamToPlaceholder(std::shared_ptr<nx::ncm::ContentStorage>& contentStorage, NcmContentId placeholderId) = 0;
virtual void BufferData(void* buf, off_t offset, size_t size) = 0;
virtual void RetrieveHeader();
virtual const HFS0BaseHeader* GetSecureHeader();
virtual u64 GetDataOffset();
virtual const HFS0FileEntry* GetFileEntry(unsigned int index);
virtual const HFS0FileEntry* GetFileEntryByName(std::string name);
virtual const HFS0FileEntry* GetFileEntryByNcaId(const NcmContentId& ncaId);
virtual std::vector<const HFS0FileEntry*> GetFileEntriesByExtension(std::string extension);
virtual const char* GetFileEntryName(const HFS0FileEntry* fileEntry);
};
}

View file

@ -1,6 +1,7 @@
#pragma once
#include <string>
namespace inst::curl {
bool downloadFile(const std::string ourUrl, const char *pagefilename);
std::string downloadToBuffer (const std::string ourUrl);
std::string downloadToBuffer (const std::string ourUrl, int firstRange = -1, int secondRange = -1);
}

View file

@ -112,8 +112,6 @@ namespace tin::install::nsp
size_t startSizeBuffered = 0;
double speed = 0.0;
//consoleUpdate(NULL);
inst::ui::setInstBarPerc(0);
while (!bufferedPlaceholderWriter.IsBufferDataComplete())
{
@ -137,7 +135,6 @@ namespace tin::install::nsp
inst::ui::setInstInfoText("Downloading " + inst::util::formatUrlString(ncaFileName) + " at " + std::to_string(speed).substr(0, std::to_string(speed).size()-4) + "MB/s");
inst::ui::setInstBarPerc((double)downloadProgress);
}
//consoleUpdate(NULL);
}
inst::ui::setInstBarPerc(100);
@ -152,13 +149,11 @@ namespace tin::install::nsp
printf("> Install Progress: %lu/%lu MB (%i%s)\r", installSizeMB, totalSizeMB, installProgress, "%");
inst::ui::setInstBarPerc((double)installProgress);
//consoleUpdate(NULL);
}
inst::ui::setInstBarPerc(100);
thrd_join(curlThread, NULL);
thrd_join(writeThread, NULL);
//consoleUpdate(NULL);
}
void HTTPNSP::BufferData(void* buf, off_t offset, size_t size)

156
source/install/http_xci.cpp Executable file
View file

@ -0,0 +1,156 @@
/*
Copyright (c) 2017-2018 Adubbz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "install/http_xci.hpp"
#include <threads.h>
#include "data/buffered_placeholder_writer.hpp"
#include "util/error.hpp"
#include "nspInstall.hpp"
#include "util/util.hpp"
namespace tin::install::xci
{
HTTPXCI::HTTPXCI(std::string url) :
m_download(url)
{
}
struct StreamFuncArgs
{
tin::network::HTTPDownload* download;
tin::data::BufferedPlaceholderWriter* bufferedPlaceholderWriter;
u64 pfs0Offset;
u64 ncaSize;
};
int CurlStreamFunc(void* in)
{
StreamFuncArgs* args = reinterpret_cast<StreamFuncArgs*>(in);
auto streamFunc = [&](u8* streamBuf, size_t streamBufSize) -> size_t
{
while (true)
{
if (args->bufferedPlaceholderWriter->CanAppendData(streamBufSize))
break;
}
args->bufferedPlaceholderWriter->AppendData(streamBuf, streamBufSize);
return streamBufSize;
};
args->download->StreamDataRange(args->pfs0Offset, args->ncaSize, streamFunc);
return 0;
}
int PlaceholderWriteFunc(void* in)
{
StreamFuncArgs* args = reinterpret_cast<StreamFuncArgs*>(in);
while (!args->bufferedPlaceholderWriter->IsPlaceholderComplete())
{
if (args->bufferedPlaceholderWriter->CanWriteSegmentToPlaceholder())
args->bufferedPlaceholderWriter->WriteSegmentToPlaceholder();
}
return 0;
}
bool HTTPXCI::CanStream() {
return true;
}
void HTTPXCI::StreamToPlaceholder(std::shared_ptr<nx::ncm::ContentStorage>& contentStorage, NcmContentId placeholderId)
{
const HFS0FileEntry* fileEntry = this->GetFileEntryByNcaId(placeholderId);
std::string ncaFileName = this->GetFileEntryName(fileEntry);
printf("Retrieving %s\n", ncaFileName.c_str());
size_t ncaSize = fileEntry->fileSize;
tin::data::BufferedPlaceholderWriter bufferedPlaceholderWriter(contentStorage, placeholderId, ncaSize);
StreamFuncArgs args;
args.download = &m_download;
args.bufferedPlaceholderWriter = &bufferedPlaceholderWriter;
args.pfs0Offset = this->GetDataOffset() + fileEntry->dataOffset;
args.ncaSize = ncaSize;
thrd_t curlThread;
thrd_t writeThread;
thrd_create(&curlThread, CurlStreamFunc, &args);
thrd_create(&writeThread, PlaceholderWriteFunc, &args);
u64 freq = armGetSystemTickFreq();
u64 startTime = armGetSystemTick();
size_t startSizeBuffered = 0;
double speed = 0.0;
inst::ui::setInstBarPerc(0);
while (!bufferedPlaceholderWriter.IsBufferDataComplete())
{
u64 newTime = armGetSystemTick();
if (newTime - startTime >= freq)
{
size_t newSizeBuffered = bufferedPlaceholderWriter.GetSizeBuffered();
double mbBuffered = (newSizeBuffered / 1000000.0) - (startSizeBuffered / 1000000.0);
double duration = ((double)(newTime - startTime) / (double)freq);
speed = mbBuffered / duration;
startTime = newTime;
startSizeBuffered = newSizeBuffered;
u64 totalSizeMB = bufferedPlaceholderWriter.GetTotalDataSize() / 1000000;
u64 downloadSizeMB = bufferedPlaceholderWriter.GetSizeBuffered() / 1000000;
int downloadProgress = (int)(((double)bufferedPlaceholderWriter.GetSizeBuffered() / (double)bufferedPlaceholderWriter.GetTotalDataSize()) * 100.0);
printf("> Download Progress: %lu/%lu MB (%i%s) (%.2f MB/s)\r", downloadSizeMB, totalSizeMB, downloadProgress, "%", speed);
inst::ui::setInstInfoText("Downloading " + inst::util::formatUrlString(ncaFileName) + " at " + std::to_string(speed).substr(0, std::to_string(speed).size()-4) + "MB/s");
inst::ui::setInstBarPerc((double)downloadProgress);
}
}
inst::ui::setInstBarPerc(100);
u64 totalSizeMB = bufferedPlaceholderWriter.GetTotalDataSize() / 1000000;
inst::ui::setInstInfoText("Installing " + ncaFileName + "...");
inst::ui::setInstBarPerc(0);
while (!bufferedPlaceholderWriter.IsPlaceholderComplete())
{
u64 installSizeMB = bufferedPlaceholderWriter.GetSizeWrittenToPlaceholder() / 1000000;
int installProgress = (int)(((double)bufferedPlaceholderWriter.GetSizeWrittenToPlaceholder() / (double)bufferedPlaceholderWriter.GetTotalDataSize()) * 100.0);
printf("> Install Progress: %lu/%lu MB (%i%s)\r", installSizeMB, totalSizeMB, installProgress, "%");
}
inst::ui::setInstBarPerc(100);
thrd_join(curlThread, NULL);
thrd_join(writeThread, NULL);
}
void HTTPXCI::BufferData(void* buf, off_t offset, size_t size)
{
m_download.BufferDataRange(buf, offset, size, nullptr);
}
}

View file

@ -47,10 +47,10 @@ namespace tin::install
}
// TODO: Implement RAII on NcmContentMetaDatabase
void Install::InstallContentMetaRecords(tin::data::ByteBuffer& installContentMetaBuf)
void Install::InstallContentMetaRecords(tin::data::ByteBuffer& installContentMetaBuf, int i)
{
NcmContentMetaDatabase contentMetaDatabase;
NcmContentMetaKey contentMetaKey = m_contentMeta.GetContentMetaKey();
NcmContentMetaKey contentMetaKey = m_contentMeta[i].GetContentMetaKey();
try
{
@ -65,14 +65,13 @@ namespace tin::install
}
serviceClose(&contentMetaDatabase.s);
//consoleUpdate(NULL);
}
void Install::InstallApplicationRecord()
void Install::InstallApplicationRecord(int i)
{
Result rc = 0;
std::vector<ContentStorageRecord> storageRecords;
u64 baseTitleId = tin::util::GetBaseTitleId(this->GetTitleId(), this->GetContentMetaType());
u64 baseTitleId = tin::util::GetBaseTitleId(this->GetTitleId(i), this->GetContentMetaType(i));
u32 contentMetaCount = 0;
printf("Base title Id: 0x%lx", baseTitleId);
@ -107,7 +106,7 @@ namespace tin::install
// Add our new content meta
ContentStorageRecord storageRecord;
storageRecord.metaRecord = m_contentMeta.GetContentMetaKey();
storageRecord.metaRecord = m_contentMeta[i].GetContentMetaKey();
storageRecord.storageId = m_destStorageId;
storageRecords.push_back(storageRecord);
@ -120,39 +119,47 @@ namespace tin::install
printf("Pushing application record...\n");
ASSERT_OK(nsPushApplicationRecord(baseTitleId, 0x3, storageRecords.data(), storageRecords.size() * sizeof(ContentStorageRecord)), "Failed to push application record");
//consoleUpdate(NULL);
}
// Validate and obtain all data needed for install
void Install::Prepare()
{
tin::data::ByteBuffer cnmtBuf;
auto cnmtTuple = this->ReadCNMT();
m_contentMeta = std::get<0>(cnmtTuple);
NcmContentInfo cnmtContentRecord = std::get<1>(cnmtTuple);
nx::ncm::ContentStorage contentStorage(m_destStorageId);
std::vector<std::tuple<nx::ncm::ContentMeta, NcmContentInfo>> tupelList = this->ReadCNMT();
for (size_t i = 0; i < tupelList.size(); i++) {
std::tuple<nx::ncm::ContentMeta, NcmContentInfo> cnmtTuple = tupelList[i];
m_contentMeta.push_back(std::get<0>(cnmtTuple));
NcmContentInfo cnmtContentRecord = std::get<1>(cnmtTuple);
if (!contentStorage.Has(cnmtContentRecord.content_id))
{
printf("Installing CNMT NCA...\n");
this->InstallNCA(cnmtContentRecord.content_id);
}
else
{
printf("CNMT NCA already installed. Proceeding...\n");
nx::ncm::ContentStorage contentStorage(m_destStorageId);
if (!contentStorage.Has(cnmtContentRecord.content_id))
{
printf("Installing CNMT NCA...\n");
this->InstallNCA(cnmtContentRecord.content_id);
}
else
{
printf("CNMT NCA already installed. Proceeding...\n");
}
// Parse data and create install content meta
if (m_ignoreReqFirmVersion)
printf("WARNING: Required system firmware version is being IGNORED!\n");
tin::data::ByteBuffer installContentMetaBuf;
m_contentMeta[i].GetInstallContentMeta(installContentMetaBuf, cnmtContentRecord, m_ignoreReqFirmVersion);
this->InstallContentMetaRecords(installContentMetaBuf, i);
this->InstallApplicationRecord(i);
}
}
// Parse data and create install content meta
if (m_ignoreReqFirmVersion)
printf("WARNING: Required system firmware version is being IGNORED!\n");
tin::data::ByteBuffer installContentMetaBuf;
m_contentMeta.GetInstallContentMeta(installContentMetaBuf, cnmtContentRecord, m_ignoreReqFirmVersion);
this->InstallContentMetaRecords(installContentMetaBuf);
this->InstallApplicationRecord();
void Install::Begin()
{
printf("Installing ticket and cert...\n");
try
{
@ -162,36 +169,32 @@ namespace tin::install
{
printf("WARNING: Ticket installation failed! This may not be an issue, depending on your use case.\nProceed with caution!\n");
}
}
void Install::Begin()
{
printf("Installing NCAs...\n");
//consoleUpdate(NULL);
for (auto& record : m_contentMeta.GetContentInfos())
{
printf("Installing from %s\n", tin::util::GetNcaIdString(record.content_id).c_str());
//consoleUpdate(NULL);
this->InstallNCA(record.content_id);
for (nx::ncm::ContentMeta contentMeta: m_contentMeta) {
printf("Installing NCAs...\n");
for (auto& record : contentMeta.GetContentInfos())
{
printf("Installing from %s\n", tin::util::GetNcaIdString(record.content_id).c_str());
this->InstallNCA(record.content_id);
}
printf("Post Install Records: \n");
}
declinedValidation = false;
printf("Post Install Records: \n");
//this->DebugPrintInstallData();
}
u64 Install::GetTitleId()
u64 Install::GetTitleId(int i)
{
return m_contentMeta.GetContentMetaKey().id;
return m_contentMeta[i].GetContentMetaKey().id;
}
NcmContentMetaType Install::GetContentMetaType()
NcmContentMetaType Install::GetContentMetaType(int i)
{
return static_cast<NcmContentMetaType>(m_contentMeta.GetContentMetaKey().type);
return static_cast<NcmContentMetaType>(m_contentMeta[i].GetContentMetaKey().type);
}
void Install::DebugPrintInstallData()
{
/*
#ifdef NXLINK_DEBUG
NcmContentMetaDatabase contentMetaDatabase;
@ -272,5 +275,6 @@ namespace tin::install
}
#endif
*/
}
}

View file

@ -53,13 +53,13 @@ namespace tin::install::nsp
}
std::tuple<nx::ncm::ContentMeta, NcmContentInfo> NSPInstallTask::ReadCNMT()
std::vector<std::tuple<nx::ncm::ContentMeta, NcmContentInfo>> NSPInstallTask::ReadCNMT()
{
NcmContentInfo cnmtRecord = tin::util::CreateNSPCNMTContentRecord(this->m_simpleFileSystem->m_absoluteRootPath.substr(0, this->m_simpleFileSystem->m_absoluteRootPath.size() - 1));
nx::ncm::ContentStorage contentStorage(m_destStorageId);
this->InstallNCA(cnmtRecord.content_id);
std::string cnmtNCAFullPath = contentStorage.GetPath(cnmtRecord.content_id);
return { tin::util::GetContentMetaFromNCA(cnmtNCAFullPath), cnmtRecord };
return { { tin::util::GetContentMetaFromNCA(cnmtNCAFullPath), cnmtRecord } };
}
void NSPInstallTask::InstallTicketCert()
@ -84,7 +84,6 @@ namespace tin::install::nsp
// Finally, let's actually import the ticket
ASSERT_OK(esImportTicket(tikBuf.get(), tikSize, certBuf.get(), certSize), "Failed to import ticket");
//consoleUpdate(NULL);
}
void NSPInstallTask::InstallNCA(const NcmContentId &ncaId)
@ -95,7 +94,7 @@ namespace tin::install::nsp
ncaName += ".nca";
else if (m_simpleFileSystem->HasFile(ncaName + ".cnmt.nca"))
ncaName += ".cnmt.nca";
else if (m_simpleFileSystem->HasFile(ncaName + ".ncz"))
else if (m_simpleFileSystem->HasFile(ncaName + ".ncz"))
ncaName += ".ncz";
else if (m_simpleFileSystem->HasFile(ncaName + ".cnmt.ncz"))
ncaName += ".cnmt.ncz";
@ -152,15 +151,14 @@ namespace tin::install::nsp
float progress;
bool failed = false;
//consoleUpdate(NULL);
try
{
inst::ui::setInstInfoText("Installing " + ncaName + "...");
inst::ui::setInstBarPerc(0);
while (fileOff < ncaSize)
{
// Clear the buffer before we read anything, just to be sure
while (fileOff < ncaSize)
{
// Clear the buffer before we read anything, just to be sure
progress = (float)fileOff / (float)ncaSize;
if (fileOff % (0x400000 * 3) == 0) {
@ -174,7 +172,6 @@ namespace tin::install::nsp
writer.write(readBuffer.get(), readSize);
fileOff += readSize;
//consoleUpdate(NULL);
}
inst::ui::setInstBarPerc(100);
}

View file

@ -46,7 +46,7 @@ namespace tin::install::nsp
m_remoteNSP->RetrieveHeader();
}
std::tuple<nx::ncm::ContentMeta, NcmContentInfo> RemoteNSPInstall::ReadCNMT()
std::vector<std::tuple<nx::ncm::ContentMeta, NcmContentInfo>> RemoteNSPInstall::ReadCNMT()
{
const PFS0FileEntry* fileEntry = m_remoteNSP->GetFileEntryByExtension("cnmt.nca");
@ -69,9 +69,8 @@ namespace tin::install::nsp
cnmtContentInfo.content_id = cnmtContentId;
*(u64*)&cnmtContentInfo.size = cnmtNcaSize & 0xFFFFFFFFFFFF;
cnmtContentInfo.content_type = NcmContentType_Meta;
//consoleUpdate(NULL);
return { tin::util::GetContentMetaFromNCA(cnmtNCAFullPath), cnmtContentInfo };
return { { tin::util::GetContentMetaFromNCA(cnmtNCAFullPath), cnmtContentInfo } };
}
void RemoteNSPInstall::InstallNCA(const NcmContentId& ncaId)
@ -135,7 +134,7 @@ namespace tin::install::nsp
}
void RemoteNSPInstall::InstallTicketCert()
{
{
// Read the tik file and put it into a buffer
const PFS0FileEntry* tikFileEntry = m_remoteNSP->GetFileEntryByExtension("tik");
@ -166,6 +165,5 @@ namespace tin::install::nsp
// Finally, let's actually import the ticket
ASSERT_OK(esImportTicket(tikBuf.get(), tikSize, certBuf.get(), certSize), "Failed to import ticket");
//consoleUpdate(NULL);
}
}

186
source/install/install_xci.cpp Executable file
View file

@ -0,0 +1,186 @@
/*
Copyright (c) 2017-2018 Adubbz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "install/install_xci.hpp"
#include "util/file_util.hpp"
#include "util/title_util.hpp"
#include "nx/nca_writer.h"
#include "util/debug.h"
#include "util/error.hpp"
#include "nspInstall.hpp"
#include "ui/MainApplication.hpp"
namespace tin::install::xci
{
XCIInstallTask::XCIInstallTask(tin::install::xci::XCI& xci, NcmStorageId destStorageId, bool ignoreReqFirmVersion) :
Install(destStorageId, ignoreReqFirmVersion), m_xci(&xci)
{
m_xci->RetrieveHeader();
}
std::vector<std::tuple<nx::ncm::ContentMeta, NcmContentInfo>> XCIInstallTask::ReadCNMT()
{
std::vector<std::tuple<nx::ncm::ContentMeta, NcmContentInfo>> CNMTList;
for (const HFS0FileEntry* fileEntry : m_xci->GetFileEntriesByExtension("cnmt.nca")) {
if (fileEntry == nullptr)
THROW_FORMAT("Failed to find cnmt file entry!\n");
std::string cnmtNcaName(m_xci->GetFileEntryName(fileEntry));
NcmContentId cnmtContentId = tin::util::GetNcaIdFromString(cnmtNcaName);
size_t cnmtNcaSize = fileEntry->fileSize;
nx::ncm::ContentStorage contentStorage(m_destStorageId);
printf("CNMT Name: %s\n", cnmtNcaName.c_str());
// We install the cnmt nca early to read from it later
this->InstallNCA(cnmtContentId);
std::string cnmtNCAFullPath = contentStorage.GetPath(cnmtContentId);
NcmContentInfo cnmtContentInfo;
cnmtContentInfo.content_id = cnmtContentId;
*(u64*)&cnmtContentInfo.size = cnmtNcaSize & 0xFFFFFFFFFFFF;
cnmtContentInfo.content_type = NcmContentType_Meta;
CNMTList.push_back( { tin::util::GetContentMetaFromNCA(cnmtNCAFullPath), cnmtContentInfo } );
}
return CNMTList;
}
void XCIInstallTask::InstallNCA(const NcmContentId& ncaId)
{
const HFS0FileEntry* fileEntry = m_xci->GetFileEntryByNcaId(ncaId);
std::string ncaFileName = m_xci->GetFileEntryName(fileEntry);
size_t ncaSize = fileEntry->fileSize;
printf("Installing %s to storage Id %u\n", ncaFileName.c_str(), m_destStorageId);
std::shared_ptr<nx::ncm::ContentStorage> contentStorage(new nx::ncm::ContentStorage(m_destStorageId));
// Attempt to delete any leftover placeholders
try
{
contentStorage->DeletePlaceholder(*(NcmPlaceHolderId*)&ncaId);
}
catch (...) {}
printf("Size: 0x%lx\n", ncaSize);
if (m_xci->CanStream()) {
m_xci->StreamToPlaceholder(contentStorage, ncaId);
} else {
NcaWriter writer(ncaId, contentStorage);
float progress;
u64 fileStart = m_xci->GetDataOffset() + fileEntry->dataOffset;
u64 fileOff = 0;
size_t readSize = 0x400000; // 4MB buff
auto readBuffer = std::make_unique<u8[]>(readSize);
try
{
inst::ui::setInstInfoText("Installing " + ncaFileName + "...");
inst::ui::setInstBarPerc(0);
while (fileOff < ncaSize)
{
progress = (float) fileOff / (float) ncaSize;
if (fileOff % (0x400000 * 3) == 0) {
printf("> Progress: %lu/%lu MB (%d%s)\r", (fileOff / 1000000), (ncaSize / 1000000), (int)(progress * 100.0), "%");
inst::ui::setInstBarPerc((double)(progress * 100.0));
}
if (fileOff + readSize >= ncaSize) readSize = ncaSize - fileOff;
m_xci->BufferData(readBuffer.get(), fileOff + fileStart, readSize);
writer.write(readBuffer.get(), readSize);
fileOff += readSize;
}
inst::ui::setInstBarPerc(100);
}
catch (std::exception& e)
{
printf("something went wrong: %s\n", e.what());
}
writer.close();
}
// Clean up the line for whatever comes next
printf(" \r");
printf("Registering placeholder...\n");
try
{
contentStorage->Register(*(NcmPlaceHolderId*)&ncaId, ncaId);
}
catch (...)
{
printf(("Failed to register " + ncaFileName + ". It may already exist.\n").c_str());
}
try
{
contentStorage->DeletePlaceholder(*(NcmPlaceHolderId*)&ncaId);
}
catch (...) {}
}
void XCIInstallTask::InstallTicketCert()
{
// Read the tik files and put it into a buffer
std::vector<const HFS0FileEntry*> tikFileEntries = m_xci->GetFileEntriesByExtension("tik");
std::vector<const HFS0FileEntry*> certFileEntries = m_xci->GetFileEntriesByExtension("cert");
for (size_t i = 0; i < tikFileEntries.size(); i++)
{
if (tikFileEntries[i] == nullptr)
{
printf("Remote tik file is missing.\n");
throw std::runtime_error("Remote tik file is not present!");
}
u64 tikSize = tikFileEntries[i]->fileSize;
auto tikBuf = std::make_unique<u8[]>(tikSize);
printf("> Reading tik\n");
m_xci->BufferData(tikBuf.get(), m_xci->GetDataOffset() + tikFileEntries[i]->dataOffset, tikSize);
if (certFileEntries[i] == nullptr)
{
printf("Remote cert file is missing.\n");
throw std::runtime_error("Remote cert file is not present!");
}
u64 certSize = certFileEntries[i]->fileSize;
auto certBuf = std::make_unique<u8[]>(certSize);
printf("> Reading cert\n");
m_xci->BufferData(certBuf.get(), m_xci->GetDataOffset() + certFileEntries[i]->dataOffset, certSize);
// Finally, let's actually import the ticket
ASSERT_OK(esImportTicket(tikBuf.get(), tikSize, certBuf.get(), certSize), "Failed to import ticket");
}
}
}

55
source/install/local_xci.cpp Executable file
View file

@ -0,0 +1,55 @@
/*
Copyright (c) 2017-2018 Adubbz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "install/local_xci.hpp"
#include "error.hpp"
#include "debug.h"
namespace tin::install::xci
{
LocalXCI::LocalXCI(std::string path)
{
m_xciFile = fopen((path).c_str(), "rb");
if (!m_xciFile)
THROW_FORMAT("can't open file at %s\n", path.c_str());
}
LocalXCI::~LocalXCI()
{
fclose(m_xciFile);
}
bool LocalXCI::CanStream() {
return false;
}
void LocalXCI::StreamToPlaceholder(std::shared_ptr<nx::ncm::ContentStorage>& contentStorage, NcmContentId placeholderId)
{
THROW_FORMAT("not streamable\n");
}
void LocalXCI::BufferData(void* buf, off_t offset, size_t size)
{
fseeko(m_xciFile, offset, SEEK_SET);
fread(buf, 1, size, m_xciFile);
}
}

171
source/install/xci.cpp Executable file
View file

@ -0,0 +1,171 @@
/*
Copyright (c) 2017-2018 Adubbz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "install/xci.hpp"
#include "util/title_util.hpp"
#include "error.hpp"
#include "debug.h"
namespace tin::install::xci
{
XCI::XCI()
{
}
void XCI::RetrieveHeader()
{
printf("Retrieving HFS0 header...\n");
// Retrieve hfs0 offset
u64 hfs0Offset = 0xf000;
// Retrieve main hfs0 header
std::vector<u8> m_headerBytes;
m_headerBytes.resize(sizeof(HFS0BaseHeader), 0);
this->BufferData(m_headerBytes.data(), hfs0Offset, sizeof(HFS0BaseHeader));
printf("Base header: \n");
printBytes(nxlinkout, m_headerBytes.data(), sizeof(HFS0BaseHeader), true);
// Retrieve full header
HFS0BaseHeader *header = reinterpret_cast<HFS0BaseHeader*>(m_headerBytes.data());
if (header->magic != MAGIC_HFS0)
THROW_FORMAT("hfs0 magic doesn't match at 0x%lx\n", hfs0Offset);
size_t remainingHeaderSize = header->numFiles * sizeof(HFS0FileEntry) + header->stringTableSize;
m_headerBytes.resize(sizeof(HFS0BaseHeader) + remainingHeaderSize, 0);
this->BufferData(m_headerBytes.data() + sizeof(HFS0BaseHeader), hfs0Offset + sizeof(HFS0BaseHeader), remainingHeaderSize);
printf("Base header: \n");
printBytes(nxlinkout, m_headerBytes.data(), sizeof(HFS0BaseHeader) + remainingHeaderSize, true);
// Find Secure partition
header = reinterpret_cast<HFS0BaseHeader*>(m_headerBytes.data());
for (unsigned int i = 0; i < header->numFiles; i++)
{
const HFS0FileEntry *entry = hfs0GetFileEntry(header, i);
std::string entryName(hfs0GetFileName(header, entry));
if (entryName != "secure")
continue;
m_secureHeaderOffset = hfs0Offset + remainingHeaderSize + 0x10 + entry->dataOffset;
m_secureHeaderBytes.resize(sizeof(HFS0BaseHeader), 0);
this->BufferData(m_secureHeaderBytes.data(), m_secureHeaderOffset, sizeof(HFS0BaseHeader));
printf("Secure header: \n");
printBytes(nxlinkout, m_secureHeaderBytes.data(), sizeof(HFS0BaseHeader), true);
if (this->GetSecureHeader()->magic != MAGIC_HFS0)
THROW_FORMAT("hfs0 magic doesn't match at 0x%lx\n", m_secureHeaderOffset);
// Retrieve full header
remainingHeaderSize = this->GetSecureHeader()->numFiles * sizeof(HFS0FileEntry) + this->GetSecureHeader()->stringTableSize;
m_secureHeaderBytes.resize(sizeof(HFS0BaseHeader) + remainingHeaderSize, 0);
this->BufferData(m_secureHeaderBytes.data() + sizeof(HFS0BaseHeader), m_secureHeaderOffset + sizeof(HFS0BaseHeader), remainingHeaderSize);
return;
}
THROW_FORMAT("couldn't optain secure hfs0 header\n");
}
const HFS0BaseHeader* XCI::GetSecureHeader()
{
if (m_secureHeaderBytes.empty())
THROW_FORMAT("Cannot retrieve header as header bytes are empty. Have you retrieved it yet?\n");
return reinterpret_cast<HFS0BaseHeader*>(m_secureHeaderBytes.data());
}
u64 XCI::GetDataOffset()
{
if (m_secureHeaderBytes.empty())
THROW_FORMAT("Cannot get data offset as header is empty. Have you retrieved it yet?\n");
return m_secureHeaderOffset + m_secureHeaderBytes.size();
}
const HFS0FileEntry* XCI::GetFileEntry(unsigned int index)
{
if (index >= this->GetSecureHeader()->numFiles)
THROW_FORMAT("File entry index is out of bounds\n")
return hfs0GetFileEntry(this->GetSecureHeader(), index);
}
const HFS0FileEntry* XCI::GetFileEntryByName(std::string name)
{
for (unsigned int i = 0; i < this->GetSecureHeader()->numFiles; i++)
{
const HFS0FileEntry* fileEntry = this->GetFileEntry(i);
std::string foundName(this->GetFileEntryName(fileEntry));
if (foundName == name)
return fileEntry;
}
return nullptr;
}
const HFS0FileEntry* XCI::GetFileEntryByNcaId(const NcmContentId& ncaId)
{
const HFS0FileEntry* fileEntry = nullptr;
std::string ncaIdStr = tin::util::GetNcaIdString(ncaId);
if ((fileEntry = this->GetFileEntryByName(ncaIdStr + ".nca")) == nullptr)
{
if ((fileEntry = this->GetFileEntryByName(ncaIdStr + ".cnmt.nca")) == nullptr)
{
if ((fileEntry = this->GetFileEntryByName(ncaIdStr + ".ncz")) == nullptr)
{
if ((fileEntry = this->GetFileEntryByName(ncaIdStr + ".cnmt.ncz")) == nullptr)
{
return nullptr;
}
}
}
}
return fileEntry;
}
std::vector<const HFS0FileEntry*> XCI::GetFileEntriesByExtension(std::string extension)
{
std::vector<const HFS0FileEntry*> entryList;
for (unsigned int i = 0; i < this->GetSecureHeader()->numFiles; i++)
{
const HFS0FileEntry* fileEntry = this->GetFileEntry(i);
std::string name(this->GetFileEntryName(fileEntry));
auto foundExtension = name.substr(name.find(".") + 1);
if (foundExtension == extension)
entryList.push_back(fileEntry);
}
return entryList;
}
const char* XCI::GetFileEntryName(const HFS0FileEntry* fileEntry)
{
return hfs0GetFileName(this->GetSecureHeader(), fileEntry);
}
}

View file

@ -31,6 +31,8 @@ SOFTWARE.
#include "util/network_util.hpp"
#include "install/install_nsp_remote.hpp"
#include "install/http_nsp.hpp"
#include "install/install_xci.hpp"
#include "install/http_xci.hpp"
#include "install/install.hpp"
#include "util/error.hpp"
@ -39,6 +41,7 @@ SOFTWARE.
#include "nspInstall.hpp"
#include "util/config.hpp"
#include "util/util.hpp"
#include "util/curl.hpp"
const unsigned int MAX_URL_SIZE = 1024;
const unsigned int MAX_URLS = 256;
@ -141,19 +144,35 @@ namespace netInstStuff{
try {
for (urlItr = 0; urlItr < ourUrlList.size(); urlItr++) {
inst::ui::setTopInstInfoText("Installing " + urlNames[urlItr]);
if (inst::curl::downloadToBuffer(ourUrlList[urlItr], 0x100, 0x103) == "HEAD") {
inst::ui::setTopInstInfoText("Installing " + urlNames[urlItr]);
tin::install::nsp::HTTPNSP httpNSP(ourUrlList[urlItr]);
tin::install::xci::HTTPXCI httpXCI(ourUrlList[urlItr]);
printf("%s %s\n", "Install request from", ourUrlList[urlItr].c_str());
tin::install::nsp::RemoteNSPInstall install(m_destStorageId, inst::config::ignoreReqVers, &httpNSP);
printf("%s %s\n", "Install request from", ourUrlList[urlItr].c_str());
tin::install::xci::XCIInstallTask install(httpXCI, m_destStorageId, inst::config::ignoreReqVers);
printf("%s\n", "Preparing installation");
inst::ui::setInstInfoText("Preparing installation...");
inst::ui::setInstBarPerc(0);
install.Prepare();
printf("%s\n", "Preparing installation");
inst::ui::setInstInfoText("Preparing installation...");
inst::ui::setInstBarPerc(0);
install.Prepare();
install.Begin();
install.Begin();
} else {
inst::ui::setTopInstInfoText("Installing " + urlNames[urlItr]);
tin::install::nsp::HTTPNSP httpNSP(ourUrlList[urlItr]);
printf("%s %s\n", "Install request from", ourUrlList[urlItr].c_str());
tin::install::nsp::RemoteNSPInstall install(m_destStorageId, inst::config::ignoreReqVers, &httpNSP);
printf("%s\n", "Preparing installation");
inst::ui::setInstInfoText("Preparing installation...");
inst::ui::setInstBarPerc(0);
install.Prepare();
install.Begin();
}
}
}
catch (std::exception& e) {

View file

@ -26,6 +26,8 @@ SOFTWARE.
#include <ctime>
#include "install/install_nsp.hpp"
#include "install/install_xci.hpp"
#include "install/local_xci.hpp"
#include "nx/fs.hpp"
#include "util/file_util.hpp"
#include "util/title_util.hpp"
@ -102,29 +104,44 @@ namespace nspInstStuff {
try
{
for (nspItr = 0; nspItr < ourNspList.size(); nspItr++) {
inst::ui::setTopInstInfoText("Installing " + inst::util::shortenString(ourNspList[nspItr].filename().string(), 42, true));
if (ourNspList[nspItr].extension() == ".nsz") {
oldNamesOfFiles.push_back(ourNspList[nspItr]);
std::string newfilename = ourNspList[nspItr].string().substr(0, ourNspList[nspItr].string().find_last_of('.'))+"_temp.nsp";
rename(ourNspList[nspItr], newfilename);
filesToBeRenamed.push_back(newfilename);
ourNspList[nspItr] = newfilename;
if (ourNspList[nspItr].extension() == ".xci" || ourNspList[nspItr].extension() == ".xcz") {
inst::ui::setTopInstInfoText("Installing " + inst::util::shortenString(ourNspList[nspItr].filename().string(), 42, true));
tin::install::xci::LocalXCI xci(ourNspList[nspItr]);
tin::install::xci::XCIInstallTask task(xci, m_destStorageId, inst::config::ignoreReqVers);
printf("Preparing installation\n");
inst::ui::setInstInfoText("Preparing installation...");
inst::ui::setInstBarPerc(0);
task.Prepare();
task.Begin();
} else {
inst::ui::setTopInstInfoText("Installing " + inst::util::shortenString(ourNspList[nspItr].filename().string(), 42, true));
if (ourNspList[nspItr].extension() == ".nsz") {
oldNamesOfFiles.push_back(ourNspList[nspItr]);
std::string newfilename = ourNspList[nspItr].string().substr(0, ourNspList[nspItr].string().find_last_of('.'))+"_temp.nsp";
rename(ourNspList[nspItr], newfilename);
filesToBeRenamed.push_back(newfilename);
ourNspList[nspItr] = newfilename;
}
std::string path = "@Sdcard://" + ourNspList[nspItr].string().erase(0, 6);
nx::fs::IFileSystem fileSystem;
fileSystem.OpenFileSystemWithId(path, FsFileSystemType_ApplicationPackage, 0);
tin::install::nsp::SimpleFileSystem simpleFS(fileSystem, "/", path + "/");
tin::install::nsp::NSPInstallTask task(simpleFS, m_destStorageId, inst::config::ignoreReqVers);
printf("Preparing installation\n");
inst::ui::setInstInfoText("Preparing installation...");
inst::ui::setInstBarPerc(0);
task.Prepare();
task.Begin();
}
std::string path = "@Sdcard://" + ourNspList[nspItr].string().erase(0, 6);
nx::fs::IFileSystem fileSystem;
fileSystem.OpenFileSystemWithId(path, FsFileSystemType_ApplicationPackage, 0);
tin::install::nsp::SimpleFileSystem simpleFS(fileSystem, "/", path + "/");
tin::install::nsp::NSPInstallTask task(simpleFS, m_destStorageId, inst::config::ignoreReqVers);
printf("Preparing installation\n");
inst::ui::setInstInfoText("Preparing installation...");
inst::ui::setInstBarPerc(0);
task.Prepare();
task.Begin();
}
}
catch (std::exception& e)

View file

@ -50,7 +50,7 @@ namespace inst::ui {
this->menu->ClearItems();
try {
this->ourDirectories = util::getDirsAtPath(this->currentDir);
this->ourFiles = util::getDirectoryFiles(this->currentDir, {".nsp", ".nsz"});
this->ourFiles = util::getDirectoryFiles(this->currentDir, {".nsp", ".nsz", ".xci", ".xcz"});
} catch (std::exception& e) {
this->drawMenuItems(false, this->currentDir.parent_path());
return;

View file

@ -50,7 +50,7 @@ namespace inst::curl {
}
}
std::string downloadToBuffer (const std::string ourUrl) {
std::string downloadToBuffer (const std::string ourUrl, int firstRange, int secondRange) {
CURL *curl_handle;
CURLcode result;
std::ostringstream stream;
@ -66,6 +66,10 @@ namespace inst::curl {
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT_MS, 5000L);
curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 5000L);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeDataBuffer);
if (firstRange && secondRange) {
const char * ourRange = (std::to_string(firstRange) + "-" + std::to_string(secondRange)).c_str();
curl_easy_setopt(curl_handle, CURLOPT_RANGE, ourRange);
}
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &stream);
result = curl_easy_perform(curl_handle);