mirror of
https://github.com/Huntereb/Awoo-Installer
synced 2024-11-26 05:30:19 +00:00
Initial XCI stuff
This commit is contained in:
parent
d0cb5951fc
commit
ce9d3acf00
21 changed files with 978 additions and 107 deletions
79
include/install/hfs0.hpp
Executable file
79
include/install/hfs0.hpp
Executable 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
42
include/install/http_xci.hpp
Executable 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;
|
||||
};
|
||||
}
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
47
include/install/install_xci.hpp
Executable 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
41
include/install/local_xci.hpp
Executable 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
59
include/install/xci.hpp
Executable 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);
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
156
source/install/http_xci.cpp
Executable 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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
186
source/install/install_xci.cpp
Executable 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
55
source/install/local_xci.cpp
Executable 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
171
source/install/xci.cpp
Executable 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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue