ldr_oc_suite: adopt ams style return code; test is now clang compatible; loader.kip will not crash if pcv/ptm sysmodules is manually patched.

This commit is contained in:
KazushiM
2022-04-02 22:17:04 +08:00
parent ac43237b13
commit 926b9016b1
7 changed files with 112 additions and 111 deletions

View File

@@ -1,4 +1,4 @@
export CC := g++-11
export CC := g++
all: test

View File

@@ -28,33 +28,33 @@ namespace ams::ldr::oc {
namespace pcv {
Result MemPllmLimitHandler(u32* ptr) {
clk_pll_param* entry = reinterpret_cast<clk_pll_param *>(ptr);
if (entry->max_0 != entry->max_1)
return ResultFailure();
R_UNLESS(entry->max_0 == entry->max_1, ldr::ResultInvalidMemPllmEntry());
// Double the max clk simply
u32 max_clk = entry->max_0 * 2;
entry->max_0 = max_clk;
entry->max_1 = max_clk;
return ResultSuccess();
R_SUCCEED();
}
template<typename M>
Result MtcOverwrite(M* des, M* src) {
constexpr u32 mtc_magic = 0x5F43544D;
if (src->rev != mtc_magic)
return ResultFailure();
R_UNLESS(src->rev == mtc_magic, ldr::ResultInvalidMtcMagic());
// Ignore params from dvfs_ver to clock_src;
for (size_t offset = offsetof(M, clk_src_emc); offset < sizeof(M); offset += sizeof(u32)) {
u32* src_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(src) + offset);
u32* des_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(des) + offset);
u32 src_val = *src_ent;
if (src_val != UINT32_MAX) {
constexpr u32 ignore_val = UINT32_MAX;
if (src_val != ignore_val) {
PatchOffset(des_ent, src_val);
}
}
return ResultSuccess();
R_SUCCEED();
}
}
@@ -62,7 +62,7 @@ namespace ams::ldr::oc {
constexpr u32 CpuClkOSLimit = 1785'000;
constexpr u32 CpuClkOfficial = 1963'500;
constexpr u32 CpuVoltOfficial = 1120;
constexpr u32 GpuClkOSLimit = 921'600;
// constexpr u32 GpuClkOSLimit = 921'600;
constexpr u32 GpuClkOfficial = 1267'200;
constexpr u32 MemClkOSLimit = 1600'000;
constexpr u32 MemClkOSAlt = 1331'200;
@@ -677,16 +677,13 @@ namespace ams::ldr::oc {
}
Result CpuClockVddHandler(u32* ptr) {
u32 value_1 = *(ptr + 2);
u32 value_2 = *(ptr + 12);
constexpr u32 pattern_1 = 0;
constexpr u32 pattern_2 = 1525000;
if (value_1 != pattern_1 || value_2 != pattern_2)
return ResultFailure();
R_UNLESS(*(ptr + 2) == 0, ldr::ResultInvalidCpuClockVddEntry());
R_UNLESS(*(ptr + 12) == 1525000, ldr::ResultInvalidCpuClockVddEntry());
if (C.marikoCpuMaxClock)
PatchOffset(ptr, C.marikoCpuMaxClock);
return ResultSuccess();
R_SUCCEED();
}
Result CpuDvfsHandler(u32* ptr, uintptr_t nso_end_offset) {
@@ -696,21 +693,17 @@ namespace ams::ldr::oc {
cpu_freq_cvb_table_t* entry_1020 = entry_204 + 8;
uintptr_t entry_end_offset = reinterpret_cast<uintptr_t>(entry_free) + sizeof(NewCpuTables) - sizeof(u32);
if ( entry_end_offset >= nso_end_offset
|| *(reinterpret_cast<u32 *>(entry_free)) != 0
|| *(reinterpret_cast<u32 *>(entry_204)) != 204'000
|| *(reinterpret_cast<u32 *>(entry_end_offset)) != 0 )
{
return ResultFailure();
}
R_UNLESS(entry_end_offset < nso_end_offset, ldr::ResultOutOfRange());
R_UNLESS(*(reinterpret_cast<u32 *>(entry_free)) == 0, ldr::ResultInvalidCpuDvfs());
R_UNLESS(*(reinterpret_cast<u32 *>(entry_204)) == 204'000, ldr::ResultInvalidCpuDvfs());
R_UNLESS(*(reinterpret_cast<u32 *>(entry_end_offset)) == 0, ldr::ResultInvalidCpuDvfs());
if (C.marikoCpuMaxClock > CpuClkOfficial)
std::memcpy(reinterpret_cast<void *>(entry_free), NewCpuTables, sizeof(NewCpuTables));
// Patch CPU max volt in CPU dvfs table
cpu_freq_cvb_table_t* entry_current = entry_1020;
if (entry_current->cvb_pll_param.c0 != CpuVoltOfficial * 1000)
return ResultFailure();
R_UNLESS(entry_current->cvb_pll_param.c0 == CpuVoltOfficial * 1000, ldr::ResultInvalidCpuDvfs());
if (C.marikoCpuMaxVolt) {
while (entry_current->cvb_pll_param.c0 == CpuVoltOfficial * 1000) {
@@ -719,7 +712,7 @@ namespace ams::ldr::oc {
}
}
return ResultSuccess();
R_SUCCEED();
}
Result GpuDvfsHandler(u32* ptr, uintptr_t nso_end_offset) {
@@ -728,17 +721,15 @@ namespace ams::ldr::oc {
gpu_cvb_pll_table_t* entry_76_8 = entry_free - 17;
uintptr_t entry_end_offset = reinterpret_cast<uintptr_t>(entry_free) + sizeof(NewGpuTables) - sizeof(u32);
if ( entry_end_offset >= nso_end_offset
|| *(reinterpret_cast<u32 *>(entry_free)) != 0
|| *(reinterpret_cast<u32 *>(entry_76_8)) != 76'800
|| *(reinterpret_cast<u32 *>(entry_end_offset)) != 0 )
{
return ResultFailure();
}
R_UNLESS(entry_end_offset < nso_end_offset, ldr::ResultOutOfRange());
R_UNLESS(*(reinterpret_cast<u32 *>(entry_free)) == 0, ldr::ResultInvalidGpuDvfs());
R_UNLESS(*(reinterpret_cast<u32 *>(entry_76_8)) == 76'800, ldr::ResultInvalidGpuDvfs());
R_UNLESS(*(reinterpret_cast<u32 *>(entry_end_offset)) == 0, ldr::ResultInvalidGpuDvfs());
if (C.marikoGpuMaxClock > GpuClkOfficial)
std::memcpy(reinterpret_cast<void *>(entry_free), NewGpuTables, sizeof(NewGpuTables));
return ResultSuccess();
R_SUCCEED();
}
Result CpuVoltRangeHandler(u32* ptr) {
@@ -750,9 +741,9 @@ namespace ams::ldr::oc {
case 610:
if (C.marikoCpuMaxVolt)
PatchOffset(ptr, C.marikoCpuMaxVolt);
return ResultSuccess();
R_SUCCEED();
default:
return ResultFailure();
R_THROW(ldr::ResultInvalidCpuMinVolt());
}
}
@@ -771,20 +762,19 @@ namespace ams::ldr::oc {
PatchOffset(ptr_next, gpuMaxClockMarikoPattern[1] | reg_id);
}
return ResultSuccess();
R_SUCCEED();
}
}
return ResultFailure();
R_THROW(ldr::ResultInvalidGpuMaxClkPattern());
}
Result MtcTableHandler(u32* ptr) {
MarikoMtcTable* const mtc_table_max = reinterpret_cast<MarikoMtcTable *>(ptr - offsetof(MarikoMtcTable, rate_khz) / sizeof(u32));
MarikoMtcTable* const mtc_table_alt = mtc_table_max - 1;
constexpr u32 mtc_mariko_rev = 3;
if ( mtc_table_max->rev != mtc_mariko_rev
|| mtc_table_alt->rev != mtc_mariko_rev
|| mtc_table_alt->rate_khz != MemClkOSAlt )
return ResultFailure();
R_UNLESS(mtc_table_max->rev == mtc_mariko_rev, ldr::ResultInvalidMtcTable());
R_UNLESS(mtc_table_alt->rev == mtc_mariko_rev, ldr::ResultInvalidMtcTable());
R_UNLESS(mtc_table_alt->rate_khz == MemClkOSAlt, ldr::ResultInvalidMtcTable());
MarikoMtcTable* const table = const_cast<MarikoMtcTable *>(C.marikoMtc);
bool replace_entire_table = (C.mtcConf == ENTIRE_TABLE_MARIKO);
@@ -797,7 +787,7 @@ namespace ams::ldr::oc {
MtcTableAutoAdjust(mtc_table_max, mtc_table_alt);
MtcPllmbDivHandler(mtc_table_max);
std::memcpy(reinterpret_cast<void *>(mtc_table_alt), reinterpret_cast<void *>(table), sizeof(MarikoMtcTable));
return ResultSuccess();
R_SUCCEED();
}
Result DvbTableHandler(u32* ptr) {
@@ -805,11 +795,10 @@ namespace ams::ldr::oc {
emc_dvb_dvfs_table_t* dvb_1331_entry = dvb_max_entry - 1;
u32* dvb_1331_offset = reinterpret_cast<u32 *>(dvb_1331_entry);
if (*(dvb_1331_offset) != MemClkOSAlt)
return ResultFailure();
R_UNLESS(*(dvb_1331_offset) == MemClkOSAlt, ldr::ResultInvalidDvbTable());
PatchOffset(dvb_1331_offset, MemClkOSLimit);
return ResultSuccess();
R_SUCCEED();
}
Result MemMaxClockHandler(u32* ptr) {
@@ -821,26 +810,24 @@ namespace ams::ldr::oc {
constexpr u32 mtc_min_volt = 1100;
constexpr u32 dvb_entry_volt = 675;
Result rc = ResultSuccess();
if (value_next == mtc_min_volt) {
rc = MtcTableHandler(ptr);
R_TRY(MtcTableHandler(ptr));
} else if (value_next2 == dvb_entry_volt) {
rc = DvbTableHandler(ptr);
R_TRY(DvbTableHandler(ptr));
}
PatchOffset(ptr, C.marikoEmcMaxClock);
return rc;
R_SUCCEED();
}
Result GpuPllLimitHandler(u32* ptr) {
u32 value_next = *(ptr + 1);
if (value_next != 0)
return ResultFailure();
R_UNLESS(value_next == 0, ldr::ResultInvalidGpuPllEntry());
// Double the max clk simply
u32 max_clk = *(ptr) * 2;
PatchOffset(ptr, max_clk);
return ResultSuccess();
R_SUCCEED();
}
void Patch(uintptr_t mapped_nso, size_t nso_size) {
@@ -922,14 +909,13 @@ namespace ams::ldr::oc {
cnt[MEM_PLL_CLK],
cnt[GPU_MAX_CLOCK]);
if ( cnt[CPU_CLOCK_VDD] != 1
|| cnt[CPU_TABLE] != 1
|| cnt[GPU_TABLE] != 1
|| cnt[CPU_MAX_VOLT] > 13 || !cnt[CPU_MAX_VOLT]
|| cnt[MEM_CLOCK] == 0
|| cnt[GPU_PLL_CLK] != 1
|| cnt[MEM_PLL_CLK] != 2
|| cnt[GPU_MAX_CLOCK] != 2)
if (cnt[CPU_CLOCK_VDD] > 1 ||
cnt[CPU_TABLE] > 1 ||
cnt[GPU_TABLE] > 1 ||
cnt[CPU_MAX_VOLT] > 13 ||
cnt[GPU_PLL_CLK] > 1 ||
cnt[MEM_PLL_CLK] > 2 ||
cnt[GPU_MAX_CLOCK] > 2)
{
CRASH();
}
@@ -974,18 +960,15 @@ namespace ams::ldr::oc {
cpu_freq_cvb_table_t* entry_204 = entry_free - 16;
uintptr_t entry_end_offset = reinterpret_cast<uintptr_t>(entry_free) + sizeof(NewCpuTables) - sizeof(u32);
if ( entry_end_offset >= nso_end_offset
|| *(reinterpret_cast<u32 *>(entry_free)) != 0
|| *(reinterpret_cast<u32 *>(entry_204)) != 204'000
|| *(reinterpret_cast<u32 *>(entry_end_offset)) != 0 )
{
return ResultFailure();
}
R_UNLESS(entry_end_offset < nso_end_offset, ldr::ResultOutOfRange());
R_UNLESS(*(reinterpret_cast<u32 *>(entry_free)) == 0, ldr::ResultInvalidCpuDvfs());
R_UNLESS(*(reinterpret_cast<u32 *>(entry_204)) == 204'000, ldr::ResultInvalidCpuDvfs());
R_UNLESS(*(reinterpret_cast<u32 *>(entry_end_offset)) == 0, ldr::ResultInvalidCpuDvfs());
if (C.eristaCpuOCEnable)
std::memcpy(reinterpret_cast<void *>(entry_free), NewCpuTables, sizeof(NewCpuTables));
return ResultSuccess();
R_SUCCEED();
}
Result CpuVoltRangeHandler(u32* ptr) {
@@ -997,10 +980,10 @@ namespace ams::ldr::oc {
case 810:
if (C.eristaCpuMaxVolt)
PatchOffset(ptr, C.eristaCpuMaxVolt);
return ResultSuccess();
R_SUCCEED();
default:
LOGGING("Invalid min voltage: %u @%p!", *(ptr-1), ptr-1);
return ResultFailure();
R_THROW(ldr::ResultInvalidCpuMinVolt());
}
}
@@ -1012,21 +995,20 @@ namespace ams::ldr::oc {
return MtcOverwrite(mtc_table_max, table);
}
return ResultSuccess();
R_SUCCEED();
}
Result MemMaxClockHandler(u32* ptr) {
u32 value_next = *(ptr + 1);
constexpr u32 mtc_min_volt = 887;
Result rc = ResultSuccess();
if (value_next == mtc_min_volt) {
rc = MtcTableHandler(ptr);
}
if (value_next == mtc_min_volt)
R_TRY(MtcTableHandler(ptr));
if (C.eristaEmcMaxClock > MemClkOSLimit)
PatchOffset(ptr, C.eristaEmcMaxClock);
return rc;
R_SUCCEED();
}
Result MemVoltHandler(u32* ptr) {
@@ -1035,10 +1017,11 @@ namespace ams::ldr::oc {
constexpr u32 uv_step = 12'500;
if (emc_uv % uv_step)
emc_uv = emc_uv / uv_step * uv_step;
PatchOffset(ptr, emc_uv);
}
return ResultSuccess();
R_SUCCEED();
}
void Patch(uintptr_t mapped_nso, size_t nso_size) {
@@ -1099,11 +1082,9 @@ namespace ams::ldr::oc {
cnt[MEM_VOLT],
cnt[MEM_PLL_CLK]);
if ( cnt[CPU_CLOCK] != 1
|| cnt[CPU_MAX_VOLT] < 2
|| cnt[MEM_CLOCK] == 0
|| cnt[MEM_VOLT] != 2
|| cnt[MEM_PLL_CLK] != 2)
if (cnt[CPU_CLOCK] > 1 ||
cnt[MEM_VOLT] > 2 ||
cnt[MEM_PLL_CLK] > 2)
{
CRASH();
}
@@ -1112,10 +1093,11 @@ namespace ams::ldr::oc {
namespace pcv {
void SafetyCheck() {
if ( C.custRev != CUST_REV
|| C.marikoCpuMaxVolt >= 1300
|| C.eristaCpuMaxVolt >= 1400
|| (C.eristaEmcVolt && (C.eristaEmcVolt < 600'000 || C.eristaEmcVolt > 1250'000)))
if (C.custRev != CUST_REV ||
C.marikoCpuMaxVolt >= 1300 ||
C.eristaCpuMaxVolt >= 1400 ||
(C.eristaEmcVolt &&
(C.eristaEmcVolt < 600'000 || C.eristaEmcVolt > 1250'000)))
{
CRASH();
}

View File

@@ -14,9 +14,30 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mtc_timing_table.hpp"
#define CUST_REV 2
#include "mtc_timing_table.hpp"
#ifdef ATMOSPHERE_IS_STRATOSPHERE
#include <vapours/results/results_common.hpp>
#define LOGGING(fmt, ...) ((void)0)
#endif
#define CRASH() { AMS_ABORT(); __builtin_unreachable(); }
namespace ams::ldr {
R_DEFINE_ERROR_RESULT(OutOfRange, 1000);
R_DEFINE_ERROR_RESULT(InvalidMemPllmEntry, 1001);
R_DEFINE_ERROR_RESULT(InvalidMtcMagic, 1002);
R_DEFINE_ERROR_RESULT(InvalidMtcTable, 1003);
R_DEFINE_ERROR_RESULT(InvalidDvbTable, 1004);
R_DEFINE_ERROR_RESULT(InvalidCpuClockVddEntry, 1005);
R_DEFINE_ERROR_RESULT(InvalidCpuDvfs, 1006);
R_DEFINE_ERROR_RESULT(InvalidCpuMinVolt, 1007);
R_DEFINE_ERROR_RESULT(InvalidGpuDvfs, 1008);
R_DEFINE_ERROR_RESULT(InvalidGpuMaxClkPattern, 1009);
R_DEFINE_ERROR_RESULT(InvalidGpuPllEntry, 1010);
}
namespace ams::ldr::oc {
enum MtcConfig {
@@ -26,7 +47,7 @@ namespace ams::ldr::oc {
ENTIRE_TABLE_MARIKO = 3,
};
typedef struct {
typedef struct CustomizeTable {
u8 cust[4] = {'C', 'U', 'S', 'T'};
u16 custRev = CUST_REV;
u16 mtcConf = AUTO_ADJ_MARIKO_SAFE;
@@ -47,16 +68,8 @@ namespace ams::ldr::oc {
inline void PatchOffset(u32* offset, u32 value) { *(offset) = value; }
inline Result ResultFailure() { return -1; }
#ifdef ATMOSPHERE_IS_STRATOSPHERE
#define LOGGING(fmt, ...) ((void)0)
#endif
#define CRASH() { AMS_ABORT(); __builtin_unreachable(); }
namespace pcv {
typedef struct {
typedef struct cvb_coefficients {
s32 c0 = 0;
s32 c1 = 0;
s32 c2 = 0;
@@ -65,24 +78,24 @@ namespace ams::ldr::oc {
s32 c5 = 0;
} cvb_coefficients;
typedef struct {
typedef struct cpu_freq_cvb_table_t {
u64 freq;
cvb_coefficients cvb_dfll_param;
cvb_coefficients cvb_pll_param; // only c0 is reserved
} cpu_freq_cvb_table_t;
typedef struct {
typedef struct gpu_cvb_pll_table_t {
u64 freq;
cvb_coefficients cvb_dfll_param; // empty, dfll clock source not selected
cvb_coefficients cvb_pll_param;
} gpu_cvb_pll_table_t;
typedef struct {
typedef struct emc_dvb_dvfs_table_t {
u64 freq;
s32 volt[4] = {0};
} emc_dvb_dvfs_table_t;
typedef struct {
typedef struct clk_pll_param {
u32 max_0;
u32 unk[5];
u32 max_1;

View File

@@ -11,12 +11,17 @@ typedef int32_t s32;
typedef uint64_t u64;
typedef int Result;
#define R_SUCCEEDED(arg) (arg == ResultSuccess())
#define R_FAILED(arg) (!R_SUCCEEDED(arg))
#define LOGGING(fmt, ...) { printf(fmt "\n\tin %s\n", ##__VA_ARGS__, __PRETTY_FUNCTION__); }
#define AMS_ABORT() { fprintf(stderr, "Failed in %s!\n", __PRETTY_FUNCTION__); exit(-1); }
#define R_SUCCEEDED(arg) (arg == 0)
#define R_FAILED(arg) (arg != 0)
#define LOGGING(fmt, ...) { printf(fmt "\n\tin %s\n", ##__VA_ARGS__, __PRETTY_FUNCTION__); }
#define AMS_ABORT() { fprintf(stderr, "Failed in %s!\n", __PRETTY_FUNCTION__); exit(-1); }
#define R_SUCCEED() { return 0; }
#define R_THROW(err) { return err; }
#define R_TRY(expr) { Result _rc = (expr); if (R_FAILED(_rc)) { return _rc; } }
#define R_UNLESS(expr, rc) { if (!(expr)) { return rc; } }
inline Result ResultSuccess() { return 0; }
#define R_DEFINE_ERROR_RESULT(name, rc) \
inline Result Result##name() { return rc; }
#include "ldr_oc_suite.hpp"

View File

@@ -18,6 +18,7 @@
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
namespace ams::ldr::oc {
namespace pcv {

View File

@@ -1,118 +0,0 @@
#!/bin/bash
fw_dir="/Volumes/RAM/NX-14.0.0/"
tmp_dir="/Volumes/RAM/"
repack_out_dir="/Volumes/RAM/out/"
oc_test_dir="$HOME/Source/Switch-OC-Suite/Source/Atmosphere/stratosphere/loader/source/oc"
prodkeys="$HOME/.switch/prod.keys"
hactool_exe="$HOME/Source/hactool/hactool"
nx2elf_exe="$HOME/Source/nx2elf/nx2elf"
elf2nso_exe="$HOME/Source/switch-tools/elf2nso"
hacpack_exe="$HOME/Source/hacPack/hacpack"
should_remove_tmp="Y"
should_save_repack="Y"
option_Mariko_Erista="M"
echo -e "\nExtracting nca..."
out_pcv="${tmp_dir}pcv/"
out_ptm="${tmp_dir}ptm/"
mkdir -p "${out_pcv}"
mkdir -p "${out_ptm}"
cd $fw_dir
pcv_nca_name=""
ptm_nca_name=""
for file_00 in ./*.nca/00
do
if [ -e "${file_00}" ]; then
echo "Processing \"*.nca/00\" files..."
find "${fw_dir}" -type f -name "00" -exec sh -c 'DIR=$(dirname "{}"); FW_DIR=$(dirname "${DIR}"); mv "{}" "${FW_DIR}/00"; rm -r "${DIR}"; mv "${FW_DIR}/00" "${DIR}"' \;
fi
break
done
for nca_file in ./*.nca
do
file_size=`wc -c "$nca_file" | awk '{print $1}'`
if [[ "$nca_file" == *".cnmt."* || file_size -lt 16384 ]]; then
continue
fi
titleid=`$hactool_exe -k $prodkeys --disablekeywarns -t nca $nca_file | grep 'Title ID'`
if [[ $titleid == *"010000000000001a"* ]]; then
pcv_nca_name="$(basename $nca_file)"
echo "$pcv_nca_name (pcv) -> $out_pcv"
$hactool_exe -k $prodkeys --disablekeywarns -t nca $nca_file --exefsdir "$out_pcv" 1> /dev/null
fi
if [[ $titleid == *"0100000000000010"* ]]; then
ptm_nca_name="$(basename $nca_file)"
echo "$ptm_nca_name (ptm) -> $out_ptm"
$hactool_exe -k $prodkeys --disablekeywarns -t nca $nca_file --exefsdir "$out_ptm" 1> /dev/null
fi
done
echo -e "\nConverting nca to elf..."
$nx2elf_exe "${out_pcv}main" 1> /dev/null
$nx2elf_exe "${out_ptm}main" 1> /dev/null
echo -e "\nBuilding..."
cd $oc_test_dir
make test 1> /dev/null
echo -e "\nPatching..."
[ -z "$should_save_repack" ] && read -p "Save and repack to nca (y/N)? " should_save_repack
SAVE_OPT=" "
case $should_save_repack in
Y|y ) SAVE_OPT="-s ";;
esac
./test pcv $SAVE_OPT "${out_pcv}main.elf"
./test ptm $SAVE_OPT "${out_ptm}main.elf"
make clean 1> /dev/null
if [ ! -z $SAVE_OPT ]; then
case $should_save_repack in
Y|y )
patched_ext=".mariko"
[ -z "$option_Mariko_Erista" ] && read -p "[M]ariko (Default) | [E]rista ? " option_Mariko_Erista
case $option_Mariko_Erista in
E|e ) patched_ext=".erista";;
esac
mkdir -p "${repack_out_dir}"
cd "${tmp_dir}"
echo -e "\nRepacking pcv to ${repack_out_dir}${pcv_nca_name}..."
TMP="${out_pcv}temp/"
mkdir -p "${TMP}"
$elf2nso_exe "${out_pcv}main.elf${patched_ext}" "${TMP}main" 1> /dev/null
cp "${out_pcv}main.npdm" "${TMP}main.npdm"
$hacpack_exe -k $prodkeys -o "${TMP}nca" --type nca --ncatype program --titleid 010000000000001A --exefsdir "${TMP}" 1> /dev/null
find "${TMP}nca" -name "*.nca" -exec mv {} "${repack_out_dir}${pcv_nca_name}" \;
if [[ $patched_ext == ".mariko" ]]; then
echo -e "\nRepacking ptm (Mariko Only) to ${repack_out_dir}${ptm_nca_name}..."
TMP="${out_ptm}temp/"
mkdir -p "${TMP}"
$elf2nso_exe "${out_ptm}main.elf${patched_ext}" "${TMP}main" 1> /dev/null
cp "${out_ptm}main.npdm" "${TMP}main.npdm"
$hacpack_exe -k $prodkeys -o "${TMP}nca" --type nca --ncatype program --titleid 0100000000000010 --exefsdir "${TMP}" 1> /dev/null
find "${TMP}nca" -name "*.nca" -exec mv {} "${repack_out_dir}${ptm_nca_name}" \;
fi
;;
esac
fi
[ -z "$should_remove_tmp" ] && read -p "Remove temp files (Y/n)? " should_remove_tmp
case $should_remove_tmp in
N|n )
exit;;
esac
rm -fr $out_pcv
rm -fr $out_ptm
rm -fr "${tmp_dir}hacpack_backup"
echo -e "\nDone!"