Initial Commit
This commit is contained in:
54
TegraExplorer/source/config.c
Normal file
54
TegraExplorer/source/config.c
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include <utils/ini.h>
|
||||
#include <gfx_utils.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <soc/fuse.h>
|
||||
#include <soc/hw_init.h>
|
||||
#include <soc/t210.h>
|
||||
#include <storage/nx_sd.h>
|
||||
#include <storage/sdmmc.h>
|
||||
#include <utils/btn.h>
|
||||
#include <utils/list.h>
|
||||
#include <utils/util.h>
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
void set_default_configuration()
|
||||
{
|
||||
h_cfg.autoboot = 0;
|
||||
h_cfg.autoboot_list = 0;
|
||||
h_cfg.bootwait = 3;
|
||||
h_cfg.se_keygen_done = 0;
|
||||
h_cfg.backlight = 100;
|
||||
h_cfg.autohosoff = 0;
|
||||
h_cfg.autonogc = 1;
|
||||
h_cfg.updater2p = 0;
|
||||
h_cfg.bootprotect = 0;
|
||||
h_cfg.errors = 0;
|
||||
h_cfg.sept_run = 0;
|
||||
h_cfg.aes_slots_new = false;
|
||||
h_cfg.rcm_patched = fuse_check_patched_rcm();
|
||||
h_cfg.emummc_force_disable = false;
|
||||
h_cfg.t210b01 = hw_get_chip_id() == GP_HIDREV_MAJOR_T210B01;
|
||||
|
||||
sd_power_cycle_time_start = 0;
|
||||
}
|
||||
50
TegraExplorer/source/config.h
Normal file
50
TegraExplorer/source/config.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _CONFIG_H_
|
||||
#define _CONFIG_H_
|
||||
#include <utils/types.h>
|
||||
|
||||
typedef struct _hekate_config
|
||||
{
|
||||
// Non-volatile config.
|
||||
u32 autoboot;
|
||||
u32 autoboot_list;
|
||||
u32 bootwait;
|
||||
u32 backlight;
|
||||
u32 autohosoff;
|
||||
u32 autonogc;
|
||||
u32 updater2p;
|
||||
u32 bootprotect;
|
||||
// Global temporary config.
|
||||
bool t210b01;
|
||||
bool se_keygen_done;
|
||||
bool sept_run;
|
||||
bool aes_slots_new;
|
||||
bool emummc_force_disable;
|
||||
bool rcm_patched;
|
||||
u32 errors;
|
||||
} hekate_config;
|
||||
|
||||
void set_default_configuration();
|
||||
int create_config_entry();
|
||||
void config_autoboot();
|
||||
void config_bootdelay();
|
||||
void config_backlight();
|
||||
void config_auto_hos_poweroff();
|
||||
void config_nogc();
|
||||
|
||||
#endif /* _CONFIG_H_ */
|
||||
62
TegraExplorer/source/err.c
Normal file
62
TegraExplorer/source/err.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "err.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "hid/hid.h"
|
||||
#include "gfx/gfxutils.h"
|
||||
|
||||
const char *TEErrors[] = {
|
||||
"I/O ERROR",
|
||||
"NO DISK",
|
||||
"NOT READY",
|
||||
"NO FILE",
|
||||
"NO PATH",
|
||||
"PATH INVALID",
|
||||
"ACCESS DENIED",
|
||||
"ACCESS DENIED",
|
||||
"INVALID PTR",
|
||||
"PROTECTED",
|
||||
"INVALID DRIVE",
|
||||
"NO MEM",
|
||||
"NO FAT",
|
||||
"MKFS ABORT",
|
||||
[TE_ERR_UNIMPLEMENTED - 1] = "Unimplemented",
|
||||
[TE_EXCEPTION_RESET - 1] = "E Reset",
|
||||
[TE_EXCEPTION_UNDEFINED - 1] = "E Undefined",
|
||||
[TE_EXCEPTION_PREF_ABORT - 1] = "E Pref abort",
|
||||
[TE_EXCEPTION_DATA_ABORT - 1] = "E Data abort",
|
||||
[TE_ERR_SAME_LOC - 1] = "Same copy location",
|
||||
[TE_ERR_KEYDUMP_FAIL - 1] = "Keydump failed",
|
||||
[TE_ERR_PARTITION_NOT_FOUND - 1] = "Failed to find partition",
|
||||
[TE_ERR_PATH_IN_PATH - 1] = "Can't move/copy folder into itself",
|
||||
[TE_ERR_EMMC_READ_FAIL - 1] = "Emmc/Emummc read failed",
|
||||
[TE_ERR_EMMC_WRITE_FAIL - 1] = "Emmc/Emummc write failed",
|
||||
[TE_ERR_NO_SD - 1] = "No sd detected",
|
||||
[TE_ERR_FILE_TOO_BIG_FOR_DEST - 1] = "File is too big for dest",
|
||||
};
|
||||
|
||||
const char *GetErrStr(u32 err){
|
||||
--err; // obv error codes cannot be 0
|
||||
if (err >= 0 && err < ARRAY_SIZE(TEErrors))
|
||||
return TEErrors[err];
|
||||
|
||||
return "(Unknown)";
|
||||
}
|
||||
|
||||
#define lx 256
|
||||
#define ly 240
|
||||
#define lenx 768
|
||||
#define leny 240
|
||||
|
||||
void DrawError(ErrCode_t err){
|
||||
if (err.err == 0)
|
||||
return;
|
||||
|
||||
SETCOLOR(COLOR_ORANGE, COLOR_DARKGREY);
|
||||
gfx_box(lx, ly, lx + lenx, ly + leny, COLOR_ORANGE);
|
||||
gfx_boxGrey(lx + 16, ly + 16, lx + lenx - 16, ly + leny - 16, 0x33);
|
||||
gfx_con_setpos(lx + ((lenx - 17 * 16) / 2), ly + 32);
|
||||
gfx_printf("An error occured!\n\n%bErr : %d\nLine: %d\nFile: %s\nDesc: %s%b", lx + 48, err.err, err.loc, err.file, GetErrStr(err.err), 0);
|
||||
gfx_con_setpos(lx + ((lenx - 19 * 16) / 2), ly + leny - 48);
|
||||
gfx_printf("Press A to continue");
|
||||
|
||||
hidWaitMask((JoyA | JoyB));
|
||||
}
|
||||
30
TegraExplorer/source/err.h
Normal file
30
TegraExplorer/source/err.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
|
||||
typedef struct {
|
||||
u16 err;
|
||||
u16 loc;
|
||||
char* file;
|
||||
} ErrCode_t;
|
||||
|
||||
enum {
|
||||
TE_ERR_UNIMPLEMENTED = 15,
|
||||
TE_EXCEPTION_RESET,
|
||||
TE_EXCEPTION_UNDEFINED,
|
||||
TE_EXCEPTION_PREF_ABORT,
|
||||
TE_EXCEPTION_DATA_ABORT,
|
||||
TE_ERR_SAME_LOC,
|
||||
TE_ERR_KEYDUMP_FAIL,
|
||||
TE_ERR_PARTITION_NOT_FOUND,
|
||||
TE_ERR_PATH_IN_PATH,
|
||||
TE_ERR_EMMC_READ_FAIL,
|
||||
TE_ERR_EMMC_WRITE_FAIL,
|
||||
TE_ERR_NO_SD,
|
||||
TE_ERR_FILE_TOO_BIG_FOR_DEST,
|
||||
TE_ERR_MEM_ALLOC_FAIL,
|
||||
TE_WARN_FILE_EXISTS,
|
||||
TE_WARN_FILE_TOO_SMALL_FOR_DEST,
|
||||
};
|
||||
|
||||
#define newErrCode(err) (ErrCode_t) {err, __LINE__, __FILE__}
|
||||
void DrawError(ErrCode_t err);
|
||||
191
TegraExplorer/source/fs/fscopy.c
Normal file
191
TegraExplorer/source/fs/fscopy.c
Normal file
@@ -0,0 +1,191 @@
|
||||
#include "fscopy.h"
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <utils/btn.h>
|
||||
#include "../tegraexplorer/tconf.h"
|
||||
#include "../gfx/gfx.h"
|
||||
#include <mem/heap.h>
|
||||
#include <string.h>
|
||||
#include "../gfx/gfxutils.h"
|
||||
#include "fsutils.h"
|
||||
#include "readers/folderReader.h"
|
||||
|
||||
ErrCode_t FileCopy(const char *locin, const char *locout, u8 options){
|
||||
FIL in, out;
|
||||
FILINFO in_info;
|
||||
u64 sizeRemaining, toCopy;
|
||||
u8 *buff;
|
||||
u32 x, y;
|
||||
ErrCode_t err = newErrCode(0);
|
||||
int res = 0;
|
||||
|
||||
gfx_con_getpos(&x, &y);
|
||||
|
||||
if (!strcmp(locin, locout)){
|
||||
return newErrCode(TE_ERR_SAME_LOC);
|
||||
}
|
||||
|
||||
if ((res = f_open(&in, locin, FA_READ | FA_OPEN_EXISTING))){
|
||||
return newErrCode(res);
|
||||
}
|
||||
|
||||
if ((res = f_stat(locin, &in_info))){
|
||||
return newErrCode(res);
|
||||
}
|
||||
|
||||
if ((res = f_open(&out, locout, FA_CREATE_ALWAYS | FA_WRITE))){
|
||||
return newErrCode(res);
|
||||
}
|
||||
|
||||
if (options & COPY_MODE_PRINT){
|
||||
gfx_printf("[ 0%%]");
|
||||
x += 16;
|
||||
}
|
||||
|
||||
buff = malloc(TConf.FSBuffSize);
|
||||
sizeRemaining = f_size(&in);
|
||||
const u64 totalsize = sizeRemaining;
|
||||
|
||||
while (sizeRemaining > 0){
|
||||
toCopy = MIN(sizeRemaining, TConf.FSBuffSize);
|
||||
|
||||
if ((res = f_read(&in, buff, toCopy, NULL))){
|
||||
err = newErrCode(res);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((res = f_write(&out, buff, toCopy, NULL))){
|
||||
err = newErrCode(res);
|
||||
break;
|
||||
}
|
||||
|
||||
sizeRemaining -= toCopy;
|
||||
|
||||
if (options & COPY_MODE_PRINT){
|
||||
gfx_con_setpos(x, y);
|
||||
gfx_printf("%3d%%", (u32)(((totalsize - sizeRemaining) * 100) / totalsize));
|
||||
}
|
||||
|
||||
if (options & COPY_MODE_CANCEL && btn_read() & (BTN_VOL_DOWN | BTN_VOL_UP)){
|
||||
f_unlink(locout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
f_close(&in);
|
||||
f_close(&out);
|
||||
free(buff);
|
||||
|
||||
f_chmod(locout, in_info.fattrib, 0x3A);
|
||||
|
||||
if (options & COPY_MODE_PRINT){
|
||||
gfx_con_setpos(x - 16, y);
|
||||
}
|
||||
|
||||
//f_stat(locin, &in_info); //somehow stops fatfs from being weird
|
||||
return err;
|
||||
}
|
||||
|
||||
void BoxRestOfScreen(){
|
||||
u32 tempX, tempY;
|
||||
gfx_con_getpos(&tempX, &tempY);
|
||||
gfx_boxGrey(tempX, tempY, YLEFT, tempY + 16, 0x1B);
|
||||
}
|
||||
|
||||
ErrCode_t FolderCopy(const char *locin, const char *locout){
|
||||
if (TConf.explorerCopyMode >= CMODE_CopyFolder){
|
||||
if (strstr(locout, locin) != NULL)
|
||||
return newErrCode(TE_ERR_PATH_IN_PATH);
|
||||
}
|
||||
|
||||
if (!strcmp(locin, locout)){
|
||||
return newErrCode(TE_ERR_SAME_LOC);
|
||||
}
|
||||
|
||||
char *dstPath = CombinePaths(locout, strrchr(locin, '/') + 1);
|
||||
int res = 0;
|
||||
ErrCode_t ret = newErrCode(0);
|
||||
u32 x, y;
|
||||
gfx_con_getpos(&x, &y);
|
||||
|
||||
Vector_t fileVec = ReadFolder(locin, &res);
|
||||
if (res){
|
||||
ret = newErrCode(res);
|
||||
}
|
||||
else {
|
||||
vecDefArray(FSEntry_t *, fs, fileVec);
|
||||
f_mkdir(dstPath);
|
||||
|
||||
for (int i = 0; i < fileVec.count && !ret.err; i++){
|
||||
char *temp = CombinePaths(locin, fs[i].name);
|
||||
if (fs[i].isDir){
|
||||
ret = FolderCopy(temp, dstPath);
|
||||
}
|
||||
else {
|
||||
gfx_puts_limit(fs[i].name, (YLEFT - x) / 16 - 10);
|
||||
BoxRestOfScreen();
|
||||
|
||||
char *tempDst = CombinePaths(dstPath, fs[i].name);
|
||||
ret = FileCopy(temp, tempDst, COPY_MODE_PRINT);
|
||||
free(tempDst);
|
||||
|
||||
gfx_con_setpos(x, y);
|
||||
}
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
FILINFO fno;
|
||||
|
||||
if (!ret.err){
|
||||
res = f_stat(locin, &fno);
|
||||
if (res)
|
||||
ret = newErrCode(res);
|
||||
else
|
||||
ret = newErrCode(f_chmod(dstPath, fno.fattrib, 0x3A));
|
||||
}
|
||||
|
||||
free(dstPath);
|
||||
clearFileVector(&fileVec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ErrCode_t FolderDelete(const char *path){
|
||||
int res = 0;
|
||||
ErrCode_t ret = newErrCode(0);
|
||||
u32 x, y;
|
||||
gfx_con_getpos(&x, &y);
|
||||
|
||||
Vector_t fileVec = ReadFolder(path, &res);
|
||||
if (res){
|
||||
ret = newErrCode(res);
|
||||
}
|
||||
else {
|
||||
vecDefArray(FSEntry_t *, fs, fileVec);
|
||||
|
||||
for (int i = 0; i < fileVec.count && !ret.err; i++){
|
||||
char *temp = CombinePaths(path, fs[i].name);
|
||||
if (fs[i].isDir){
|
||||
ret = FolderDelete(temp);
|
||||
}
|
||||
else {
|
||||
gfx_puts_limit(fs[i].name, (YLEFT - x) / 16 - 10);
|
||||
BoxRestOfScreen();
|
||||
res = f_unlink(temp);
|
||||
if (res){
|
||||
ret = newErrCode(res);
|
||||
}
|
||||
gfx_con_setpos(x, y);
|
||||
}
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret.err){
|
||||
res = f_unlink(path);
|
||||
if (res)
|
||||
ret = newErrCode(res);
|
||||
}
|
||||
|
||||
clearFileVector(&fileVec);
|
||||
return ret;
|
||||
}
|
||||
10
TegraExplorer/source/fs/fscopy.h
Normal file
10
TegraExplorer/source/fs/fscopy.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
#include "../err.h"
|
||||
|
||||
#define COPY_MODE_CANCEL BIT(0)
|
||||
#define COPY_MODE_PRINT BIT(1)
|
||||
|
||||
ErrCode_t FileCopy(const char *locin, const char *locout, u8 options);
|
||||
ErrCode_t FolderDelete(const char *path);
|
||||
ErrCode_t FolderCopy(const char *locin, const char *locout);
|
||||
27
TegraExplorer/source/fs/fstypes.h
Normal file
27
TegraExplorer/source/fs/fstypes.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
union {
|
||||
struct {
|
||||
u8 readOnly:1;
|
||||
u8 hidden:1;
|
||||
u8 system:1;
|
||||
u8 volume:1;
|
||||
u8 isDir:1;
|
||||
u8 archive:1;
|
||||
};
|
||||
u8 optionUnion;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
u16 size:12;
|
||||
u16 showSize:1;
|
||||
u16 sizeDef:3;
|
||||
};
|
||||
u16 sizeUnion;
|
||||
};
|
||||
} FSEntry_t;
|
||||
|
||||
#define newFSEntry(filename) (FSEntry_t) {.name = filename}
|
||||
58
TegraExplorer/source/fs/fsutils.c
Normal file
58
TegraExplorer/source/fs/fsutils.c
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <mem/heap.h>
|
||||
#include <string.h>
|
||||
#include "fsutils.h"
|
||||
#include "../utils/utils.h"
|
||||
#include <utils/sprintf.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include "readers/folderReader.h"
|
||||
|
||||
char *CombinePaths(const char *current, const char *add){
|
||||
char *ret;
|
||||
|
||||
size_t size = strlen(current) + strlen(add) + 2;
|
||||
ret = (char*) malloc (size);
|
||||
|
||||
s_printf(ret, (current[strlen(current) - 1] == '/') ? "%s%s" : "%s/%s", current, add);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *EscapeFolder(const char *current){
|
||||
char *ret;
|
||||
char *temp;
|
||||
|
||||
ret = CpyStr(current);
|
||||
temp = strrchr(ret, '/');
|
||||
|
||||
if (*(temp - 1) == ':')
|
||||
temp++;
|
||||
|
||||
*temp = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
FSEntry_t GetFileInfo(const char *path){
|
||||
FILINFO fno;
|
||||
f_stat(path, &fno);
|
||||
FSEntry_t entry = {.optionUnion = fno.fattrib, .name = strrchr(path, '/') + 1};
|
||||
if (!(*entry.name))
|
||||
entry.name = "Root";
|
||||
return entry;
|
||||
}
|
||||
|
||||
char *GetFileAttribs(FSEntry_t entry){
|
||||
char *ret = CpyStr("RHSVDA");
|
||||
MaskIn(ret, entry.optionUnion, '-');
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Returns 1 if a file exists, 0 if it does not
|
||||
bool FileExists(const char* path){
|
||||
FRESULT fr;
|
||||
FILINFO fno;
|
||||
|
||||
fr = f_stat(path, &fno);
|
||||
|
||||
return !(fr & FR_NO_FILE);
|
||||
}
|
||||
9
TegraExplorer/source/fs/fsutils.h
Normal file
9
TegraExplorer/source/fs/fsutils.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
#include "fstypes.h"
|
||||
|
||||
FSEntry_t GetFileInfo(const char *path);
|
||||
char *EscapeFolder(const char *current);
|
||||
char *CombinePaths(const char *current, const char *add);
|
||||
char *GetFileAttribs(FSEntry_t entry);
|
||||
bool FileExists(const char* path);
|
||||
148
TegraExplorer/source/fs/menus/explorer.c
Normal file
148
TegraExplorer/source/fs/menus/explorer.c
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "explorer.h"
|
||||
#include "../../utils/vector.h"
|
||||
#include "../readers/folderReader.h"
|
||||
#include "../../gfx/menu.h"
|
||||
#include "../fsutils.h"
|
||||
#include "../../gfx/gfx.h"
|
||||
#include "../../gfx/gfxutils.h"
|
||||
#include "../../utils/utils.h"
|
||||
#include "filemenu.h"
|
||||
#include <string.h>
|
||||
#include <mem/heap.h>
|
||||
#include "../../tegraexplorer/tconf.h"
|
||||
#include "../../err.h"
|
||||
#include "../fscopy.h"
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include "../../hid/hid.h"
|
||||
#include "foldermenu.h"
|
||||
|
||||
MenuEntry_t topEntries[] = {
|
||||
{.optionUnion = COLORTORGB(COLOR_GREEN) | SKIPBIT},
|
||||
{.optionUnion = COLORTORGB(COLOR_ORANGE)},
|
||||
{.optionUnion = COLORTORGB(COLOR_GREY) | SKIPBIT, .name = "Clipboard -> Current folder"},
|
||||
{.optionUnion = COLORTORGB(COLOR_ORANGE), .name = "Current folder options"}
|
||||
};
|
||||
|
||||
MenuEntry_t MakeMenuOutFSEntry(FSEntry_t entry){
|
||||
MenuEntry_t out = {.name = entry.name, .sizeUnion = entry.sizeUnion};
|
||||
out.optionUnion = (entry.isDir) ? COLORTORGB(COLOR_WHITE) : COLORTORGB(COLOR_VIOLET);
|
||||
out.icon = (entry.isDir) ? 127 : 128;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FileExplorer(char *path){
|
||||
char *storedPath = CpyStr(path);
|
||||
int res = 0;
|
||||
|
||||
if (TConf.explorerCopyMode == CMODE_Move || TConf.explorerCopyMode == CMODE_MoveFolder)
|
||||
ResetCopyParams();
|
||||
|
||||
while (1){
|
||||
topEntries[2].optionUnion = (TConf.explorerCopyMode != CMODE_None) ? (COLORTORGB(COLOR_ORANGE)) : (COLORTORGB(COLOR_GREY) | SKIPBIT);
|
||||
topEntries[1].name = (!strcmp(storedPath, path)) ? "<- Exit explorer" : "<- Folder back";
|
||||
|
||||
gfx_clearscreen();
|
||||
gfx_printf("Loading...\r");
|
||||
|
||||
int readRes = 0;
|
||||
Vector_t fileVec = ReadFolder(storedPath, &readRes);
|
||||
if (readRes){
|
||||
clearFileVector(&fileVec);
|
||||
DrawError(newErrCode(readRes));
|
||||
return;
|
||||
}
|
||||
|
||||
vecDefArray(FSEntry_t*, fsEntries, fileVec);
|
||||
|
||||
topEntries[0].name = storedPath;
|
||||
Vector_t entries = newVec(sizeof(MenuEntry_t), fileVec.count + ARR_LEN(topEntries));
|
||||
entries.count = ARR_LEN(topEntries);
|
||||
memcpy(entries.data, topEntries, sizeof(MenuEntry_t) * ARR_LEN(topEntries));
|
||||
|
||||
for (int i = 0; i < fileVec.count; i++){
|
||||
MenuEntry_t a = MakeMenuOutFSEntry(fsEntries[i]);
|
||||
vecAddElem(&entries, a);
|
||||
}
|
||||
|
||||
gfx_con_setpos(144, 24);
|
||||
gfx_boxGrey(0, 16, 160, 31, 0x1B);
|
||||
|
||||
if (res >= fileVec.count + ARR_LEN(topEntries))
|
||||
res = 0;
|
||||
|
||||
res = newMenu(&entries, res, 60, 42, ENABLEB | ENABLEPAGECOUNT, (int)fileVec.count);
|
||||
vecFree(entries);
|
||||
|
||||
char *oldPath = storedPath;
|
||||
|
||||
if (res == 2){
|
||||
ErrCode_t err = {0};
|
||||
char *filename = CpyStr(strrchr(TConf.srcCopy, '/') + 1);
|
||||
char *dst = CombinePaths(storedPath, filename);
|
||||
|
||||
if (!strcmp(TConf.srcCopy, dst))
|
||||
err = newErrCode(TE_ERR_SAME_LOC);
|
||||
|
||||
if (!err.err && TConf.explorerCopyMode >= CMODE_CopyFolder){
|
||||
if (strstr(dst, TConf.srcCopy) != NULL)
|
||||
err = newErrCode(TE_ERR_PATH_IN_PATH);
|
||||
}
|
||||
|
||||
if (!err.err){
|
||||
if (TConf.explorerCopyMode == CMODE_Move || TConf.explorerCopyMode == CMODE_MoveFolder){
|
||||
if ((err.err = f_rename(TConf.srcCopy, dst)))
|
||||
err = newErrCode(err.err);
|
||||
}
|
||||
else if (TConf.explorerCopyMode == CMODE_Copy) {
|
||||
gfx_clearscreen();
|
||||
RESETCOLOR;
|
||||
gfx_printf("Hold vol+/- to cancel\nCopying %s... ", filename);
|
||||
err = FileCopy(TConf.srcCopy, dst, COPY_MODE_CANCEL | COPY_MODE_PRINT);
|
||||
}
|
||||
else {
|
||||
gfx_clearscreen();
|
||||
RESETCOLOR;
|
||||
gfx_printf("\nCopying folder... ");
|
||||
err = FolderCopy(TConf.srcCopy, storedPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DrawError(err);
|
||||
free(dst);
|
||||
free(filename);
|
||||
ResetCopyParams();
|
||||
}
|
||||
else if (res == 3){
|
||||
if (FolderMenu(storedPath)){
|
||||
storedPath = EscapeFolder(oldPath);
|
||||
free(oldPath);
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
else if (res < ARR_LEN(topEntries)) {
|
||||
if (!strcmp(storedPath, path)){
|
||||
clearFileVector(&fileVec);
|
||||
free(storedPath);
|
||||
return;
|
||||
}
|
||||
|
||||
storedPath = EscapeFolder(oldPath);
|
||||
free(oldPath);
|
||||
res = 0;
|
||||
}
|
||||
else if (fsEntries[res - ARR_LEN(topEntries)].isDir) {
|
||||
storedPath = CombinePaths(storedPath, fsEntries[res - ARR_LEN(topEntries)].name);
|
||||
free(oldPath);
|
||||
res = 0;
|
||||
}
|
||||
else {
|
||||
FileMenu(storedPath, fsEntries[res - ARR_LEN(topEntries)]);
|
||||
}
|
||||
|
||||
clearFileVector(&fileVec);
|
||||
}
|
||||
}
|
||||
7
TegraExplorer/source/fs/menus/explorer.h
Normal file
7
TegraExplorer/source/fs/menus/explorer.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include "../../utils/vector.h"
|
||||
#include "../../gfx/menu.h"
|
||||
#include "../fstypes.h"
|
||||
|
||||
void FileExplorer(char *path);
|
||||
MenuEntry_t MakeMenuOutFSEntry(FSEntry_t entry);
|
||||
216
TegraExplorer/source/fs/menus/filemenu.c
Normal file
216
TegraExplorer/source/fs/menus/filemenu.c
Normal file
@@ -0,0 +1,216 @@
|
||||
#include "filemenu.h"
|
||||
#include "../../err.h"
|
||||
#include "../../gfx/menu.h"
|
||||
#include "../../gfx/gfxutils.h"
|
||||
#include "../fsutils.h"
|
||||
#include <mem/heap.h>
|
||||
#include <string.h>
|
||||
#include <utils/sprintf.h>
|
||||
#include "../../tegraexplorer/tconf.h"
|
||||
#include "../../hid/hid.h"
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include "../../utils/utils.h"
|
||||
#include "../../keys/nca.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include "../../storage/emummc.h"
|
||||
#include "../../script/eval.h"
|
||||
#include "../../script/parser.h"
|
||||
#include "../../script/garbageCollector.h"
|
||||
|
||||
|
||||
MenuEntry_t FileMenuEntries[] = {
|
||||
{.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "-- File menu --"},
|
||||
{.optionUnion = COLORTORGB(COLOR_GREEN) | SKIPBIT}, // For the file name and size
|
||||
{.optionUnion = COLORTORGB(COLOR_VIOLET) | SKIPBIT}, // For the file Attribs
|
||||
{.optionUnion = HIDEBIT},
|
||||
{.optionUnion = COLORTORGB(COLOR_WHITE), .name = "<- Back"},
|
||||
{.optionUnion = COLORTORGB(COLOR_BLUE), .name = "\nCopy to clipboard"},
|
||||
{.optionUnion = COLORTORGB(COLOR_BLUE), .name = "Move to clipboard"},
|
||||
{.optionUnion = COLORTORGB(COLOR_BLUE), .name = "Rename file\n"},
|
||||
{.optionUnion = COLORTORGB(COLOR_RED), .name = "Delete file"},
|
||||
{.optionUnion = COLORTORGB(COLOR_GREEN), .name = "View hex"},
|
||||
{.optionUnion = COLORTORGB(COLOR_ORANGE), .name = "Launch Payload"},
|
||||
{.optionUnion = COLORTORGB(COLOR_YELLOW), .name = "Launch Script"},
|
||||
};
|
||||
|
||||
|
||||
void UnimplementedException(char *path, FSEntry_t entry){
|
||||
DrawError(newErrCode(TE_ERR_UNIMPLEMENTED));
|
||||
}
|
||||
|
||||
extern int launch_payload(char *path);
|
||||
|
||||
void LaunchPayload(char *path, FSEntry_t entry){
|
||||
launch_payload(CombinePaths(path, entry.name));
|
||||
}
|
||||
|
||||
void CopyClipboard(char *path, FSEntry_t entry){
|
||||
char *thing = CombinePaths(path, entry.name);
|
||||
SetCopyParams(thing, CMODE_Copy);
|
||||
free(thing);
|
||||
}
|
||||
|
||||
void MoveClipboard(char *path, FSEntry_t entry){
|
||||
char *thing = CombinePaths(path, entry.name);
|
||||
SetCopyParams(thing, CMODE_Move);
|
||||
free(thing);
|
||||
}
|
||||
|
||||
void DeleteFile(char *path, FSEntry_t entry){
|
||||
gfx_con_setpos(384 + 16, 200 + 16 + 10 * 16);
|
||||
SETCOLOR(COLOR_RED, COLOR_DARKGREY);
|
||||
gfx_printf("Are you sure? ");
|
||||
|
||||
WaitFor(500);
|
||||
if (!MakeYesNoHorzMenu(3, COLOR_DARKGREY))
|
||||
return;
|
||||
|
||||
char *thing = CombinePaths(path, entry.name);
|
||||
int res = f_unlink(thing);
|
||||
if (res)
|
||||
DrawError(newErrCode(res));
|
||||
free(thing);
|
||||
}
|
||||
|
||||
void RunScriptString(char *str, u32 size){
|
||||
TConf.scriptCWD = "sd:/";
|
||||
gfx_clearscreen();
|
||||
ParserRet_t ret = parseScript(str, size);
|
||||
setStaticVars(&ret.staticVarHolder);
|
||||
initRuntimeVars();
|
||||
eval(ret.main.operations.data, ret.main.operations.count, 0);
|
||||
exitRuntimeVars();
|
||||
exitStaticVars(&ret.staticVarHolder);
|
||||
exitFunction(ret.main.operations.data, ret.main.operations.count);
|
||||
vecFree(ret.staticVarHolder);
|
||||
vecFree(ret.main.operations);
|
||||
}
|
||||
|
||||
void RunScript(char *path, FSEntry_t entry){
|
||||
char *thing = CombinePaths(path, entry.name);
|
||||
u32 size;
|
||||
char *script = sd_file_read(thing, &size);
|
||||
free(thing);
|
||||
TConf.scriptCWD = path;
|
||||
|
||||
if (!script)
|
||||
return;
|
||||
|
||||
if (((entry.size >= 16 && entry.sizeDef == 1) || entry.sizeDef >= 2) && !TConf.minervaEnabled)
|
||||
return;
|
||||
|
||||
gfx_clearscreen();
|
||||
|
||||
ParserRet_t ret = parseScript(script, size);
|
||||
free(script);
|
||||
setStaticVars(&ret.staticVarHolder);
|
||||
initRuntimeVars();
|
||||
Variable_t* res = eval(ret.main.operations.data, ret.main.operations.count, 1);
|
||||
exitRuntimeVars();
|
||||
exitStaticVars(&ret.staticVarHolder);
|
||||
exitFunction(ret.main.operations.data, ret.main.operations.count);
|
||||
vecFree(ret.staticVarHolder);
|
||||
vecFree(ret.main.operations);
|
||||
}
|
||||
|
||||
void RenameFile(char *path, FSEntry_t entry){
|
||||
gfx_clearscreen();
|
||||
char *renameTo = ShowKeyboard(entry.name, false);
|
||||
if (renameTo == NULL || !(*renameTo)) // smol memory leak but eh
|
||||
return;
|
||||
|
||||
char *src = CombinePaths(path, entry.name);
|
||||
char *dst = CombinePaths(path, renameTo);
|
||||
|
||||
int res = f_rename(src, dst);
|
||||
if (res){
|
||||
DrawError(newErrCode(res));
|
||||
}
|
||||
|
||||
free(src);
|
||||
free(dst);
|
||||
free(renameTo);
|
||||
}
|
||||
|
||||
// This is from the original TE and it's bad but uhh i'm too lazy to fix it
|
||||
void HexView(char *path, FSEntry_t entry){
|
||||
char *filePath = CombinePaths(path, entry.name);
|
||||
|
||||
FIL in;
|
||||
u8 *print;
|
||||
u32 size;
|
||||
QWORD offset = 0;
|
||||
int res;
|
||||
Input_t *input = hidRead();
|
||||
|
||||
while (input->buttons & (BtnPow | JoyB))
|
||||
hidRead();
|
||||
|
||||
gfx_clearscreen();
|
||||
print = calloc(2048, 1);
|
||||
|
||||
if ((res = f_open(&in, filePath, FA_READ | FA_OPEN_EXISTING))){
|
||||
DrawError(newErrCode(res));
|
||||
return;
|
||||
}
|
||||
|
||||
while (1){
|
||||
f_lseek(&in, offset * 32);
|
||||
|
||||
if ((res = f_read(&in, print, 2048, &size))){
|
||||
DrawError(newErrCode(res));
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_con_setpos(0, 31);
|
||||
gfx_hexdump(offset * 32, print, ((size + 31) / 32) * 32);
|
||||
|
||||
input = hidRead();
|
||||
|
||||
if (!(input->buttons))
|
||||
input = hidWait();
|
||||
|
||||
if (input->down && 2048 == size)
|
||||
offset += 2;
|
||||
if (input->up && offset > 0)
|
||||
offset -= 2;
|
||||
if (input->buttons & (BtnPow | JoyB))
|
||||
break;
|
||||
}
|
||||
f_close(&in);
|
||||
free(print);
|
||||
free(filePath);
|
||||
}
|
||||
|
||||
fileMenuPath FileMenuPaths[] = {
|
||||
CopyClipboard,
|
||||
MoveClipboard,
|
||||
RenameFile,
|
||||
DeleteFile,
|
||||
HexView,
|
||||
LaunchPayload,
|
||||
RunScript
|
||||
};
|
||||
|
||||
void FileMenu(char *path, FSEntry_t entry){
|
||||
FileMenuEntries[1].name = entry.name;
|
||||
FileMenuEntries[0].sizeUnion = entry.sizeUnion;
|
||||
char attribs[16];
|
||||
char *attribList = GetFileAttribs(entry);
|
||||
s_printf(attribs, "Attribs:%s\n", attribList);
|
||||
free(attribList);
|
||||
FileMenuEntries[2].name = attribs;
|
||||
|
||||
FileMenuEntries[10].hide = !StrEndsWith(entry.name, ".bin");
|
||||
FileMenuEntries[11].hide = !StrEndsWith(entry.name, ".te");
|
||||
|
||||
Vector_t ent = vecFromArray(FileMenuEntries, ARR_LEN(FileMenuEntries), sizeof(MenuEntry_t));
|
||||
gfx_boxGrey(384, 200, 384 + 512, 200 + 320, 0x33);
|
||||
gfx_con_setpos(384 + 16, 200 + 16);
|
||||
int res = newMenu(&ent, 0, 30, 19, ENABLEB | ALWAYSREDRAW | USELIGHTGREY, ent.count);
|
||||
|
||||
if (res <= 4)
|
||||
return;
|
||||
|
||||
FileMenuPaths[res - 5](path, entry);
|
||||
}
|
||||
8
TegraExplorer/source/fs/menus/filemenu.h
Normal file
8
TegraExplorer/source/fs/menus/filemenu.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "../fstypes.h"
|
||||
|
||||
typedef void (*fileMenuPath)(char *path, FSEntry_t entry);
|
||||
|
||||
void FileMenu(char *path, FSEntry_t entry);
|
||||
void RunScript(char *path, FSEntry_t entry);
|
||||
void RunScriptString(char *str, u32 size);
|
||||
134
TegraExplorer/source/fs/menus/foldermenu.c
Normal file
134
TegraExplorer/source/fs/menus/foldermenu.c
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "foldermenu.h"
|
||||
#include "../../err.h"
|
||||
#include "../../gfx/menu.h"
|
||||
#include "../../gfx/gfxutils.h"
|
||||
#include "../fsutils.h"
|
||||
#include <mem/heap.h>
|
||||
#include <string.h>
|
||||
#include <utils/sprintf.h>
|
||||
#include "../../tegraexplorer/tconf.h"
|
||||
#include "../../hid/hid.h"
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include "../../utils/utils.h"
|
||||
#include "../../keys/nca.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include "../fscopy.h"
|
||||
|
||||
MenuEntry_t FolderMenuEntries[] = {
|
||||
{.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "-- Folder menu --"},
|
||||
{.optionUnion = COLORTORGB(COLOR_GREEN) | SKIPBIT}, // For the file name and size
|
||||
{.optionUnion = COLORTORGB(COLOR_VIOLET) | SKIPBIT}, // For the file Attribs
|
||||
{.optionUnion = HIDEBIT},
|
||||
{.optionUnion = COLORTORGB(COLOR_WHITE), .name = "<- Back"},
|
||||
{.optionUnion = COLORTORGB(COLOR_BLUE), .name = "\nCopy to clipboard"},
|
||||
{.optionUnion = COLORTORGB(COLOR_BLUE), .name = "Move to clipboard"},
|
||||
{.optionUnion = COLORTORGB(COLOR_BLUE), .name = "Rename current folder\n"},
|
||||
{.optionUnion = COLORTORGB(COLOR_RED), .name = "Delete current folder"},
|
||||
{.optionUnion = COLORTORGB(COLOR_GREEN), .name = "\nCreate folder"}
|
||||
};
|
||||
|
||||
int UnimplementedFolderException(const char *path){
|
||||
DrawError(newErrCode(TE_ERR_UNIMPLEMENTED));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FolderCopyClipboard(const char *path){
|
||||
SetCopyParams(path, CMODE_CopyFolder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FolderMoveClipboard(const char *path){
|
||||
SetCopyParams(path, CMODE_MoveFolder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DeleteFolder(const char *path){
|
||||
gfx_con_setpos(384 + 16, 200 + 16 + 10 * 16);
|
||||
SETCOLOR(COLOR_RED, COLOR_DARKGREY);
|
||||
gfx_printf("Are you sure? ");
|
||||
|
||||
WaitFor(1000);
|
||||
if (MakeYesNoHorzMenu(3, COLOR_DARKGREY)){
|
||||
gfx_clearscreen();
|
||||
SETCOLOR(COLOR_RED, COLOR_DEFAULT);
|
||||
gfx_printf("\nDeleting... ");
|
||||
ErrCode_t err = FolderDelete(path);
|
||||
if (err.err){
|
||||
DrawError(err);
|
||||
}
|
||||
else return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RenameFolder(const char *path){
|
||||
char *prev = EscapeFolder(path);
|
||||
gfx_clearscreen();
|
||||
|
||||
char *renameTo = ShowKeyboard(strrchr(path, '/') + 1, false);
|
||||
if (renameTo == NULL || !(*renameTo)) // smol memory leak but eh
|
||||
return 0;
|
||||
|
||||
char *dst = CombinePaths(prev, renameTo);
|
||||
|
||||
int res = f_rename(path, dst);
|
||||
if (res){
|
||||
DrawError(newErrCode(res));
|
||||
}
|
||||
|
||||
free(prev);
|
||||
free(dst);
|
||||
free(renameTo);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CreateFolder(const char *path){
|
||||
gfx_clearscreen();
|
||||
|
||||
char *create = ShowKeyboard("New Folder", true);
|
||||
if (create == NULL || !(*create)) // smol memory leak but eh
|
||||
return 0;
|
||||
|
||||
char *dst = CombinePaths(path, create);
|
||||
f_mkdir(dst);
|
||||
|
||||
free(dst);
|
||||
free(create);
|
||||
return 0;
|
||||
}
|
||||
|
||||
folderMenuPath FolderMenuPaths[] = {
|
||||
FolderCopyClipboard,
|
||||
FolderMoveClipboard,
|
||||
RenameFolder,
|
||||
DeleteFolder,
|
||||
CreateFolder
|
||||
};
|
||||
|
||||
int FolderMenu(const char *path){
|
||||
FSEntry_t file = GetFileInfo(path);
|
||||
FolderMenuEntries[1].name = file.name;
|
||||
|
||||
char attribs[16];
|
||||
char *attribList = GetFileAttribs(file);
|
||||
s_printf(attribs, "Attribs:%s\n", attribList);
|
||||
free(attribList);
|
||||
FolderMenuEntries[2].name = attribs;
|
||||
|
||||
// If root, disable all options other than create folder!
|
||||
int isRoot = !strcmp(file.name, "Root");
|
||||
FolderMenuEntries[2].hide = isRoot;
|
||||
for (int i = 5; i <= 8; i++)
|
||||
FolderMenuEntries[i].hide = isRoot;
|
||||
|
||||
|
||||
Vector_t ent = vecFromArray(FolderMenuEntries, ARR_LEN(FolderMenuEntries), sizeof(MenuEntry_t));
|
||||
gfx_boxGrey(384, 200, 384 + 512, 200 + 320, 0x33);
|
||||
gfx_con_setpos(384 + 16, 200 + 16);
|
||||
int res = newMenu(&ent, 0, 30, 19, ENABLEB | ALWAYSREDRAW | USELIGHTGREY, ent.count);
|
||||
|
||||
if (res <= 4)
|
||||
return 0;
|
||||
|
||||
return FolderMenuPaths[res - 5](path);
|
||||
}
|
||||
4
TegraExplorer/source/fs/menus/foldermenu.h
Normal file
4
TegraExplorer/source/fs/menus/foldermenu.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
typedef int (*folderMenuPath)(const char *path);
|
||||
int FolderMenu(const char *path);
|
||||
48
TegraExplorer/source/fs/readers/folderReader.c
Normal file
48
TegraExplorer/source/fs/readers/folderReader.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "folderReader.h"
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include "../../utils/utils.h"
|
||||
#include <mem/heap.h>
|
||||
|
||||
void clearFileVector(Vector_t *v){
|
||||
vecPDefArray(FSEntry_t*, entries, v);
|
||||
for (int i = 0; i < v->count; i++)
|
||||
free(entries[i].name);
|
||||
|
||||
free(v->data);
|
||||
}
|
||||
|
||||
Vector_t /* of type FSEntry_t */ ReadFolder(const char *path, int *res){
|
||||
Vector_t out = newVec(sizeof(FSEntry_t), 16); // we may want to prealloc with the same size as the folder
|
||||
DIR dir;
|
||||
FILINFO fno;
|
||||
|
||||
if ((*res = f_opendir(&dir, path))){
|
||||
// Err!
|
||||
return out;
|
||||
}
|
||||
|
||||
while (!(*res = f_readdir(&dir, &fno)) && fno.fname[0]) {
|
||||
FSEntry_t newEntry = {.optionUnion = fno.fattrib, .name = CpyStr(fno.fname)};
|
||||
|
||||
if (!newEntry.isDir){
|
||||
u64 total = fno.fsize;
|
||||
u8 type = 0;
|
||||
while (total > 1024){
|
||||
total /= 1024;
|
||||
type++;
|
||||
}
|
||||
|
||||
if (type > 3)
|
||||
type = 3;
|
||||
|
||||
newEntry.showSize = 1;
|
||||
newEntry.size = total;
|
||||
newEntry.sizeDef = type;
|
||||
}
|
||||
vecAddElem(&out, newEntry);
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
|
||||
return out;
|
||||
}
|
||||
7
TegraExplorer/source/fs/readers/folderReader.h
Normal file
7
TegraExplorer/source/fs/readers/folderReader.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
#include "../../utils/vector.h"
|
||||
#include "../fstypes.h"
|
||||
|
||||
void clearFileVector(Vector_t *v);
|
||||
Vector_t /* of type FSEntry_t */ ReadFolder(const char *path, int *res);
|
||||
705
TegraExplorer/source/gfx/gfx.c
Normal file
705
TegraExplorer/source/gfx/gfx.c
Normal file
@@ -0,0 +1,705 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2020 CTCaer
|
||||
* Copyright (c) 2019-2020 shchmue
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include "gfx.h"
|
||||
|
||||
gfx_ctxt_t gfx_ctxt;
|
||||
gfx_con_t gfx_con;
|
||||
|
||||
static const u8 _gfx_font[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 032 ( )
|
||||
0x00, 0x30, 0x30, 0x18, 0x18, 0x00, 0x0C, 0x00, // Char 033 (!)
|
||||
0x00, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, // Char 034 (")
|
||||
0x00, 0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, // Char 035 (#)
|
||||
0x00, 0x18, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x18, // Char 036 ($)
|
||||
0x00, 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, // Char 037 (%)
|
||||
0x00, 0x3C, 0x66, 0x3C, 0x1C, 0xE6, 0x66, 0xFC, // Char 038 (&)
|
||||
0x00, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, // Char 039 (')
|
||||
0x00, 0x30, 0x18, 0x0C, 0x0C, 0x18, 0x30, 0x00, // Char 040 (()
|
||||
0x00, 0x0C, 0x18, 0x30, 0x30, 0x18, 0x0C, 0x00, // Char 041 ())
|
||||
0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // Char 042 (*)
|
||||
0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // Char 043 (+)
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0C, 0x00, // Char 044 (,)
|
||||
0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, // Char 045 (-)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // Char 046 (.)
|
||||
0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, // Char 047 (/)
|
||||
0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00, // Char 048 (0)
|
||||
0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00, // Char 049 (1)
|
||||
0x00, 0x3C, 0x62, 0x30, 0x0C, 0x06, 0x7E, 0x00, // Char 050 (2)
|
||||
0x00, 0x3C, 0x62, 0x38, 0x60, 0x66, 0x3C, 0x00, // Char 051 (3)
|
||||
0x00, 0x6C, 0x6C, 0x66, 0xFE, 0x60, 0x60, 0x00, // Char 052 (4)
|
||||
0x00, 0x7E, 0x06, 0x7E, 0x60, 0x66, 0x3C, 0x00, // Char 053 (5)
|
||||
0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00, // Char 054 (6)
|
||||
0x00, 0x7E, 0x30, 0x30, 0x18, 0x18, 0x18, 0x00, // Char 055 (7)
|
||||
0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // Char 056 (8)
|
||||
0x00, 0x3C, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00, // Char 057 (9)
|
||||
0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, // Char 058 (:)
|
||||
0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x0C, 0x00, // Char 059 (;)
|
||||
0x00, 0x70, 0x1C, 0x06, 0x06, 0x1C, 0x70, 0x00, // Char 060 (<)
|
||||
0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, // Char 061 (=)
|
||||
0x00, 0x0E, 0x38, 0x60, 0x60, 0x38, 0x0E, 0x00, // Char 062 (>)
|
||||
0x00, 0x3C, 0x66, 0x30, 0x18, 0x00, 0x18, 0x00, // Char 063 (?)
|
||||
0x00, 0x3C, 0x66, 0x76, 0x76, 0x06, 0x46, 0x3C, // Char 064 (@)
|
||||
0x00, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // Char 065 (A)
|
||||
0x00, 0x3E, 0x66, 0x3E, 0x66, 0x66, 0x3E, 0x00, // Char 066 (B)
|
||||
0x00, 0x3C, 0x66, 0x06, 0x06, 0x66, 0x3C, 0x00, // Char 067 (C)
|
||||
0x00, 0x1E, 0x36, 0x66, 0x66, 0x36, 0x1E, 0x00, // Char 068 (D)
|
||||
0x00, 0x7E, 0x06, 0x1E, 0x06, 0x06, 0x7E, 0x00, // Char 069 (E)
|
||||
0x00, 0x3E, 0x06, 0x1E, 0x06, 0x06, 0x06, 0x00, // Char 070 (F)
|
||||
0x00, 0x3C, 0x66, 0x06, 0x76, 0x66, 0x3C, 0x00, // Char 071 (G)
|
||||
0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // Char 072 (H)
|
||||
0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 073 (I)
|
||||
0x00, 0x78, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, // Char 074 (J)
|
||||
0x00, 0x66, 0x36, 0x1E, 0x1E, 0x36, 0x66, 0x00, // Char 075 (K)
|
||||
0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7E, 0x00, // Char 076 (L)
|
||||
0x00, 0x46, 0x6E, 0x7E, 0x56, 0x46, 0x46, 0x00, // Char 077 (M)
|
||||
0x00, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x66, 0x00, // Char 078 (N)
|
||||
0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 079 (O)
|
||||
0x00, 0x3E, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x00, // Char 080 (P)
|
||||
0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x70, 0x00, // Char 081 (Q)
|
||||
0x00, 0x3E, 0x66, 0x3E, 0x1E, 0x36, 0x66, 0x00, // Char 082 (R)
|
||||
0x00, 0x3C, 0x66, 0x0C, 0x30, 0x66, 0x3C, 0x00, // Char 083 (S)
|
||||
0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // Char 084 (T)
|
||||
0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 085 (U)
|
||||
0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // Char 086 (V)
|
||||
0x00, 0x46, 0x46, 0x56, 0x7E, 0x6E, 0x46, 0x00, // Char 087 (W)
|
||||
0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, // Char 088 (X)
|
||||
0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, // Char 089 (Y)
|
||||
0x00, 0x7E, 0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, // Char 090 (Z)
|
||||
0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, // Char 091 ([)
|
||||
0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, // Char 092 (\)
|
||||
0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, // Char 093 (])
|
||||
0x00, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, // Char 094 (^)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // Char 095 (_)
|
||||
0x00, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, // Char 096 (`)
|
||||
0x00, 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x7C, 0x00, // Char 097 (a)
|
||||
0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, // Char 098 (b)
|
||||
0x00, 0x00, 0x3C, 0x06, 0x06, 0x06, 0x3C, 0x00, // Char 099 (c)
|
||||
0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, // Char 100 (d)
|
||||
0x00, 0x00, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00, // Char 101 (e)
|
||||
0x00, 0x38, 0x0C, 0x3E, 0x0C, 0x0C, 0x0C, 0x00, // Char 102 (f)
|
||||
0x00, 0x00, 0x7C, 0x66, 0x7C, 0x40, 0x3C, 0x00, // Char 103 (g)
|
||||
0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x00, // Char 104 (h)
|
||||
0x00, 0x18, 0x00, 0x1C, 0x18, 0x18, 0x3C, 0x00, // Char 105 (i)
|
||||
0x00, 0x30, 0x00, 0x30, 0x30, 0x30, 0x1E, 0x00, // Char 106 (j)
|
||||
0x00, 0x06, 0x06, 0x36, 0x1E, 0x36, 0x66, 0x00, // Char 107 (k)
|
||||
0x00, 0x1C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 108 (l)
|
||||
0x00, 0x00, 0x66, 0xFE, 0xFE, 0xD6, 0xC6, 0x00, // Char 109 (m)
|
||||
0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, // Char 110 (n)
|
||||
0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 111 (o)
|
||||
0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x00, // Char 112 (p)
|
||||
0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x00, // Char 113 (q)
|
||||
0x00, 0x00, 0x3E, 0x66, 0x06, 0x06, 0x06, 0x00, // Char 114 (r)
|
||||
0x00, 0x00, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x00, // Char 115 (s)
|
||||
0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x70, 0x00, // Char 116 (t)
|
||||
0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, // Char 117 (u)
|
||||
0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // Char 118 (v)
|
||||
0x00, 0x00, 0xC6, 0xD6, 0xFE, 0x7C, 0x6C, 0x00, // Char 119 (w)
|
||||
0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // Char 120 (x)
|
||||
0x00, 0x00, 0x66, 0x66, 0x7C, 0x60, 0x3C, 0x00, // Char 121 (y)
|
||||
0x00, 0x00, 0x7E, 0x30, 0x18, 0x0C, 0x7E, 0x00, // Char 122 (z)
|
||||
0x00, 0x18, 0x08, 0x08, 0x04, 0x08, 0x08, 0x18, // Char 123 ({)
|
||||
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, // Char 124 (|)
|
||||
0x00, 0x0C, 0x08, 0x08, 0x10, 0x08, 0x08, 0x0C, // Char 125 (})
|
||||
0x00, 0x00, 0x00, 0x4C, 0x32, 0x00, 0x00, 0x00, // Char 126 (~)
|
||||
0x00, 0x0C, 0x12, 0x7E, 0x42, 0x42, 0x7E, 0x00, // Char 127 (folder)
|
||||
0x00, 0x0E, 0x12, 0x22, 0x22, 0x22, 0x3E, 0x00, // Char 128 (file)
|
||||
0x00, 0x08, 0x0C, 0x0E, 0x7E, 0x70, 0x30, 0x10, // Char 129 (Charging)
|
||||
};
|
||||
|
||||
u32 YLeftConfig = YLEFT;
|
||||
|
||||
void gfx_clear_grey(u8 color)
|
||||
{
|
||||
memset(gfx_ctxt.fb, color, gfx_ctxt.width * gfx_ctxt.height * 4);
|
||||
}
|
||||
|
||||
void gfx_clear_partial_grey(u8 color, u32 pos_x, u32 height)
|
||||
{
|
||||
memset(gfx_ctxt.fb + pos_x * gfx_ctxt.stride, color, height * 4 * gfx_ctxt.stride);
|
||||
}
|
||||
|
||||
void gfx_clear_color(u32 color)
|
||||
{
|
||||
for (u32 i = 0; i < gfx_ctxt.width * gfx_ctxt.height; i++)
|
||||
gfx_ctxt.fb[i] = color;
|
||||
}
|
||||
|
||||
void gfx_init_ctxt(u32 *fb, u32 width, u32 height, u32 stride)
|
||||
{
|
||||
gfx_ctxt.fb = fb;
|
||||
gfx_ctxt.width = width;
|
||||
gfx_ctxt.height = height;
|
||||
gfx_ctxt.stride = stride;
|
||||
}
|
||||
|
||||
void gfx_con_init()
|
||||
{
|
||||
gfx_con.gfx_ctxt = &gfx_ctxt;
|
||||
gfx_con.fntsz = 16;
|
||||
gfx_con.x = 0;
|
||||
gfx_con.y = 0;
|
||||
gfx_con.savedx = 0;
|
||||
gfx_con.savedy = 0;
|
||||
gfx_con.fgcol = 0xFFCCCCCC;
|
||||
gfx_con.fillbg = 1;
|
||||
gfx_con.bgcol = 0xFF1B1B1B;
|
||||
gfx_con.mute = 0;
|
||||
}
|
||||
|
||||
void gfx_con_setcol(u32 fgcol, int fillbg, u32 bgcol)
|
||||
{
|
||||
gfx_con.fgcol = fgcol;
|
||||
gfx_con.fillbg = fillbg;
|
||||
gfx_con.bgcol = bgcol;
|
||||
}
|
||||
|
||||
void gfx_con_getpos(u32 *x, u32 *y)
|
||||
{
|
||||
*x = YLEFT - gfx_con.y;
|
||||
*y = gfx_con.x;
|
||||
}
|
||||
|
||||
void gfx_con_setpos(u32 x, u32 y)
|
||||
{
|
||||
gfx_con.x = y;
|
||||
gfx_con.y = YLEFT - x;
|
||||
}
|
||||
|
||||
void gfx_putc(char c)
|
||||
{
|
||||
// Duplicate code for performance reasons.
|
||||
switch (gfx_con.fntsz)
|
||||
{
|
||||
case 16:
|
||||
if (c >= 32 && c <= 129)
|
||||
{
|
||||
u8 *cbuf = (u8 *)&_gfx_font[8 * (c - 32)];
|
||||
u32 *fb = gfx_ctxt.fb + gfx_con.x + gfx_con.y * gfx_ctxt.stride;
|
||||
|
||||
for (u32 i = 0; i < 16; i+=2)
|
||||
{
|
||||
u8 v = *cbuf;
|
||||
for (u32 t = 0; t < 8; t++){
|
||||
if (v & 1 || gfx_con.fillbg){
|
||||
u32 setColor = (v & 1) ? gfx_con.fgcol : gfx_con.bgcol;
|
||||
*fb = setColor;
|
||||
*(fb + 1) = setColor;
|
||||
*(fb - gfx_ctxt.stride) = setColor;
|
||||
*(fb - gfx_ctxt.stride + 1) = setColor;
|
||||
}
|
||||
v >>= 1;
|
||||
fb -= gfx_ctxt.stride * 2;
|
||||
}
|
||||
fb += gfx_ctxt.stride * 16 + 2;
|
||||
cbuf++;
|
||||
/*
|
||||
for (u32 k = 0; k < 2; k++)
|
||||
{
|
||||
for (u32 j = 0; j < 8; j++)
|
||||
{
|
||||
if (v & 1)
|
||||
{
|
||||
*fb = gfx_con.fgcol;
|
||||
fb -= gfx_ctxt.stride;
|
||||
*fb = gfx_con.fgcol;
|
||||
}
|
||||
else if (gfx_con.fillbg)
|
||||
{
|
||||
*fb = gfx_con.bgcol;
|
||||
fb -= gfx_ctxt.stride;
|
||||
*fb = gfx_con.bgcol;
|
||||
}
|
||||
else
|
||||
fb -= gfx_ctxt.stride;
|
||||
v >>= 1;
|
||||
fb -= gfx_ctxt.stride;
|
||||
}
|
||||
//fb += gfx_ctxt.stride - 16;
|
||||
//fb = fbtop + 2;
|
||||
fb += (gfx_ctxt.stride * 16) + 1;
|
||||
v = *cbuf;
|
||||
}
|
||||
cbuf++;
|
||||
*/
|
||||
}
|
||||
|
||||
gfx_con.y -= 16;
|
||||
if (gfx_con.y < 16){
|
||||
gfx_con.y = YLeftConfig;
|
||||
gfx_con.x += 16;
|
||||
if (gfx_con.x > 719)
|
||||
gfx_con.x = 0;
|
||||
}
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
gfx_con.y = YLeftConfig;
|
||||
gfx_con.x += 16;
|
||||
if (gfx_con.x > gfx_ctxt.width - 16)
|
||||
gfx_con.x = 0;
|
||||
}
|
||||
else if (c == '\e')
|
||||
gfx_con.y = 575;
|
||||
else if (c == '\a')
|
||||
gfx_con.y = 639;
|
||||
else if (c == '\r')
|
||||
gfx_con.y = YLeftConfig;
|
||||
|
||||
break;
|
||||
case 8:
|
||||
default:
|
||||
if (c >= 30 && c <= 129)
|
||||
{
|
||||
u8 *cbuf = (u8 *)&_gfx_font[8 * (c - 32)];
|
||||
u32 *fb = gfx_ctxt.fb + gfx_con.x + gfx_con.y * gfx_ctxt.stride;
|
||||
for (u32 i = 0; i < 8; i++)
|
||||
{
|
||||
u8 v = *cbuf++;
|
||||
for (u32 j = 0; j < 8; j++)
|
||||
{
|
||||
if (v & 1)
|
||||
*fb = gfx_con.fgcol;
|
||||
else if (gfx_con.fillbg)
|
||||
*fb = gfx_con.bgcol;
|
||||
v >>= 1;
|
||||
fb -= gfx_ctxt.stride;
|
||||
}
|
||||
fb += (gfx_ctxt.stride * 8) + 1;
|
||||
}
|
||||
|
||||
gfx_con.y -= 8;
|
||||
if (gfx_con.y < 8){
|
||||
gfx_con.y = YLeftConfig;
|
||||
gfx_con.x += 8;
|
||||
}
|
||||
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
gfx_con.y = YLeftConfig;
|
||||
gfx_con.x += 8;
|
||||
if (gfx_con.x > gfx_ctxt.width - 8)
|
||||
gfx_con.x = 0;
|
||||
}
|
||||
else if (c == '\e')
|
||||
gfx_con.y = 575;
|
||||
else if (c == '\a')
|
||||
gfx_con.y = 639;
|
||||
else if (c == '\r')
|
||||
gfx_con.y = YLeftConfig;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_puts(const char *s)
|
||||
{
|
||||
if (!s || gfx_con.mute)
|
||||
return;
|
||||
|
||||
for (; *s; s++)
|
||||
gfx_putc(*s);
|
||||
}
|
||||
|
||||
void gfx_puts_small(const char *s){
|
||||
gfx_con.fntsz = 8;
|
||||
|
||||
gfx_puts(s);
|
||||
|
||||
gfx_con.fntsz = 16;
|
||||
}
|
||||
|
||||
void gfx_puts_limit(const char *s, u32 limit){
|
||||
if (!s || gfx_con.mute)
|
||||
return;
|
||||
|
||||
u32 len = strlen(s);
|
||||
|
||||
if (len > limit)
|
||||
limit -= 3;
|
||||
|
||||
for (int i = 0; i < MIN(len, limit); i++)
|
||||
gfx_putc(s[i]);
|
||||
|
||||
if (len > limit + 3)
|
||||
gfx_puts("...");
|
||||
}
|
||||
|
||||
static void _gfx_putn(u32 v, int base, char fill, int fcnt)
|
||||
{
|
||||
char buf[65];
|
||||
static const char digits[] = "0123456789ABCDEFghijklmnopqrstuvwxyz";
|
||||
char *p;
|
||||
int c = fcnt;
|
||||
|
||||
if (base > 36)
|
||||
return;
|
||||
|
||||
bool minus = (base == 10 && v & 0x80000000);
|
||||
|
||||
if (minus)
|
||||
v = (v ^ 0xFFFFFFFF) + 1;
|
||||
|
||||
p = buf + 64;
|
||||
*p = 0;
|
||||
do
|
||||
{
|
||||
c--;
|
||||
*--p = digits[v % base];
|
||||
v /= base;
|
||||
} while (v);
|
||||
|
||||
if (minus){
|
||||
*--p = '-';
|
||||
c--;
|
||||
}
|
||||
|
||||
if (fill != 0)
|
||||
{
|
||||
if (fill == ' ')
|
||||
gfx_con.y -= c * 16;
|
||||
else while (c > 0)
|
||||
{
|
||||
*--p = fill;
|
||||
c--;
|
||||
}
|
||||
}
|
||||
|
||||
gfx_puts(p);
|
||||
}
|
||||
|
||||
void gfx_put_small_sep()
|
||||
{
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 8;
|
||||
gfx_putc('\n');
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
}
|
||||
|
||||
void gfx_put_big_sep()
|
||||
{
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 16;
|
||||
gfx_putc('\n');
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
}
|
||||
|
||||
void gfx_vprintf(const char *fmt, va_list ap)
|
||||
{
|
||||
int fill, fcnt;
|
||||
|
||||
while(*fmt)
|
||||
{
|
||||
if(*fmt == '%')
|
||||
{
|
||||
fmt++;
|
||||
fill = 0;
|
||||
fcnt = 0;
|
||||
if ((*fmt >= '0' && *fmt <= '9') || *fmt == ' ')
|
||||
{
|
||||
fcnt = *fmt;
|
||||
fmt++;
|
||||
if (*fmt >= '0' && *fmt <= '9')
|
||||
{
|
||||
fill = fcnt;
|
||||
fcnt = *fmt - '0';
|
||||
fmt++;
|
||||
}
|
||||
else
|
||||
{
|
||||
fill = ' ';
|
||||
fcnt -= '0';
|
||||
}
|
||||
}
|
||||
switch(*fmt)
|
||||
{
|
||||
case 'c':
|
||||
gfx_putc(va_arg(ap, u32));
|
||||
break;
|
||||
case 's':
|
||||
gfx_puts(va_arg(ap, char *));
|
||||
break;
|
||||
case 'd':
|
||||
_gfx_putn(va_arg(ap, u32), 10, fill, fcnt);
|
||||
break;
|
||||
case 'p':
|
||||
case 'P':
|
||||
case 'x':
|
||||
case 'X':
|
||||
_gfx_putn(va_arg(ap, u32), 16, fill, fcnt);
|
||||
break;
|
||||
case 'k':
|
||||
gfx_con.fgcol = va_arg(ap, u32);
|
||||
break;
|
||||
case 'K':
|
||||
gfx_con.bgcol = va_arg(ap, u32);
|
||||
gfx_con.fillbg = 1;
|
||||
break;
|
||||
case 'b':;
|
||||
u32 b = YLEFT - va_arg(ap, u32);
|
||||
gfx_con.y = b;
|
||||
YLeftConfig = gfx_con.y;
|
||||
break;
|
||||
case '%':
|
||||
gfx_putc('%');
|
||||
break;
|
||||
case '\0':
|
||||
return;
|
||||
default:
|
||||
gfx_putc('%');
|
||||
gfx_putc(*fmt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
gfx_putc(*fmt);
|
||||
fmt++;
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_printf(const char *fmt, ...)
|
||||
{
|
||||
if (gfx_con.mute)
|
||||
return;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
gfx_vprintf(fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void gfx_putc_small(char c){
|
||||
gfx_con.fntsz = 8;
|
||||
gfx_putc(c);
|
||||
gfx_con.fntsz = 16;
|
||||
}
|
||||
|
||||
#define hexDumpLen 0x20
|
||||
|
||||
void gfx_hexdump(u32 base, const u8 *buf, u32 len)
|
||||
{
|
||||
if (gfx_con.mute)
|
||||
return;
|
||||
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 8;
|
||||
for(u32 i = 0; i < len; i++)
|
||||
{
|
||||
if(i % hexDumpLen == 0)
|
||||
{
|
||||
if(i != 0)
|
||||
{
|
||||
gfx_puts("| ");
|
||||
for(u32 j = 0; j < hexDumpLen; j++)
|
||||
{
|
||||
u8 c = buf[i - hexDumpLen + j];
|
||||
if(c >= 32 && c <= 126)
|
||||
gfx_putc(c);
|
||||
else
|
||||
gfx_putc('.');
|
||||
}
|
||||
gfx_putc('\n');
|
||||
}
|
||||
gfx_printf("%08x: ", base + i);
|
||||
}
|
||||
gfx_printf("%02x ", buf[i]);
|
||||
if (i == len - 1)
|
||||
{
|
||||
int ln = len % hexDumpLen != 0;
|
||||
u32 k = hexDumpLen - 1;
|
||||
if (ln)
|
||||
{
|
||||
k = (len & 0xF) - 1;
|
||||
for (u32 j = 0; j < hexDumpLen - k; j++)
|
||||
gfx_puts(" ");
|
||||
}
|
||||
gfx_puts("| ");
|
||||
for(u32 j = 0; j < (ln ? k : k + 1); j++)
|
||||
{
|
||||
u8 c = buf[i - k + j];
|
||||
if(c >= 32 && c <= 126)
|
||||
gfx_putc(c);
|
||||
else
|
||||
gfx_putc('.');
|
||||
}
|
||||
gfx_putc('\n');
|
||||
}
|
||||
}
|
||||
gfx_putc('\n');
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
}
|
||||
|
||||
void gfx_hexdiff(u32 base, const u8 *buf1, const u8 *buf2, u32 len)
|
||||
{
|
||||
if (gfx_con.mute)
|
||||
return;
|
||||
|
||||
if (memcmp(buf1, buf2, len) == 0)
|
||||
{
|
||||
gfx_printf("Diff: No differences found.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 8;
|
||||
for(u32 i = 0; i < len; i+=0x10)
|
||||
{
|
||||
u32 bytes_left = len - i < 0x10 ? len - i : 0x10;
|
||||
if (memcmp(buf1 + i, buf2 + i, bytes_left) == 0)
|
||||
continue;
|
||||
gfx_printf("Diff 1: %08x: ", base + i);
|
||||
for (u32 j = 0; j < bytes_left; j++)
|
||||
{
|
||||
if (buf1[i+j] != buf2[i+j])
|
||||
gfx_con.fgcol = COLOR_ORANGE;
|
||||
gfx_printf("%02x ", buf1[i+j]);
|
||||
gfx_con.fgcol = 0xFFCCCCCC;
|
||||
}
|
||||
gfx_puts("| ");
|
||||
gfx_putc('\n');
|
||||
gfx_printf("Diff 2: %08x: ", base + i);
|
||||
for (u32 j = 0; j < bytes_left; j++)
|
||||
{
|
||||
if (buf1[i+j] != buf2[i+j])
|
||||
gfx_con.fgcol = COLOR_ORANGE;
|
||||
gfx_printf("%02x ", buf2[i+j]);
|
||||
gfx_con.fgcol = 0xFFCCCCCC;
|
||||
}
|
||||
gfx_puts("| ");
|
||||
gfx_putc('\n');
|
||||
gfx_putc('\n');
|
||||
}
|
||||
gfx_putc('\n');
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
}
|
||||
|
||||
static int abs(int x)
|
||||
{
|
||||
if (x < 0)
|
||||
return -x;
|
||||
return x;
|
||||
}
|
||||
|
||||
void gfx_set_pixel(u32 x, u32 y, u32 color)
|
||||
{
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = color;
|
||||
}
|
||||
|
||||
void gfx_set_pixel_horz(int x, int y, u32 color) {
|
||||
*(gfx_ctxt.fb + (YLEFT - x) * gfx_ctxt.stride + y) = color;
|
||||
}
|
||||
|
||||
void gfx_line(int x0, int y0, int x1, int y1, u32 color)
|
||||
{
|
||||
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||
int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||
int err = (dx > dy ? dx : -dy) / 2, e2;
|
||||
|
||||
while (1)
|
||||
{
|
||||
gfx_set_pixel(x0, y0, color);
|
||||
if (x0 == x1 && y0 == y1)
|
||||
break;
|
||||
e2 = err;
|
||||
if (e2 >-dx)
|
||||
{
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 < dy)
|
||||
{
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_set_rect_grey(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
u32 pos = 0;
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
{
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
{
|
||||
memset(&gfx_ctxt.fb[x + y*gfx_ctxt.stride], buf[pos], 4);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_boxGrey(int x0, int y0, int x1, int y1, u8 shade){
|
||||
for (int y = (YLEFT - x0); y >= (YLEFT - x1); y--){
|
||||
memset(gfx_ctxt.fb + y * gfx_ctxt.stride + y0, shade, (y1 - y0 + 1) * 4);
|
||||
}
|
||||
}
|
||||
/*
|
||||
void gfx_boxGrey_old(int x0, int y0, int x1, int y1, u8 shade){
|
||||
for (int y = y0; y <= y1; y++){
|
||||
memset(gfx_ctxt.fb + y * gfx_ctxt.stride + x0, shade, (x1 - x0) * 4);
|
||||
}
|
||||
}
|
||||
*/
|
||||
void gfx_box(int x0, int y0, int x1, int y1, u32 color){
|
||||
for (int y = (YLEFT - x0); y >= (YLEFT - x1); y--){
|
||||
for (int x = y0; x <= y1; x++){
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
void gfx_box_old(int x0, int y0, int x1, int y1, u32 color){
|
||||
for (int x = x0; x < x1 + 1; x++){
|
||||
for (int y = y0; y < y1 + 1; y++){
|
||||
gfx_set_pixel(x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
void gfx_set_rect_rgb(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
u32 pos = 0;
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
{
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
{
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = buf[pos + 2] | (buf[pos + 1] << 8) | (buf[pos] << 16);
|
||||
pos+=3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_set_rect_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
u32 *ptr = (u32 *)buf;
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = *ptr++;
|
||||
}
|
||||
|
||||
void gfx_render_bmp_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
{
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = buf[(size_y + pos_y - 1 - y ) * size_x + x - pos_x];
|
||||
}
|
||||
}
|
||||
96
TegraExplorer/source/gfx/gfx.h
Normal file
96
TegraExplorer/source/gfx/gfx.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2020 CTCaer
|
||||
* Copyright (c) 2018 M4xw
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _GFX_H_
|
||||
#define _GFX_H_
|
||||
|
||||
#include <utils/types.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define EPRINTF(text) gfx_printf("%k"text"%k\n", 0xFFFF0000, 0xFFCCCCCC)
|
||||
#define EPRINTFARGS(text, args...) gfx_printf("%k"text"%k\n", 0xFFFF0000, args, 0xFFCCCCCC)
|
||||
#define WPRINTF(text) gfx_printf("%k"text"%k\n", 0xFFFFDD00, 0xFFCCCCCC)
|
||||
#define WPRINTFARGS(text, args...) gfx_printf("%k"text"%k\n", 0xFFFFDD00, args, 0xFFCCCCCC)
|
||||
|
||||
typedef struct _gfx_ctxt_t
|
||||
{
|
||||
u32 *fb;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 stride;
|
||||
} gfx_ctxt_t;
|
||||
|
||||
typedef struct _gfx_con_t
|
||||
{
|
||||
gfx_ctxt_t *gfx_ctxt;
|
||||
u32 fntsz;
|
||||
u32 x;
|
||||
u32 y;
|
||||
u32 savedx;
|
||||
u32 savedy;
|
||||
u32 fgcol;
|
||||
int fillbg;
|
||||
u32 bgcol;
|
||||
bool mute;
|
||||
} gfx_con_t;
|
||||
|
||||
extern gfx_ctxt_t gfx_ctxt;
|
||||
extern gfx_con_t gfx_con;
|
||||
#define YLEFT 1279
|
||||
|
||||
void gfx_init_ctxt(u32 *fb, u32 width, u32 height, u32 stride);
|
||||
void gfx_clear_grey(u8 color);
|
||||
void gfx_clear_partial_grey(u8 color, u32 pos_x, u32 height);
|
||||
void gfx_clear_color(u32 color);
|
||||
void gfx_con_init();
|
||||
void gfx_con_setcol(u32 fgcol, int fillbg, u32 bgcol);
|
||||
void gfx_con_getpos(u32 *x, u32 *y);
|
||||
void gfx_con_setpos(u32 x, u32 y);
|
||||
void gfx_putc(char c);
|
||||
void gfx_puts(const char *s);
|
||||
void gfx_printf(const char *fmt, ...);
|
||||
void gfx_vprintf(const char *fmt, va_list ap);
|
||||
void gfx_hexdump(u32 base, const u8 *buf, u32 len);
|
||||
void gfx_hexdiff(u32 base, const u8 *buf1, const u8 *buf2, u32 len);
|
||||
void gfx_puts_limit(const char *s, u32 limit);
|
||||
void gfx_puts_small(const char *s);
|
||||
void gfx_putc_small(char c);
|
||||
|
||||
void gfx_set_pixel(u32 x, u32 y, u32 color);
|
||||
void gfx_set_pixel_horz(int x, int y, u32 color);
|
||||
void gfx_line(int x0, int y0, int x1, int y1, u32 color);
|
||||
void gfx_put_small_sep();
|
||||
void gfx_put_big_sep();
|
||||
void gfx_set_rect_grey(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
void gfx_set_rect_rgb(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
void gfx_set_rect_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
void gfx_render_bmp_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
void gfx_box(int x0, int y0, int x1, int y1, u32 color);
|
||||
void gfx_boxGrey(int x0, int y0, int x1, int y1, u8 shade);
|
||||
|
||||
/*
|
||||
#define GFX_SETPOSCORRECTED(x, y) gfx_con_setpos(y - YLEFT, x)
|
||||
#define GFX_BOXCORRECTED(x0, y0, x1, y1, color) gfx_box_old((y0 - YLEFT), x0, (y1 - YLEFT), x1, color)
|
||||
#define GFX_BOXGREYCORRECTED(x0, y0, x1, y1, shade) gfx_boxGrey((y0 - YLEFT), x0, (y1 - YLEFT), x1, shade)
|
||||
*/
|
||||
|
||||
// Global gfx console and context.
|
||||
extern gfx_ctxt_t gfx_ctxt;
|
||||
extern gfx_con_t gfx_con;
|
||||
|
||||
#endif
|
||||
61
TegraExplorer/source/gfx/gfxutils.c
Normal file
61
TegraExplorer/source/gfx/gfxutils.c
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "gfx.h"
|
||||
#include "gfxutils.h"
|
||||
#include <power/max17050.h>
|
||||
#include <power/max17050.h>
|
||||
#include <power/bq24193.h>
|
||||
#include "../hid/hid.h"
|
||||
|
||||
void gfx_printTopInfo() {
|
||||
int battery = 0;
|
||||
max17050_get_property(MAX17050_RepSOC, &battery);
|
||||
|
||||
int current_charge_status = 0;
|
||||
bq24193_get_property(BQ24193_ChargeStatus, ¤t_charge_status);
|
||||
SETCOLOR(COLOR_DEFAULT, COLOR_WHITE);
|
||||
gfx_con_setpos(0, 0);
|
||||
gfx_printf("Tegraexplorer %d.%d.%d | Battery: %d%% %c\n", LP_VER_MJ, LP_VER_MN, LP_VER_BF, battery >> 8, ((current_charge_status) ? 129 : 32));
|
||||
RESETCOLOR;
|
||||
}
|
||||
|
||||
void gfx_clearscreen(){
|
||||
gfx_boxGrey(0, 16, 1279, 703, 0x1b);
|
||||
|
||||
gfx_boxGrey(0, 703, 1279, 719, 0xFF);
|
||||
gfx_boxGrey(0, 0, 1279, 15, 0xFF);
|
||||
|
||||
|
||||
gfx_printTopInfo();
|
||||
}
|
||||
|
||||
MenuEntry_t YesNoEntries[] = {
|
||||
{.optionUnion = COLORTORGB(COLOR_YELLOW), .name = "No"},
|
||||
{.R = 255, .name = "Yes"}
|
||||
};
|
||||
|
||||
int MakeYesNoHorzMenu(int spacesBetween, u32 bg){
|
||||
return MakeHorizontalMenu(YesNoEntries, ARR_LEN(YesNoEntries), spacesBetween, bg, 0);
|
||||
}
|
||||
|
||||
int MakeHorizontalMenu(MenuEntry_t *entries, int len, int spacesBetween, u32 bg, int startPos){
|
||||
u32 initialX = 0, initialY = 0;
|
||||
u32 highlight = startPos;
|
||||
gfx_con_getpos(&initialX, &initialY);
|
||||
|
||||
while (1){
|
||||
for (int i = 0; i < len; i++){
|
||||
(highlight == i) ? SETCOLOR(bg, entries[i].optionUnion) : SETCOLOR(entries[i].optionUnion, bg);
|
||||
gfx_puts(entries[i].name);
|
||||
gfx_con.y -= spacesBetween * 16;
|
||||
}
|
||||
gfx_con_setpos(initialX, initialY);
|
||||
Input_t *input = hidWait();
|
||||
if (input->a)
|
||||
return highlight;
|
||||
else if (input->b)
|
||||
return 0;
|
||||
else if ((input->left || input->down) && highlight > 0)
|
||||
highlight--;
|
||||
else if ((input->right || input->up) && highlight < len - 1)
|
||||
highlight++;
|
||||
}
|
||||
}
|
||||
19
TegraExplorer/source/gfx/gfxutils.h
Normal file
19
TegraExplorer/source/gfx/gfxutils.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "gfx.h"
|
||||
#include "menu.h"
|
||||
|
||||
#define COLOR_WHITE 0xFFFFFFFF
|
||||
#define COLOR_DEFAULT 0xFF1B1B1B
|
||||
#define COLOR_GREY 0xFF888888
|
||||
#define COLOR_DARKGREY 0xFF333333
|
||||
|
||||
#define COLORTORGB(color) (color & 0x00FFFFFF)
|
||||
#define SETCOLOR(fg, bg) gfx_con_setcol(fg, 1, bg)
|
||||
#define RESETCOLOR SETCOLOR(COLOR_WHITE, COLOR_DEFAULT);
|
||||
|
||||
#define RGBUnionToU32(optionUnion) (optionUnion | 0xFF000000)
|
||||
|
||||
void gfx_clearscreen();
|
||||
int MakeHorizontalMenu(MenuEntry_t *entries, int len, int spacesBetween, u32 bg, int startPos);
|
||||
int MakeYesNoHorzMenu(int spacesBetween, u32 bg);
|
||||
void gfx_printTopInfo();
|
||||
168
TegraExplorer/source/gfx/menu.c
Normal file
168
TegraExplorer/source/gfx/menu.c
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "menu.h"
|
||||
#include "../utils/vector.h"
|
||||
#include "gfx.h"
|
||||
#include "gfxutils.h"
|
||||
#include "../hid/hid.h"
|
||||
#include <utils/util.h>
|
||||
#include <utils/btn.h>
|
||||
#include <utils/sprintf.h>
|
||||
#include <string.h>
|
||||
#include <mem/minerva.h>
|
||||
|
||||
const char *sizeDefs[] = {
|
||||
"B ",
|
||||
"KB",
|
||||
"MB",
|
||||
"GB"
|
||||
};
|
||||
|
||||
void _printEntry(MenuEntry_t entry, u32 maxLen, u8 highlighted, u32 bg){
|
||||
if (entry.hide)
|
||||
return;
|
||||
|
||||
(highlighted) ? SETCOLOR(bg, RGBUnionToU32(entry.optionUnion)) : SETCOLOR(RGBUnionToU32(entry.optionUnion), bg);
|
||||
|
||||
if (entry.icon){
|
||||
gfx_putc(entry.icon);
|
||||
gfx_putc(' ');
|
||||
maxLen -= 2;
|
||||
}
|
||||
|
||||
u32 curX = 0, curY = 0;
|
||||
gfx_con_getpos(&curX, &curY);
|
||||
gfx_puts_limit(entry.name, maxLen - ((entry.showSize) ? 8 : 0));
|
||||
if (entry.showSize){
|
||||
(highlighted) ? SETCOLOR(bg, COLOR_BLUE) : SETCOLOR(COLOR_BLUE, bg);
|
||||
gfx_con_setpos(curX + (maxLen - 6) * 16, curY);
|
||||
gfx_printf("%4d", entry.size);
|
||||
gfx_puts_small(sizeDefs[entry.sizeDef]);
|
||||
}
|
||||
|
||||
gfx_putc('\n');
|
||||
}
|
||||
|
||||
int newMenu(Vector_t* vec, int startIndex, int screenLenX, int screenLenY, u8 options, int entryCount) {
|
||||
vecPDefArray(MenuEntry_t*, entries, vec);
|
||||
u32 selected = startIndex;
|
||||
|
||||
while (entries[selected].skip || entries[selected].hide){
|
||||
selected++;
|
||||
if (selected >= vec->count)
|
||||
selected = 0;
|
||||
}
|
||||
|
||||
u32 lastIndex = selected;
|
||||
u32 startX = 0, startY = 0;
|
||||
gfx_con_getpos(&startX, &startY);
|
||||
|
||||
u32 bgColor = (options & USELIGHTGREY) ? COLOR_DARKGREY : COLOR_DEFAULT;
|
||||
|
||||
|
||||
bool redrawScreen = true;
|
||||
Input_t *input = hidRead();
|
||||
|
||||
// Maybe add a check here so you don't read OOB by providing a too high startindex?
|
||||
|
||||
u32 lastPress = 0x666 + get_tmr_ms();
|
||||
u32 holdTimer = 300;
|
||||
|
||||
while(1) {
|
||||
u32 lastDraw = get_tmr_ms();
|
||||
if (redrawScreen || options & ALWAYSREDRAW){
|
||||
if (options & ENABLEPAGECOUNT){
|
||||
SETCOLOR(COLOR_DEFAULT, COLOR_WHITE);
|
||||
char temp[40] = "";
|
||||
s_printf(temp, " Page %d / %d | Total %d entries", (selected / screenLenY) + 1, ((vec->count - 1) / screenLenY) + 1, entryCount);
|
||||
gfx_con_setpos(YLEFT - strlen(temp) * 18, 0);
|
||||
gfx_printf(temp);
|
||||
}
|
||||
|
||||
gfx_con_setpos(startX, startY);
|
||||
|
||||
if (redrawScreen){
|
||||
minerva_periodic_training();
|
||||
gfx_boxGrey(startX, startY, startX + screenLenX * 16, startY + screenLenY * 16, (options & USELIGHTGREY) ? 0x33 : 0x1B);
|
||||
}
|
||||
|
||||
|
||||
int start = selected / screenLenY * screenLenY;
|
||||
gfx_con_setpos(startX, startY);
|
||||
gfx_printf("%b", startX);
|
||||
for (int i = start; i < MIN(vec->count, start + screenLenY); i++){
|
||||
_printEntry(entries[i], screenLenX, (i == selected), bgColor);
|
||||
}
|
||||
gfx_printf("%b", 0);
|
||||
}
|
||||
else if (lastIndex != selected) {
|
||||
u32 minLastCur = MIN(lastIndex, selected);
|
||||
u32 maxLastCur = MAX(lastIndex, selected);
|
||||
gfx_con_setpos(startX, startY + ((minLastCur % screenLenY) * 16));
|
||||
_printEntry(entries[minLastCur], screenLenX, (minLastCur == selected), bgColor);
|
||||
gfx_con_setpos(startX, startY + ((maxLastCur % screenLenY) * 16));
|
||||
_printEntry(entries[maxLastCur], screenLenX, (minLastCur != selected), bgColor);
|
||||
}
|
||||
|
||||
lastIndex = selected;
|
||||
|
||||
SETCOLOR(COLOR_DEFAULT, COLOR_WHITE);
|
||||
gfx_con_setpos(0, 704);
|
||||
gfx_printf("Time taken for screen draw: %dms ", get_tmr_ms() - lastDraw);
|
||||
|
||||
while(hidRead()){
|
||||
if (!(input->buttons)){
|
||||
holdTimer = 300;
|
||||
break;
|
||||
}
|
||||
|
||||
if (input->buttons & (JoyRUp | JoyRDown))
|
||||
holdTimer = 40;
|
||||
|
||||
if ((lastPress + holdTimer) < get_tmr_ms()){
|
||||
if (holdTimer > 50)
|
||||
holdTimer -= 50;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (1){
|
||||
if (hidRead()->a)
|
||||
return selected;
|
||||
else if (input->b && options & ENABLEB)
|
||||
return 0;
|
||||
else if (input->down || input->rDown || input->right){ //Rdown should probs not trigger a page change. Same for RUp
|
||||
u32 temp = (input->right && !(input->down || input->rDown)) ? screenLenY : 1;
|
||||
|
||||
if (vec->count > selected + temp){
|
||||
selected += temp;
|
||||
break;
|
||||
}
|
||||
else if (input->right && (selected / screenLenY != (vec->count - 1) / screenLenY)){
|
||||
selected = vec->count - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (input->up || input->rUp || input->left){
|
||||
u32 temp = (input->left && !(input->up || input->rUp)) ? screenLenY : 1;
|
||||
if (selected >= temp){
|
||||
selected -= temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
holdTimer = 300;
|
||||
gfx_printTopInfo();
|
||||
}
|
||||
}
|
||||
|
||||
lastPress = get_tmr_ms();
|
||||
|
||||
int m = (selected > lastIndex) ? 1 : -1;
|
||||
while (selected > 0 && selected < vec->count - 1 && entries[selected].optionUnion & SKIPHIDEBITS)
|
||||
selected += m;
|
||||
|
||||
if (entries[selected].optionUnion & SKIPHIDEBITS)
|
||||
selected = lastIndex;
|
||||
|
||||
redrawScreen = (selected / screenLenY != lastIndex / screenLenY);
|
||||
}
|
||||
}
|
||||
47
TegraExplorer/source/gfx/menu.h
Normal file
47
TegraExplorer/source/gfx/menu.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
#include "../utils/vector.h"
|
||||
|
||||
typedef void (*menuEntriesGatherer)(Vector_t* vec, void* data);
|
||||
typedef void (*menuPaths)();
|
||||
|
||||
typedef struct _menuEntry {
|
||||
union {
|
||||
struct {
|
||||
u32 B:8;
|
||||
u32 G:8;
|
||||
u32 R:8;
|
||||
u32 skip:1;
|
||||
u32 hide:1;
|
||||
};
|
||||
u32 optionUnion;
|
||||
};
|
||||
char *name;
|
||||
u8 icon;
|
||||
union {
|
||||
struct {
|
||||
u16 size:12;
|
||||
u16 showSize:1;
|
||||
u16 sizeDef:3;
|
||||
};
|
||||
u16 sizeUnion;
|
||||
};
|
||||
} MenuEntry_t;
|
||||
|
||||
#define SKIPHIDEBITS 0x3000000
|
||||
#define RGBCOLOR(r, g, b) (b << 16 | g << 8 | r)
|
||||
|
||||
#define SKIPBIT BIT(24)
|
||||
#define HIDEBIT BIT(25)
|
||||
|
||||
#define ENABLEB BIT(0)
|
||||
#define ENABLEPAGECOUNT BIT(1)
|
||||
#define ALWAYSREDRAW BIT(2)
|
||||
#define USELIGHTGREY BIT(3)
|
||||
|
||||
#define ScreenDefaultLenX 79
|
||||
#define ScreenDefaultLenY 30
|
||||
|
||||
#define ARR_LEN(x) (sizeof(x) / sizeof(*x))
|
||||
|
||||
int newMenu(Vector_t* vec, int startIndex, int screenLenX, int screenLenY, u8 options, int entryCount);
|
||||
106
TegraExplorer/source/hid/hid.c
Normal file
106
TegraExplorer/source/hid/hid.c
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "hid.h"
|
||||
#include <input/joycon.h>
|
||||
#include <utils/btn.h>
|
||||
#include "../gfx/gfx.h"
|
||||
#include <utils/types.h>
|
||||
#include <utils/util.h>
|
||||
#include "../utils/utils.h"
|
||||
#include "../tegraexplorer/tools.h"
|
||||
#include <display/di.h>
|
||||
#include "../config.h"
|
||||
|
||||
static Input_t inputs = {0};
|
||||
u16 LbaseX = 0, LbaseY = 0, RbaseX = 0, RbaseY = 0;
|
||||
|
||||
void hidInit(){
|
||||
jc_init_hw();
|
||||
}
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
Input_t *hidRead(){
|
||||
jc_gamepad_rpt_t *controller = joycon_poll();
|
||||
|
||||
inputs.buttons = 0;
|
||||
u8 left_connected = 0;
|
||||
u8 right_connected = 0;
|
||||
|
||||
if (controller != NULL){
|
||||
if (controller->home && !h_cfg.t210b01)
|
||||
RebootToPayloadOrRcm();
|
||||
|
||||
if (controller->cap)
|
||||
TakeScreenshot();
|
||||
|
||||
inputs.buttons = controller->buttons;
|
||||
|
||||
left_connected = controller->conn_l;
|
||||
right_connected = controller->conn_r;
|
||||
}
|
||||
|
||||
|
||||
u8 btn = btn_read();
|
||||
inputs.volp = (btn & BTN_VOL_UP) ? 1 : 0;
|
||||
inputs.volm = (btn & BTN_VOL_DOWN) ? 1 : 0;
|
||||
inputs.power = (btn & BTN_POWER) ? 1 : 0;
|
||||
|
||||
if (left_connected){
|
||||
if ((LbaseX == 0 || LbaseY == 0) || controller->l3){
|
||||
LbaseX = controller->lstick_x;
|
||||
LbaseY = controller->lstick_y;
|
||||
}
|
||||
|
||||
inputs.up = (controller->up || inputs.volp || (controller->lstick_y > LbaseY + 500)) ? 1 : 0;
|
||||
inputs.down = (controller->down || inputs.volm || (controller->lstick_y < LbaseY - 500)) ? 1 : 0;
|
||||
inputs.left = (controller->left || (controller->lstick_x < LbaseX - 500)) ? 1 : 0;
|
||||
inputs.right = (controller->right || (controller->lstick_x > LbaseX + 500)) ? 1 : 0;
|
||||
}
|
||||
else {
|
||||
inputs.up = inputs.volp;
|
||||
inputs.down = inputs.volm;
|
||||
}
|
||||
|
||||
if (right_connected){
|
||||
if ((RbaseX == 0 || RbaseY == 0) || controller->r3){
|
||||
RbaseX = controller->rstick_x;
|
||||
RbaseY = controller->rstick_y;
|
||||
}
|
||||
|
||||
inputs.rUp = (controller->rstick_y > RbaseY + 500) ? 1 : 0;
|
||||
inputs.rDown = (controller->rstick_y < RbaseY - 500) ? 1 : 0;
|
||||
inputs.rLeft = (controller->rstick_x < RbaseX - 500) ? 1 : 0;
|
||||
inputs.rRight = (controller->rstick_x > RbaseX + 500) ? 1 : 0;
|
||||
}
|
||||
inputs.a = inputs.a || inputs.power;
|
||||
|
||||
return &inputs;
|
||||
}
|
||||
|
||||
Input_t *hidWaitMask(u32 mask){
|
||||
Input_t *in = hidRead();
|
||||
|
||||
while (in->buttons & mask)
|
||||
hidRead();
|
||||
|
||||
while (!(in->buttons & mask)){
|
||||
hidRead();
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
Input_t *hidWait(){
|
||||
Input_t *in = hidRead();
|
||||
|
||||
while (in->buttons)
|
||||
hidRead();
|
||||
|
||||
while (!(in->buttons))
|
||||
hidRead();
|
||||
return in;
|
||||
}
|
||||
|
||||
bool hidConnected(){
|
||||
jc_gamepad_rpt_t *controller = joycon_poll();
|
||||
return (controller->conn_l && controller->conn_r) ? 1 : 0;
|
||||
}
|
||||
77
TegraExplorer/source/hid/hid.h
Normal file
77
TegraExplorer/source/hid/hid.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
|
||||
//#define BIT(n) (1U << n)
|
||||
|
||||
#define JoyY BIT(0)
|
||||
#define JoyX BIT(1)
|
||||
#define JoyB BIT(2)
|
||||
#define JoyA BIT(3)
|
||||
#define JoyRB BIT(6)
|
||||
#define JoyMenu BIT(12)
|
||||
#define JoyLDown BIT(16)
|
||||
#define JoyLUp BIT(17)
|
||||
#define JoyLRight BIT(18)
|
||||
#define JoyLLeft BIT(19)
|
||||
#define JoyLB BIT(22)
|
||||
#define BtnPow BIT(24)
|
||||
#define BtnVolP BIT(25)
|
||||
#define BtnVolM BIT(26)
|
||||
#define JoyRDown BIT(27)
|
||||
#define JoyRUp BIT(28)
|
||||
#define JoyRRight BIT(29)
|
||||
#define JoyRLeft BIT(30)
|
||||
|
||||
#define WAITBUTTONS (JoyY | JoyX | JoyB | JoyA | JoyLDown | JoyLUp | JoyLRight | JoyLLeft)
|
||||
|
||||
typedef struct _inputs {
|
||||
union {
|
||||
struct {
|
||||
// Joy-Con (R).
|
||||
u32 y:1;
|
||||
u32 x:1;
|
||||
u32 b:1;
|
||||
u32 a:1;
|
||||
u32 sr_r:1;
|
||||
u32 sl_r:1;
|
||||
u32 r:1;
|
||||
u32 zr:1;
|
||||
|
||||
// Shared
|
||||
u32 minus:1;
|
||||
u32 plus:1;
|
||||
u32 r3:1;
|
||||
u32 l3:1;
|
||||
u32 home:1;
|
||||
u32 cap:1;
|
||||
u32 pad:1;
|
||||
u32 wired:1;
|
||||
|
||||
// Joy-Con (L).
|
||||
u32 down:1;
|
||||
u32 up:1;
|
||||
u32 right:1;
|
||||
u32 left:1;
|
||||
u32 sr_l:1;
|
||||
u32 sl_l:1;
|
||||
u32 l:1;
|
||||
u32 zl:1;
|
||||
|
||||
u32 power:1;
|
||||
u32 volp:1;
|
||||
u32 volm:1;
|
||||
|
||||
u32 rDown:1;
|
||||
u32 rUp:1;
|
||||
u32 rRight:1;
|
||||
u32 rLeft:1;
|
||||
};
|
||||
u32 buttons;
|
||||
};
|
||||
} Input_t;
|
||||
|
||||
void hidInit();
|
||||
Input_t *hidRead();
|
||||
Input_t *hidWait();
|
||||
Input_t *hidWaitMask(u32 mask);
|
||||
bool hidConnected();
|
||||
176
TegraExplorer/source/keys/key_sources.inl
Normal file
176
TegraExplorer/source/keys/key_sources.inl
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 shchmue
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#define KB_FIRMWARE_VERSION_100_200 0
|
||||
#define KB_FIRMWARE_VERSION_300 1
|
||||
#define KB_FIRMWARE_VERSION_301 2
|
||||
#define KB_FIRMWARE_VERSION_400 3
|
||||
#define KB_FIRMWARE_VERSION_500 4
|
||||
#define KB_FIRMWARE_VERSION_600 5
|
||||
#define KB_FIRMWARE_VERSION_620 6
|
||||
#define KB_FIRMWARE_VERSION_700 7
|
||||
#define KB_FIRMWARE_VERSION_810 8
|
||||
#define KB_FIRMWARE_VERSION_900 9
|
||||
#define KB_FIRMWARE_VERSION_910 10
|
||||
#define KB_FIRMWARE_VERSION_MAX KB_FIRMWARE_VERSION_910
|
||||
|
||||
static const u8 null_hash[0x20] __attribute__((aligned(4))) = {
|
||||
0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24,
|
||||
0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55};
|
||||
|
||||
static const u8 keyblob_key_source[][0x10] __attribute__((aligned(4))) = {
|
||||
{0xDF, 0x20, 0x6F, 0x59, 0x44, 0x54, 0xEF, 0xDC, 0x70, 0x74, 0x48, 0x3B, 0x0D, 0xED, 0x9F, 0xD3}, //1.0.0
|
||||
{0x0C, 0x25, 0x61, 0x5D, 0x68, 0x4C, 0xEB, 0x42, 0x1C, 0x23, 0x79, 0xEA, 0x82, 0x25, 0x12, 0xAC}, //3.0.0
|
||||
{0x33, 0x76, 0x85, 0xEE, 0x88, 0x4A, 0xAE, 0x0A, 0xC2, 0x8A, 0xFD, 0x7D, 0x63, 0xC0, 0x43, 0x3B}, //3.0.1
|
||||
{0x2D, 0x1F, 0x48, 0x80, 0xED, 0xEC, 0xED, 0x3E, 0x3C, 0xF2, 0x48, 0xB5, 0x65, 0x7D, 0xF7, 0xBE}, //4.0.0
|
||||
{0xBB, 0x5A, 0x01, 0xF9, 0x88, 0xAF, 0xF5, 0xFC, 0x6C, 0xFF, 0x07, 0x9E, 0x13, 0x3C, 0x39, 0x80}, //5.0.0
|
||||
{0xD8, 0xCC, 0xE1, 0x26, 0x6A, 0x35, 0x3F, 0xCC, 0x20, 0xF3, 0x2D, 0x3B, 0x51, 0x7D, 0xE9, 0xC0} //6.0.0
|
||||
};
|
||||
|
||||
static const u8 master_kek_sources[KB_FIRMWARE_VERSION_MAX - KB_FIRMWARE_VERSION_620 + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x37, 0x4B, 0x77, 0x29, 0x59, 0xB4, 0x04, 0x30, 0x81, 0xF6, 0xE5, 0x8C, 0x6D, 0x36, 0x17, 0x9A}, //6.2.0
|
||||
{0x9A, 0x3E, 0xA9, 0xAB, 0xFD, 0x56, 0x46, 0x1C, 0x9B, 0xF6, 0x48, 0x7F, 0x5C, 0xFA, 0x09, 0x5C}, //7.0.0
|
||||
{0xDE, 0xDC, 0xE3, 0x39, 0x30, 0x88, 0x16, 0xF8, 0xAE, 0x97, 0xAD, 0xEC, 0x64, 0x2D, 0x41, 0x41}, //8.1.0
|
||||
{0x1A, 0xEC, 0x11, 0x82, 0x2B, 0x32, 0x38, 0x7A, 0x2B, 0xED, 0xBA, 0x01, 0x47, 0x7E, 0x3B, 0x67}, //9.0.0
|
||||
{0x30, 0x3F, 0x02, 0x7E, 0xD8, 0x38, 0xEC, 0xD7, 0x93, 0x25, 0x34, 0xB5, 0x30, 0xEB, 0xCA, 0x7A}, //9.1.0
|
||||
};
|
||||
|
||||
static const u8 master_key_vectors[KB_FIRMWARE_VERSION_MAX + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D}, /* Zeroes encrypted with Master Key 00. */
|
||||
{0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD}, /* Master key 00 encrypted with Master key 01. */
|
||||
{0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72}, /* Master key 01 encrypted with Master key 02. */
|
||||
{0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07}, /* Master key 02 encrypted with Master key 03. */
|
||||
{0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9}, /* Master key 03 encrypted with Master key 04. */
|
||||
{0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE}, /* Master key 04 encrypted with Master key 05. */
|
||||
{0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */
|
||||
{0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F}, /* Master key 06 encrypted with Master key 07. */
|
||||
{0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29}, /* Master key 07 encrypted with Master key 08. */
|
||||
{0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80}, /* Master key 08 encrypted with Master key 09. */
|
||||
{0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A}, /* Master key 09 encrypted with Master key 0A. */
|
||||
};
|
||||
|
||||
//======================================Keys======================================//
|
||||
// from Package1 -> Secure_Monitor
|
||||
static const u8 aes_kek_generation_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9};
|
||||
static const u8 aes_kek_seed_01[0x10] __attribute__((aligned(4))) = {
|
||||
0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74};
|
||||
static const u8 aes_kek_seed_03[0x10] __attribute__((aligned(4))) = {
|
||||
0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB};
|
||||
static const u8 package2_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7};
|
||||
static const u8 titlekek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B};
|
||||
static const u8 retail_specific_aes_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95};
|
||||
|
||||
// from Package1ldr (or Secure_Monitor on 6.2.0+)
|
||||
static const u8 keyblob_mac_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x59, 0xC7, 0xFB, 0x6F, 0xBE, 0x9B, 0xBE, 0x87, 0x65, 0x6B, 0x15, 0xC0, 0x53, 0x73, 0x36, 0xA5};
|
||||
static const u8 master_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0xD8, 0xA2, 0x41, 0x0A, 0xC6, 0xC5, 0x90, 0x01, 0xC6, 0x1D, 0x6A, 0x26, 0x7C, 0x51, 0x3F, 0x3C};
|
||||
static const u8 per_console_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x4F, 0x02, 0x5F, 0x0E, 0xB6, 0x6D, 0x11, 0x0E, 0xDC, 0x32, 0x7D, 0x41, 0x86, 0xC2, 0xF4, 0x78};
|
||||
static const u8 device_master_key_source_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x0C, 0x91, 0x09, 0xDB, 0x93, 0x93, 0x07, 0x81, 0x07, 0x3C, 0xC4, 0x16, 0x22, 0x7C, 0x6C, 0x28};
|
||||
static const u8 mariko_master_kek_sources[KB_FIRMWARE_VERSION_MAX - KB_FIRMWARE_VERSION_600 + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x77, 0x60, 0x5A, 0xD2, 0xEE, 0x6E, 0xF8, 0x3C, 0x3F, 0x72, 0xE2, 0x59, 0x9D, 0xAC, 0x5E, 0x56}, // 6.0.0.
|
||||
{0x1E, 0x80, 0xB8, 0x17, 0x3E, 0xC0, 0x60, 0xAA, 0x11, 0xBE, 0x1A, 0x4A, 0xA6, 0x6F, 0xE4, 0xAE}, // 6.2.0.
|
||||
{0x94, 0x08, 0x67, 0xBD, 0x0A, 0x00, 0x38, 0x84, 0x11, 0xD3, 0x1A, 0xDB, 0xDD, 0x8D, 0xF1, 0x8A}, // 7.0.0.
|
||||
{0x5C, 0x24, 0xE3, 0xB8, 0xB4, 0xF7, 0x00, 0xC2, 0x3C, 0xFD, 0x0A, 0xCE, 0x13, 0xC3, 0xDC, 0x23}, // 8.1.0.
|
||||
{0x86, 0x69, 0xF0, 0x09, 0x87, 0xC8, 0x05, 0xAE, 0xB5, 0x7B, 0x48, 0x74, 0xDE, 0x62, 0xA6, 0x13}, // 9.0.0.
|
||||
{0x0E, 0x44, 0x0C, 0xED, 0xB4, 0x36, 0xC0, 0x3F, 0xAA, 0x1D, 0xAE, 0xBF, 0x62, 0xB1, 0x09, 0x82}, // 9.1.0.
|
||||
};
|
||||
|
||||
static const u8 device_master_key_source_sources[KB_FIRMWARE_VERSION_MAX - KB_FIRMWARE_VERSION_400 + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}, /* 4.0.0 Device Master Key Source Source. */
|
||||
{0x6C, 0xEF, 0xC6, 0x27, 0x8B, 0xEC, 0x8A, 0x91, 0x99, 0xAB, 0x24, 0xAC, 0x4F, 0x1C, 0x8F, 0x1C}, /* 5.0.0 Device Master Key Source Source. */
|
||||
{0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4}, /* 6.0.0 Device Master Key Source Source. */
|
||||
{0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17}, /* 6.2.0 Device Master Key Source Source. */
|
||||
{0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D}, /* 7.0.0 Device Master Key Source Source. */
|
||||
{0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE}, /* 8.1.0 Device Master Key Source Source. */
|
||||
{0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49}, /* 9.0.0 Device Master Key Source Source. */
|
||||
{0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94}, /* 9.1.0 Device Master Key Source Source. */
|
||||
};
|
||||
|
||||
// from ES
|
||||
static const u8 eticket_rsa_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0XDB, 0XA4, 0X51, 0X12, 0X4C, 0XA0, 0XA9, 0X83, 0X68, 0X14, 0XF5, 0XED, 0X95, 0XE3, 0X12, 0X5B};
|
||||
static const u8 eticket_rsa_kekek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0X46, 0X6E, 0X57, 0XB7, 0X4A, 0X44, 0X7F, 0X02, 0XF3, 0X21, 0XCD, 0XE5, 0X8F, 0X2F, 0X55, 0X35};
|
||||
|
||||
// from SSL
|
||||
static const u8 ssl_rsa_kek_source_x[0x10] __attribute__((aligned(4))) = {
|
||||
0X7F, 0X5B, 0XB0, 0X84, 0X7B, 0X25, 0XAA, 0X67, 0XFA, 0XC8, 0X4B, 0XE2, 0X3D, 0X7B, 0X69, 0X03};
|
||||
static const u8 ssl_rsa_kek_source_y[0x10] __attribute__((aligned(4))) = {
|
||||
0X9A, 0X38, 0X3B, 0XF4, 0X31, 0XD0, 0XBD, 0X81, 0X32, 0X53, 0X4B, 0XA9, 0X64, 0X39, 0X7D, 0XE3};
|
||||
|
||||
static const u8 device_master_kek_sources[KB_FIRMWARE_VERSION_MAX - KB_FIRMWARE_VERSION_400 + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x88, 0x62, 0x34, 0x6E, 0xFA, 0xF7, 0xD8, 0x3F, 0xE1, 0x30, 0x39, 0x50, 0xF0, 0xB7, 0x5D, 0x5D}, /* 4.0.0 Device Master Kek Source. */
|
||||
{0x06, 0x1E, 0x7B, 0xE9, 0x6D, 0x47, 0x8C, 0x77, 0xC5, 0xC8, 0xE7, 0x94, 0x9A, 0xA8, 0x5F, 0x2E}, /* 5.0.0 Device Master Kek Source. */
|
||||
{0x99, 0xFA, 0x98, 0xBD, 0x15, 0x1C, 0x72, 0xFD, 0x7D, 0x9A, 0xD5, 0x41, 0x00, 0xFD, 0xB2, 0xEF}, /* 6.0.0 Device Master Kek Source. */
|
||||
{0x81, 0x3C, 0x6C, 0xBF, 0x5D, 0x21, 0xDE, 0x77, 0x20, 0xD9, 0x6C, 0xE3, 0x22, 0x06, 0xAE, 0xBB}, /* 6.2.0 Device Master Kek Source. */
|
||||
{0x86, 0x61, 0xB0, 0x16, 0xFA, 0x7A, 0x9A, 0xEA, 0xF6, 0xF5, 0xBE, 0x1A, 0x13, 0x5B, 0x6D, 0x9E}, /* 7.0.0 Device Master Kek Source. */
|
||||
{0xA6, 0x81, 0x71, 0xE7, 0xB5, 0x23, 0x74, 0xB0, 0x39, 0x8C, 0xB7, 0xFF, 0xA0, 0x62, 0x9F, 0x8D}, /* 8.1.0 Device Master Kek Source. */
|
||||
{0x03, 0xE7, 0xEB, 0x43, 0x1B, 0xCF, 0x5F, 0xB5, 0xED, 0xDC, 0x97, 0xAE, 0x21, 0x8D, 0x19, 0xED}, /* 9.0.0 Device Master Kek Source. */
|
||||
{0xCE, 0xFE, 0x41, 0x0F, 0x46, 0x9A, 0x30, 0xD6, 0xF2, 0xE9, 0x0C, 0x6B, 0xB7, 0x15, 0x91, 0x36}, /* 9.1.0 Device Master Kek Source. */
|
||||
};
|
||||
|
||||
// from SPL
|
||||
static const u8 aes_key_generation_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8};
|
||||
|
||||
// from FS
|
||||
static const u8 bis_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x34, 0xC1, 0xA0, 0xC4, 0x82, 0x58, 0xF8, 0xB4, 0xFA, 0x9E, 0x5E, 0x6A, 0xDA, 0xFC, 0x7E, 0x4F};
|
||||
static const u8 bis_key_source[3][0x20] __attribute__((aligned(4))) = {
|
||||
{0xF8, 0x3F, 0x38, 0x6E, 0x2C, 0xD2, 0xCA, 0x32, 0xA8, 0x9A, 0xB9, 0xAA, 0x29, 0xBF, 0xC7, 0x48,
|
||||
0x7D, 0x92, 0xB0, 0x3A, 0xA8, 0xBF, 0xDE, 0xE1, 0xA7, 0x4C, 0x3B, 0x6E, 0x35, 0xCB, 0x71, 0x06},
|
||||
{0x41, 0x00, 0x30, 0x49, 0xDD, 0xCC, 0xC0, 0x65, 0x64, 0x7A, 0x7E, 0xB4, 0x1E, 0xED, 0x9C, 0x5F,
|
||||
0x44, 0x42, 0x4E, 0xDA, 0xB4, 0x9D, 0xFC, 0xD9, 0x87, 0x77, 0x24, 0x9A, 0xDC, 0x9F, 0x7C, 0xA4},
|
||||
{0x52, 0xC2, 0xE9, 0xEB, 0x09, 0xE3, 0xEE, 0x29, 0x32, 0xA1, 0x0C, 0x1F, 0xB6, 0xA0, 0x92, 0x6C,
|
||||
0x4D, 0x12, 0xE1, 0x4B, 0x2A, 0x47, 0x4C, 0x1C, 0x09, 0xCB, 0x03, 0x59, 0xF0, 0x15, 0xF4, 0xE4}
|
||||
};
|
||||
static const u8 header_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x1F, 0x12, 0x91, 0x3A, 0x4A, 0xCB, 0xF0, 0x0D, 0x4C, 0xDE, 0x3A, 0xF6, 0xD5, 0x23, 0x88, 0x2A};
|
||||
static const u8 header_key_source[0x20] __attribute__((aligned(4))) = {
|
||||
0x5A, 0x3E, 0xD8, 0x4F, 0xDE, 0xC0, 0xD8, 0x26, 0x31, 0xF7, 0xE2, 0x5D, 0x19, 0x7B, 0xF5, 0xD0,
|
||||
0x1C, 0x9B, 0x7B, 0xFA, 0xF6, 0x28, 0x18, 0x3D, 0x71, 0xF6, 0x4D, 0x73, 0xF1, 0x50, 0xB9, 0xD2};
|
||||
static const u8 key_area_key_sources[3][0x10] __attribute__((aligned(4))) = {
|
||||
{0x7F, 0x59, 0x97, 0x1E, 0x62, 0x9F, 0x36, 0xA1, 0x30, 0x98, 0x06, 0x6F, 0x21, 0x44, 0xC3, 0x0D}, // application
|
||||
{0x32, 0x7D, 0x36, 0x08, 0x5A, 0xD1, 0x75, 0x8D, 0xAB, 0x4E, 0x6F, 0xBA, 0xA5, 0x55, 0xD8, 0x82}, // ocean
|
||||
{0x87, 0x45, 0xF1, 0xBB, 0xA6, 0xBE, 0x79, 0x64, 0x7D, 0x04, 0x8B, 0xA6, 0x7B, 0x5F, 0xDA, 0x4A}, // system
|
||||
};
|
||||
static const u8 save_mac_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0XD8, 0X9C, 0X23, 0X6E, 0XC9, 0X12, 0X4E, 0X43, 0XC8, 0X2B, 0X03, 0X87, 0X43, 0XF9, 0XCF, 0X1B};
|
||||
static const u8 save_mac_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0XE4, 0XCD, 0X3D, 0X4A, 0XD5, 0X0F, 0X74, 0X28, 0X45, 0XA4, 0X87, 0XE5, 0XA0, 0X63, 0XEA, 0X1F};
|
||||
static const u8 save_mac_sd_card_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0X04, 0X89, 0XEF, 0X5D, 0X32, 0X6E, 0X1A, 0X59, 0XC4, 0XB7, 0XAB, 0X8C, 0X36, 0X7A, 0XAB, 0X17};
|
||||
static const u8 save_mac_sd_card_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0X6F, 0X64, 0X59, 0X47, 0XC5, 0X61, 0X46, 0XF9, 0XFF, 0XA0, 0X45, 0XD5, 0X95, 0X33, 0X29, 0X18};
|
||||
static const u8 sd_card_custom_storage_key_source[0x20] __attribute__((aligned(4))) = {
|
||||
0X37, 0X0C, 0X34, 0X5E, 0X12, 0XE4, 0XCE, 0XFE, 0X21, 0XB5, 0X8E, 0X64, 0XDB, 0X52, 0XAF, 0X35,
|
||||
0X4F, 0X2C, 0XA5, 0XA3, 0XFC, 0X99, 0X9A, 0X47, 0XC0, 0X3E, 0XE0, 0X04, 0X48, 0X5B, 0X2F, 0XD0};
|
||||
static const u8 sd_card_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0X88, 0X35, 0X8D, 0X9C, 0X62, 0X9B, 0XA1, 0XA0, 0X01, 0X47, 0XDB, 0XE0, 0X62, 0X1B, 0X54, 0X32};
|
||||
static const u8 sd_card_nca_key_source[0x20] __attribute__((aligned(4))) = {
|
||||
0X58, 0X41, 0XA2, 0X84, 0X93, 0X5B, 0X56, 0X27, 0X8B, 0X8E, 0X1F, 0XC5, 0X18, 0XE9, 0X9F, 0X2B,
|
||||
0X67, 0XC7, 0X93, 0XF0, 0XF2, 0X4F, 0XDE, 0XD0, 0X75, 0X49, 0X5D, 0XCA, 0X00, 0X6D, 0X99, 0XC2};
|
||||
static const u8 sd_card_save_key_source[0x20] __attribute__((aligned(4))) = {
|
||||
0X24, 0X49, 0XB7, 0X22, 0X72, 0X67, 0X03, 0XA8, 0X19, 0X65, 0XE6, 0XE3, 0XEA, 0X58, 0X2F, 0XDD,
|
||||
0X9A, 0X95, 0X15, 0X17, 0XB1, 0X6E, 0X8F, 0X7F, 0X1F, 0X68, 0X26, 0X31, 0X52, 0XEA, 0X29, 0X6A};
|
||||
53
TegraExplorer/source/keys/keyfile.c
Normal file
53
TegraExplorer/source/keys/keyfile.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "keys.h"
|
||||
#include "keyfile.h"
|
||||
#include <utils/types.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <string.h>
|
||||
#include <utils/ini.h>
|
||||
#include "../tegraexplorer/tconf.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include "../gfx/gfx.h"
|
||||
|
||||
#define GetHexFromChar(c) ((c & 0x0F) + (c >= 'A' ? 9 : 0))
|
||||
|
||||
char *getKey(const char *search, link_t *inilist){
|
||||
LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, inilist, link){
|
||||
if (ini_sec->type == INI_CHOICE){
|
||||
LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link)
|
||||
{
|
||||
if (!strcmp(search, kv->key))
|
||||
return kv->val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void AddKey(u8 *buff, char *in, u32 len){
|
||||
if (in == NULL || strlen(in) != len * 2)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
buff[i] = (u8)((GetHexFromChar(in[i * 2]) << 4) | GetHexFromChar(in[i * 2 + 1]));
|
||||
}
|
||||
|
||||
int GetKeysFromFile(char *path){
|
||||
gfx_puts("Grabbing keys from prod.keys...");
|
||||
if (!sd_mount())
|
||||
return 1;
|
||||
|
||||
LIST_INIT(iniList); // Whatever we'll just let this die in memory hell
|
||||
if (!ini_parse(&iniList, path, false))
|
||||
return 1;
|
||||
|
||||
// add biskeys, mkey 0, header_key, save_mac_key
|
||||
AddKey(dumpedKeys.bis_key[0], getKey("bis_key_00", &iniList), AES_128_KEY_SIZE * 2);
|
||||
AddKey(dumpedKeys.bis_key[1], getKey("bis_key_01", &iniList), AES_128_KEY_SIZE * 2);
|
||||
AddKey(dumpedKeys.bis_key[2], getKey("bis_key_02", &iniList), AES_128_KEY_SIZE * 2);
|
||||
AddKey(dumpedKeys.master_key, getKey("master_key_00", &iniList), AES_128_KEY_SIZE);
|
||||
AddKey(dumpedKeys.header_key, getKey("header_key", &iniList), AES_128_KEY_SIZE * 2);
|
||||
AddKey(dumpedKeys.save_mac_key, getKey("save_mac_key", &iniList), AES_128_KEY_SIZE);
|
||||
|
||||
gfx_puts(" Done");
|
||||
return 0;
|
||||
}
|
||||
3
TegraExplorer/source/keys/keyfile.h
Normal file
3
TegraExplorer/source/keys/keyfile.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int GetKeysFromFile(char *path);
|
||||
295
TegraExplorer/source/keys/keys.c
Normal file
295
TegraExplorer/source/keys/keys.c
Normal file
@@ -0,0 +1,295 @@
|
||||
#include "keys.h"
|
||||
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <storage/nx_sd.h>
|
||||
#include <storage/sdmmc.h>
|
||||
#include <utils/btn.h>
|
||||
#include <utils/list.h>
|
||||
#include <utils/sprintf.h>
|
||||
#include <utils/util.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <mem/heap.h>
|
||||
#include <mem/mc.h>
|
||||
#include <mem/minerva.h>
|
||||
#include <mem/sdram.h>
|
||||
#include <sec/se.h>
|
||||
#include <sec/se_t210.h>
|
||||
#include <sec/tsec.h>
|
||||
#include <soc/fuse.h>
|
||||
#include <mem/smmu.h>
|
||||
#include <soc/t210.h>
|
||||
#include <display/di.h>
|
||||
#include <gfx_utils.h>
|
||||
#include "../config.h"
|
||||
#include "../storage/emummc.h"
|
||||
#include "../gfx/gfx.h"
|
||||
#include "../tegraexplorer/tconf.h"
|
||||
#include "../storage/mountmanager.h"
|
||||
#include "../storage/nx_emmc.h"
|
||||
|
||||
#include "key_sources.inl"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
#define DPRINTF(x)
|
||||
#define TSEC_KEY_DATA_OFFSET 0x300
|
||||
#define PKG1_MAX_SIZE 0x40000
|
||||
#define PKG1_OFFSET 0x100000
|
||||
#define KEYBLOB_OFFSET 0x180000
|
||||
|
||||
typedef struct _bl_hdr_t210b01_t
|
||||
{
|
||||
u8 aes_mac[0x10];
|
||||
u8 rsa_sig[0x100];
|
||||
u8 salt[0x20];
|
||||
u8 sha256[0x20];
|
||||
u32 version;
|
||||
u32 size;
|
||||
u32 load_addr;
|
||||
u32 entrypoint;
|
||||
u8 rsvd[0x10];
|
||||
} bl_hdr_t210b01_t;
|
||||
|
||||
|
||||
static int _key_exists(const void *data) { return memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00", 8) != 0; };
|
||||
|
||||
static ALWAYS_INLINE u8 *_find_tsec_fw(const u8 *pkg1) {
|
||||
const u32 tsec_fw_align = 0x100;
|
||||
const u32 tsec_fw_first_instruction = 0xCF42004D;
|
||||
|
||||
for (const u32 *pos = (const u32 *)pkg1; (u8 *)pos < pkg1 + PKG1_MAX_SIZE; pos += tsec_fw_align / sizeof(u32))
|
||||
if (*pos == tsec_fw_first_instruction)
|
||||
return (u8 *)pos;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE u32 _get_tsec_fw_size(tsec_key_data_t *key_data) {
|
||||
return key_data->blob0_size + sizeof(tsec_key_data_t) + key_data->blob1_size + key_data->blob2_size + key_data->blob3_size + key_data->blob4_size;
|
||||
}
|
||||
|
||||
static void _generate_kek(u32 ks, const void *key_source, void *master_key, const void *kek_seed, const void *key_seed) {
|
||||
if (!_key_exists(key_source) || !_key_exists(master_key) || !_key_exists(kek_seed))
|
||||
return;
|
||||
|
||||
se_aes_key_set(ks, master_key, AES_128_KEY_SIZE);
|
||||
se_aes_unwrap_key(ks, ks, kek_seed);
|
||||
se_aes_unwrap_key(ks, ks, key_source);
|
||||
if (key_seed && _key_exists(key_seed))
|
||||
se_aes_unwrap_key(ks, ks, key_seed);
|
||||
}
|
||||
|
||||
static void _get_device_key(u32 ks, void *out_device_key, u32 revision, const void *device_key, const void *new_device_key, const void *master_key) {
|
||||
if (revision == KB_FIRMWARE_VERSION_100_200 && !h_cfg.t210b01) {
|
||||
memcpy(out_device_key, device_key, AES_128_KEY_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (revision >= KB_FIRMWARE_VERSION_400) {
|
||||
revision -= KB_FIRMWARE_VERSION_400;
|
||||
} else {
|
||||
revision = 0;
|
||||
}
|
||||
u32 temp_key[AES_128_KEY_SIZE / 4] = {0};
|
||||
se_aes_key_set(ks, new_device_key, AES_128_KEY_SIZE);
|
||||
se_aes_crypt_ecb(ks, 0, temp_key, AES_128_KEY_SIZE, device_master_key_source_sources[revision], AES_128_KEY_SIZE);
|
||||
se_aes_key_set(ks, master_key, AES_128_KEY_SIZE);
|
||||
se_aes_unwrap_key(ks, ks, device_master_kek_sources[revision]);
|
||||
se_aes_crypt_ecb(ks, 0, out_device_key, AES_128_KEY_SIZE, temp_key, AES_128_KEY_SIZE);
|
||||
}
|
||||
|
||||
static void _derive_misc_keys(key_derivation_ctx_t *keys) {
|
||||
if (_key_exists(keys->master_key)) {
|
||||
_generate_kek(8, header_kek_source, keys->master_key, aes_kek_generation_source, aes_key_generation_source);
|
||||
se_aes_crypt_block_ecb(8, 0, keys->header_key + 0x00, header_key_source + 0x00);
|
||||
se_aes_crypt_block_ecb(8, 0, keys->header_key + 0x10, header_key_source + 0x10);
|
||||
}
|
||||
|
||||
if (_key_exists(keys->device_key) || (_key_exists(keys->master_key) && _key_exists(keys->device_key_4x))) {
|
||||
_get_device_key(8, keys->temp_key, 0, keys->device_key, keys->device_key_4x, keys->master_key);
|
||||
_generate_kek(8, save_mac_kek_source, keys->temp_key, aes_kek_generation_source, NULL);
|
||||
se_aes_crypt_block_ecb(8, 0, keys->save_mac_key, save_mac_key_source);
|
||||
}
|
||||
}
|
||||
|
||||
static void _derive_bis_keys(key_derivation_ctx_t *keys) {
|
||||
/* key = unwrap(source, wrapped_key):
|
||||
key_set(ks, wrapped_key), block_ecb(ks, 0, key, source) -> final key in key
|
||||
*/
|
||||
|
||||
u32 key_generation = fuse_read_odm_keygen_rev();
|
||||
if (key_generation)
|
||||
key_generation--;
|
||||
|
||||
if (!(_key_exists(keys->device_key) || (key_generation && _key_exists(keys->master_key) && _key_exists(keys->device_key_4x)))) {
|
||||
return;
|
||||
}
|
||||
_get_device_key(8, keys->temp_key, key_generation, keys->device_key, keys->device_key_4x, keys->master_key);
|
||||
se_aes_key_set(8, keys->temp_key, AES_128_KEY_SIZE);
|
||||
se_aes_unwrap_key(8, 8, retail_specific_aes_key_source); // kek = unwrap(rsaks, devkey)
|
||||
se_aes_crypt_block_ecb(8, 0, keys->bis_key[0] + 0x00, bis_key_source[0] + 0x00); // bkey = unwrap(bkeys, kek)
|
||||
se_aes_crypt_block_ecb(8, 0, keys->bis_key[0] + 0x10, bis_key_source[0] + 0x10);
|
||||
// kek = generate_kek(bkeks, devkey, aeskek, aeskey)
|
||||
_generate_kek(8, bis_kek_source, keys->temp_key, aes_kek_generation_source, aes_key_generation_source);
|
||||
se_aes_crypt_block_ecb(8, 0, keys->bis_key[1] + 0x00, bis_key_source[1] + 0x00); // bkey = unwrap(bkeys, kek)
|
||||
se_aes_crypt_block_ecb(8, 0, keys->bis_key[1] + 0x10, bis_key_source[1] + 0x10);
|
||||
se_aes_crypt_block_ecb(8, 0, keys->bis_key[2] + 0x00, bis_key_source[2] + 0x00);
|
||||
se_aes_crypt_block_ecb(8, 0, keys->bis_key[2] + 0x10, bis_key_source[2] + 0x10);
|
||||
}
|
||||
|
||||
static int _derive_master_keys_from_keyblobs(key_derivation_ctx_t *keys) {
|
||||
u8 *keyblob_block = (u8 *)calloc(KB_FIRMWARE_VERSION_600 + 1, NX_EMMC_BLOCKSIZE);
|
||||
encrypted_keyblob_t *current_keyblob = (encrypted_keyblob_t *)keyblob_block;
|
||||
u32 keyblob_mac[AES_128_KEY_SIZE / 4] = {0};
|
||||
|
||||
keys->sbk[0] = FUSE(FUSE_PRIVATE_KEY0);
|
||||
keys->sbk[1] = FUSE(FUSE_PRIVATE_KEY1);
|
||||
keys->sbk[2] = FUSE(FUSE_PRIVATE_KEY2);
|
||||
keys->sbk[3] = FUSE(FUSE_PRIVATE_KEY3);
|
||||
|
||||
if (keys->sbk[0] == 0xFFFFFFFF) {
|
||||
u8 *aes_keys = (u8 *)calloc(0x1000, 1);
|
||||
se_get_aes_keys(aes_keys + 0x800, aes_keys, AES_128_KEY_SIZE);
|
||||
memcpy(keys->sbk, aes_keys + 14 * AES_128_KEY_SIZE, AES_128_KEY_SIZE);
|
||||
free(aes_keys);
|
||||
}
|
||||
|
||||
se_aes_key_set(8, keys->tsec_keys, sizeof(keys->tsec_keys) / 2);
|
||||
se_aes_key_set(9, keys->sbk, 0x10);
|
||||
|
||||
if (!emummc_storage_read(&emmc_storage, KEYBLOB_OFFSET / NX_EMMC_BLOCKSIZE, KB_FIRMWARE_VERSION_600 + 1, keyblob_block)) {
|
||||
DPRINTF("Unable to read keyblobs.");
|
||||
}
|
||||
|
||||
se_aes_crypt_block_ecb(8, 0, keys->keyblob_key, keyblob_key_source); // temp = unwrap(kbks, tsec)
|
||||
se_aes_crypt_block_ecb(9, 0, keys->keyblob_key, keys->keyblob_key); // kbk = unwrap(temp, sbk)
|
||||
se_aes_key_set(7, keys->keyblob_key, sizeof(keys->keyblob_key));
|
||||
se_aes_crypt_block_ecb(7, 0, keys->keyblob_mac_key, keyblob_mac_key_source); // kbm = unwrap(kbms, kbk)
|
||||
se_aes_crypt_block_ecb(7, 0, keys->device_key, per_console_key_source); // devkey = unwrap(pcks, kbk0)
|
||||
se_aes_crypt_block_ecb(7, 0, keys->device_key_4x, device_master_key_source_kek_source);
|
||||
|
||||
se_aes_key_set(10, keys->keyblob_mac_key, sizeof(keys->keyblob_mac_key));
|
||||
se_aes_cmac(10, keyblob_mac, sizeof(keyblob_mac), current_keyblob->iv, sizeof(current_keyblob->iv) + sizeof(keyblob_t));
|
||||
if (memcmp(current_keyblob, keyblob_mac, sizeof(keyblob_mac)) != 0) {
|
||||
//EPRINTFARGS("Keyblob %x corrupt.", 0);
|
||||
free(keyblob_block);
|
||||
return true;
|
||||
}
|
||||
|
||||
se_aes_key_set(6, keys->keyblob_key, sizeof(keys->keyblob_key));
|
||||
se_aes_crypt_ctr(6, &keys->keyblob, sizeof(keyblob_t), ¤t_keyblob->key_data, sizeof(keyblob_t), current_keyblob->iv);
|
||||
|
||||
memcpy(keys->package1_key, keys->keyblob.package1_key, sizeof(keys->package1_key));
|
||||
memcpy(keys->master_kek, keys->keyblob.master_kek, sizeof(keys->master_kek));
|
||||
se_aes_key_set(7, keys->master_kek, sizeof(keys->master_kek));
|
||||
se_aes_crypt_block_ecb(7, 0, keys->master_key, master_key_source);
|
||||
|
||||
free(keyblob_block);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _derive_tsec_keys(tsec_ctxt_t *tsec_ctxt, key_derivation_ctx_t *keys) {
|
||||
tsec_ctxt->fw = _find_tsec_fw(tsec_ctxt->pkg1);
|
||||
if (!tsec_ctxt->fw) {
|
||||
DPRINTF("Unable to locate TSEC firmware.");
|
||||
return false;
|
||||
}
|
||||
|
||||
tsec_ctxt->size = _get_tsec_fw_size((tsec_key_data_t *)(tsec_ctxt->fw + TSEC_KEY_DATA_OFFSET));
|
||||
if (tsec_ctxt->size > PKG1_MAX_SIZE) {
|
||||
DPRINTF("Unexpected TSEC firmware size.");
|
||||
return false;
|
||||
}
|
||||
|
||||
int res = 0;
|
||||
u32 retries = 0;
|
||||
|
||||
mc_disable_ahb_redirect();
|
||||
|
||||
while (tsec_query(keys->tsec_keys, tsec_ctxt) < 0) {
|
||||
memset(keys->tsec_keys, 0, sizeof(keys->tsec_keys));
|
||||
retries++;
|
||||
if (retries > 15) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mc_enable_ahb_redirect(false);
|
||||
|
||||
if (res < 0) {
|
||||
//EPRINTFARGS("ERROR %x dumping TSEC.\n", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE u8 *_read_pkg1() {
|
||||
|
||||
/*
|
||||
if (emummc_storage_init_mmc(&emmc_storage, &emmc_sdmmc)) {
|
||||
DPRINTF("Unable to init MMC.");
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
if (connectMMC(MMC_CONN_EMMC))
|
||||
return NULL;
|
||||
|
||||
// Read package1.
|
||||
u8 *pkg1 = (u8 *)malloc(PKG1_MAX_SIZE);
|
||||
if (!emummc_storage_set_mmc_partition(&emmc_storage, EMMC_BOOT0)) {
|
||||
DPRINTF("Unable to set partition.");
|
||||
return NULL;
|
||||
}
|
||||
if (!emummc_storage_read(&emmc_storage, PKG1_OFFSET / NX_EMMC_BLOCKSIZE, PKG1_MAX_SIZE / NX_EMMC_BLOCKSIZE, pkg1)) {
|
||||
DPRINTF("Unable to read pkg1.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u32 pk1_offset = h_cfg.t210b01 ? sizeof(bl_hdr_t210b01_t) : 0; // Skip T210B01 OEM header.
|
||||
char *pkg1txt = calloc(16, 1);
|
||||
memcpy(pkg1txt, pkg1 + pk1_offset + 0x10, 14);
|
||||
TConf.pkg1ID = pkg1txt;
|
||||
|
||||
return pkg1;
|
||||
}
|
||||
|
||||
key_derivation_ctx_t __attribute__((aligned(4))) dumpedKeys = {0};
|
||||
|
||||
int DumpKeys(){
|
||||
if (h_cfg.t210b01) // i'm not even attempting to dump on mariko
|
||||
return 2;
|
||||
|
||||
u8 *pkg1 = _read_pkg1();
|
||||
if (!pkg1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
|
||||
tsec_ctxt_t tsec_ctxt;
|
||||
tsec_ctxt.pkg1 = pkg1;
|
||||
res =_derive_tsec_keys(&tsec_ctxt, &dumpedKeys);
|
||||
|
||||
free(pkg1);
|
||||
if (res == false) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (_derive_master_keys_from_keyblobs(&dumpedKeys))
|
||||
return 1;
|
||||
_derive_bis_keys(&dumpedKeys);
|
||||
_derive_misc_keys(&dumpedKeys);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PrintKey(u8 *key, u32 len){
|
||||
for (int i = 0; i < len; i++){
|
||||
gfx_printf("%02x", key[i]);
|
||||
}
|
||||
}
|
||||
111
TegraExplorer/source/keys/keys.h
Normal file
111
TegraExplorer/source/keys/keys.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
|
||||
#define HOS_PKG11_MAGIC 0x31314B50
|
||||
#define HOS_EKS_MAGIC 0x30534B45
|
||||
|
||||
#define AES_128_KEY_SIZE 16
|
||||
#define RSA_2048_KEY_SIZE 256
|
||||
|
||||
// only tickets of type Rsa2048Sha256 are expected
|
||||
typedef struct {
|
||||
u32 signature_type; // always 0x10004
|
||||
u8 signature[RSA_2048_KEY_SIZE];
|
||||
u8 sig_padding[0x3C];
|
||||
char issuer[0x40];
|
||||
u8 titlekey_block[RSA_2048_KEY_SIZE];
|
||||
u8 format_version;
|
||||
u8 titlekey_type;
|
||||
u16 ticket_version;
|
||||
u8 license_type;
|
||||
u8 common_key_id;
|
||||
u16 property_mask;
|
||||
u64 reserved;
|
||||
u64 ticket_id;
|
||||
u64 device_id;
|
||||
u8 rights_id[0x10];
|
||||
u32 account_id;
|
||||
u32 sect_total_size;
|
||||
u32 sect_hdr_offset;
|
||||
u16 sect_hdr_count;
|
||||
u16 sect_hdr_entry_size;
|
||||
u8 padding[0x140];
|
||||
} ticket_t;
|
||||
|
||||
typedef struct {
|
||||
u8 rights_id[0x10];
|
||||
u64 ticket_id;
|
||||
u32 account_id;
|
||||
u16 property_mask;
|
||||
u16 reserved;
|
||||
} ticket_record_t;
|
||||
|
||||
typedef struct {
|
||||
u8 read_buffer[0x40000];
|
||||
u8 rights_ids[0x40000 / 0x10][0x10];
|
||||
u8 titlekeys[0x40000 / 0x10][0x10];
|
||||
} titlekey_buffer_t;
|
||||
|
||||
typedef struct {
|
||||
u8 private_exponent[RSA_2048_KEY_SIZE];
|
||||
u8 modulus[RSA_2048_KEY_SIZE];
|
||||
u8 public_exponent[4];
|
||||
u8 reserved[0x14];
|
||||
u64 device_id;
|
||||
u8 gmac[0x10];
|
||||
} rsa_keypair_t;
|
||||
|
||||
typedef struct {
|
||||
u8 master_kek[AES_128_KEY_SIZE];
|
||||
u8 data[0x70];
|
||||
u8 package1_key[AES_128_KEY_SIZE];
|
||||
} keyblob_t;
|
||||
|
||||
typedef struct {
|
||||
u8 cmac[0x10];
|
||||
u8 iv[0x10];
|
||||
keyblob_t key_data;
|
||||
u8 unused[0x150];
|
||||
} encrypted_keyblob_t;
|
||||
|
||||
typedef struct {
|
||||
u8 temp_key[AES_128_KEY_SIZE],
|
||||
bis_key[3][AES_128_KEY_SIZE * 2],
|
||||
device_key[AES_128_KEY_SIZE],
|
||||
device_key_4x[AES_128_KEY_SIZE],
|
||||
// FS-related keys
|
||||
header_key[AES_128_KEY_SIZE * 2],
|
||||
save_mac_key[AES_128_KEY_SIZE],
|
||||
// keyblob-derived families
|
||||
keyblob_key[AES_128_KEY_SIZE],
|
||||
keyblob_mac_key[AES_128_KEY_SIZE],
|
||||
package1_key[AES_128_KEY_SIZE],
|
||||
// master key-derived families,
|
||||
master_kek[AES_128_KEY_SIZE],
|
||||
master_key[AES_128_KEY_SIZE],
|
||||
tsec_keys[AES_128_KEY_SIZE * 2];
|
||||
u32 sbk[4];
|
||||
keyblob_t keyblob;
|
||||
} key_derivation_ctx_t;
|
||||
|
||||
typedef struct _tsec_key_data_t
|
||||
{
|
||||
u8 debug_key[0x10];
|
||||
u8 blob0_auth_hash[0x10];
|
||||
u8 blob1_auth_hash[0x10];
|
||||
u8 blob2_auth_hash[0x10];
|
||||
u8 blob2_aes_iv[0x10];
|
||||
u8 hovi_eks_seed[0x10];
|
||||
u8 hovi_common_seed[0x10];
|
||||
u32 blob0_size;
|
||||
u32 blob1_size;
|
||||
u32 blob2_size;
|
||||
u32 blob3_size;
|
||||
u32 blob4_size;
|
||||
u8 reserved[0x7C];
|
||||
} tsec_key_data_t;
|
||||
|
||||
int DumpKeys();
|
||||
void PrintKey(u8 *key, u32 len);
|
||||
|
||||
extern key_derivation_ctx_t __attribute__((aligned(4))) dumpedKeys;
|
||||
31
TegraExplorer/source/keys/nca.c
Normal file
31
TegraExplorer/source/keys/nca.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "nca.h"
|
||||
#include "keys.h"
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <sec/se.h>
|
||||
#include <mem/heap.h>
|
||||
|
||||
// Thanks switchbrew https://switchbrew.org/wiki/NCA_Format
|
||||
// This function is hacky, should change it but am lazy
|
||||
int GetNcaType(char *path){
|
||||
FIL fp;
|
||||
u32 read_bytes = 0;
|
||||
|
||||
if (f_open(&fp, path, FA_READ | FA_OPEN_EXISTING))
|
||||
return -1;
|
||||
|
||||
u8 *dec_header = (u8*)malloc(0x400);
|
||||
|
||||
if (f_lseek(&fp, 0x200) || f_read(&fp, dec_header, 32, &read_bytes) || read_bytes != 32){
|
||||
f_close(&fp);
|
||||
free(dec_header);
|
||||
return -1;
|
||||
}
|
||||
|
||||
se_aes_xts_crypt(7,6,0,1, dec_header + 0x200, dec_header, 32, 1);
|
||||
|
||||
u8 ContentType = dec_header[0x205];
|
||||
|
||||
f_close(&fp);
|
||||
free(dec_header);
|
||||
return ContentType;
|
||||
}
|
||||
12
TegraExplorer/source/keys/nca.h
Normal file
12
TegraExplorer/source/keys/nca.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
enum NcaTypes {
|
||||
Porgram = 0,
|
||||
Meta,
|
||||
Control,
|
||||
Manual,
|
||||
Data,
|
||||
PublicData
|
||||
};
|
||||
|
||||
int GetNcaType(char *path);
|
||||
51
TegraExplorer/source/keys/save.c
Normal file
51
TegraExplorer/source/keys/save.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "save.h"
|
||||
#include <utils/types.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <sec/se.h>
|
||||
#include <mem/heap.h>
|
||||
#include "../err.h"
|
||||
|
||||
ErrCode_t saveCommit(const char *path){
|
||||
FIL file;
|
||||
int res;
|
||||
u8 *buf, hash[0x20], *cmac_data, cmac[0x10];
|
||||
const u32 hashed_data_size = 0x3D00, cmac_data_size = 0x200;
|
||||
|
||||
buf = malloc(hashed_data_size);
|
||||
cmac_data = malloc(cmac_data_size);
|
||||
|
||||
if ((res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ | FA_WRITE))){
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
f_lseek(&file, 0x300);
|
||||
if ((res = f_read(&file, buf, hashed_data_size, NULL))){
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
se_calc_sha256_oneshot(hash, buf, hashed_data_size);
|
||||
|
||||
f_lseek(&file, 0x108);
|
||||
if ((res = f_write(&file, hash, sizeof(hash), NULL))){
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
f_lseek(&file, 0x100);
|
||||
if ((res = f_read(&file, cmac_data, cmac_data_size, NULL))){
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
se_aes_cmac(8, cmac, 0x10, cmac_data, cmac_data_size);
|
||||
|
||||
f_lseek(&file, 0);
|
||||
|
||||
if ((res = f_write(&file, cmac, sizeof(cmac), NULL))){
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
out_free:;
|
||||
free(buf);
|
||||
free(cmac_data);
|
||||
f_close(&file);
|
||||
return newErrCode(res);
|
||||
}
|
||||
4
TegraExplorer/source/keys/save.h
Normal file
4
TegraExplorer/source/keys/save.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "../err.h"
|
||||
|
||||
ErrCode_t saveCommit(const char *path);
|
||||
136
TegraExplorer/source/libs/fatfs/diskio.c
Normal file
136
TegraExplorer/source/libs/fatfs/diskio.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 shchmue
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libs/fatfs/diskio.h> /* FatFs lower layer API */
|
||||
#include <memory_map.h>
|
||||
#include <storage/nx_sd.h>
|
||||
#include "../../storage/nx_emmc_bis.h"
|
||||
#include <storage/sdmmc.h>
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
DSTATUS disk_status (
|
||||
BYTE pdrv /* Physical drive number to identify the drive */
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Inidialize a Drive */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
DSTATUS disk_initialize (
|
||||
BYTE pdrv /* Physical drive number to identify the drive */
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
DRESULT disk_read (
|
||||
BYTE pdrv, /* Physical drive number to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
DWORD sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
switch (pdrv)
|
||||
{
|
||||
case DRIVE_SD:
|
||||
return sdmmc_storage_read(&sd_storage, sector, count, buff) ? RES_OK : RES_ERROR;
|
||||
|
||||
case DRIVE_BIS:
|
||||
return nx_emmc_bis_read(sector, count, buff);
|
||||
}
|
||||
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
DRESULT disk_write (
|
||||
BYTE pdrv, /* Physical drive number to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
DWORD sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
switch (pdrv)
|
||||
{
|
||||
case DRIVE_SD:
|
||||
return sdmmc_storage_write(&sd_storage, sector, count, (void *)buff) ? RES_OK : RES_ERROR;
|
||||
|
||||
case DRIVE_BIS:
|
||||
return nx_emmc_bis_write(sector, count, (void *)buff);
|
||||
}
|
||||
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
static u32 part_rsvd_size = 0;
|
||||
DRESULT disk_ioctl (
|
||||
BYTE pdrv, /* Physical drive nmuber (0..) */
|
||||
BYTE cmd, /* Control code */
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
DWORD *buf = (DWORD *)buff;
|
||||
|
||||
if (pdrv == DRIVE_SD)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case GET_SECTOR_COUNT:
|
||||
*buf = sd_storage.sec_cnt - part_rsvd_size;
|
||||
break;
|
||||
case GET_BLOCK_SIZE:
|
||||
*buf = 32768; // Align to 16MB.
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (pdrv == DRIVE_RAM)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case GET_SECTOR_COUNT:
|
||||
*buf = RAM_DISK_SZ >> 9; // 1GB.
|
||||
break;
|
||||
case GET_BLOCK_SIZE:
|
||||
*buf = 2048; // Align to 1MB.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
294
TegraExplorer/source/libs/fatfs/ffconf.h
Normal file
294
TegraExplorer/source/libs/fatfs/ffconf.h
Normal file
@@ -0,0 +1,294 @@
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs Functional Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 86604 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_READONLY 0
|
||||
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||
/ and optional writing functions as well. */
|
||||
|
||||
|
||||
#define FF_FS_MINIMIZE 0
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: Basic functions are fully enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
|
||||
/ are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
|
||||
#define FF_USE_STRFUNC 2
|
||||
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
|
||||
/
|
||||
/ 0: Disable string functions.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define FF_USE_FIND 1
|
||||
/* This option switches filtered directory read functions, f_findfirst() and
|
||||
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
||||
|
||||
|
||||
#define FF_USE_MKFS 1
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_FASTFS 0
|
||||
|
||||
#if FF_FASTFS
|
||||
#define FF_USE_FASTSEEK 1
|
||||
#else
|
||||
#define FF_USE_FASTSEEK 0
|
||||
#endif
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_EXPAND 0
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_CHMOD 1
|
||||
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||
|
||||
|
||||
#define FF_USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_CODE_PAGE 850
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect code page setting can cause a file open failure.
|
||||
/
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
/ 771 - KBL
|
||||
/ 775 - Baltic
|
||||
/ 850 - Latin 1
|
||||
/ 852 - Latin 2
|
||||
/ 855 - Cyrillic
|
||||
/ 857 - Turkish
|
||||
/ 860 - Portuguese
|
||||
/ 861 - Icelandic
|
||||
/ 862 - Hebrew
|
||||
/ 863 - Canadian French
|
||||
/ 864 - Arabic
|
||||
/ 865 - Nordic
|
||||
/ 866 - Russian
|
||||
/ 869 - Greek 2
|
||||
/ 932 - Japanese (DBCS)
|
||||
/ 936 - Simplified Chinese (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese (DBCS)
|
||||
/ 0 - Include all code pages above and configured by f_setcp()
|
||||
*/
|
||||
|
||||
|
||||
#define FF_USE_LFN 3
|
||||
#define FF_MAX_LFN 255
|
||||
/* The FF_USE_LFN switches the support for LFN (long file name).
|
||||
/
|
||||
/ 0: Disable LFN. FF_MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
|
||||
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
|
||||
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
|
||||
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
|
||||
/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN
|
||||
/ specification.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree() in ffsystem.c, need to be added to the project. */
|
||||
|
||||
|
||||
#define FF_LFN_UNICODE 0
|
||||
/* This option switches the character encoding on the API when LFN is enabled.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP (TCHAR = char)
|
||||
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
|
||||
/ 2: Unicode in UTF-8 (TCHAR = char)
|
||||
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
|
||||
/
|
||||
/ Also behavior of string I/O functions will be affected by this option.
|
||||
/ When LFN is not enabled, this option has no effect. */
|
||||
|
||||
|
||||
#define FF_LFN_BUF 255
|
||||
#define FF_SFN_BUF 12
|
||||
/* This set of options defines size of file name members in the FILINFO structure
|
||||
/ which is used to read out directory items. These values should be suffcient for
|
||||
/ the file names to read. The maximum possible length of the read file name depends
|
||||
/ on character encoding. When LFN is not enabled, these options have no effect. */
|
||||
|
||||
|
||||
#define FF_STRF_ENCODE 0
|
||||
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
|
||||
/ f_putc(), f_puts and f_printf() convert the character encoding in it.
|
||||
/ This option selects assumption of character encoding ON THE FILE to be
|
||||
/ read/written via those functions.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP
|
||||
/ 1: Unicode in UTF-16LE
|
||||
/ 2: Unicode in UTF-16BE
|
||||
/ 3: Unicode in UTF-8
|
||||
*/
|
||||
|
||||
|
||||
#define FF_FS_RPATH 0
|
||||
/* This option configures support for relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_VOLUMES 4
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
|
||||
#define FF_STR_VOLUME_ID 1
|
||||
// Order is important. Any change to order, must also be reflected to diskio drive enum.
|
||||
#define FF_VOLUME_STRS "sd","ram","emmc","bis"
|
||||
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
|
||||
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
|
||||
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
|
||||
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
|
||||
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
|
||||
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
|
||||
/ not defined, a user defined volume string table needs to be defined as:
|
||||
/
|
||||
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
|
||||
*/
|
||||
|
||||
|
||||
#define FF_MULTI_PARTITION 0
|
||||
/* This option switches support for multiple volumes on the physical drive.
|
||||
/ By default (0), each logical drive number is bound to the same physical drive
|
||||
/ number and only an FAT volume found on the physical drive will be mounted.
|
||||
/ When this function is enabled (1), each logical drive number can be bound to
|
||||
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
|
||||
/ funciton will be available. */
|
||||
|
||||
|
||||
#define FF_MIN_SS 512
|
||||
#define FF_MAX_SS 512
|
||||
/* This set of options configures the range of sector size to be supported. (512,
|
||||
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
|
||||
/ for variable sector size mode and disk_ioctl() function needs to implement
|
||||
/ GET_SECTOR_SIZE command. */
|
||||
|
||||
|
||||
#define FF_USE_TRIM 0
|
||||
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
|
||||
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define FF_FS_NOFSINFO 1
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
|
||||
|
||||
|
||||
#define FF_FS_EXFAT 1
|
||||
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
|
||||
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
|
||||
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||
|
||||
|
||||
#define FF_FS_NORTC 1
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2020
|
||||
/* The option FF_FS_NORTC switches timestamp function. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
|
||||
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
|
||||
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
|
||||
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
|
||||
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
|
||||
/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */
|
||||
|
||||
|
||||
#define FF_FS_LOCK 0
|
||||
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock function. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock function. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ lock control is independent of re-entrancy. */
|
||||
|
||||
|
||||
/* #include <somertos.h> // O/S definitions */
|
||||
#define FF_FS_REENTRANT 0
|
||||
#define FF_FS_TIMEOUT 1000
|
||||
#define FF_SYNC_t HANDLE
|
||||
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this function.
|
||||
/
|
||||
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||
/ function, must be added to the project. Samples are available in
|
||||
/ option/syscall.c.
|
||||
/
|
||||
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.h. */
|
||||
|
||||
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
39
TegraExplorer/source/libs/fatfs/ffsystem.c
Normal file
39
TegraExplorer/source/libs/fatfs/ffsystem.c
Normal file
@@ -0,0 +1,39 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Sample Code of OS Dependent Functions for FatFs */
|
||||
/* (C) ChaN, 2018 */
|
||||
/* (C) CTCaer, 2018 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <mem/heap.h>
|
||||
|
||||
|
||||
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Allocate a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
|
||||
UINT msize /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
return malloc(msize); /* Allocate a new memory block with POSIX API */
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Free a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void ff_memfree (
|
||||
void* mblock /* Pointer to the memory block to free (nothing to do if null) */
|
||||
)
|
||||
{
|
||||
free(mblock); /* Free the memory block with POSIX API */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
25
TegraExplorer/source/link.ld
Normal file
25
TegraExplorer/source/link.ld
Normal file
@@ -0,0 +1,25 @@
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
PROVIDE(__ipl_start = IPL_LOAD_ADDR);
|
||||
. = __ipl_start;
|
||||
.text : {
|
||||
*(.text._start);
|
||||
KEEP(*(._boot_cfg));
|
||||
KEEP(*(._ipl_version));
|
||||
*(.text._irq_setup);
|
||||
*(.text*);
|
||||
}
|
||||
.data : {
|
||||
*(.data*);
|
||||
*(.rodata*);
|
||||
}
|
||||
. = ALIGN(0x10);
|
||||
__ipl_end = .;
|
||||
.bss : {
|
||||
__bss_start = .;
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
__bss_end = .;
|
||||
}
|
||||
}
|
||||
306
TegraExplorer/source/main.c
Normal file
306
TegraExplorer/source/main.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
*
|
||||
* Copyright (c) 2018-2020 CTCaer
|
||||
* Copyright (c) 2019-2020 shchmue
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include <display/di.h>
|
||||
#include <gfx_utils.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <mem/heap.h>
|
||||
#include <mem/minerva.h>
|
||||
#include <power/bq24193.h>
|
||||
#include <power/max17050.h>
|
||||
#include <power/max77620.h>
|
||||
#include <rtc/max77620-rtc.h>
|
||||
#include <soc/bpmp.h>
|
||||
#include <soc/hw_init.h>
|
||||
#include "storage/emummc.h"
|
||||
#include "storage/nx_emmc.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include <storage/sdmmc.h>
|
||||
#include <utils/btn.h>
|
||||
#include <utils/dirlist.h>
|
||||
#include <utils/ini.h>
|
||||
#include <utils/sprintf.h>
|
||||
#include <utils/util.h>
|
||||
#include "hid/hid.h"
|
||||
#include <input/joycon.h>
|
||||
#include "gfx/menu.h"
|
||||
#include "utils/vector.h"
|
||||
#include "gfx/gfxutils.h"
|
||||
#include "tegraexplorer/mainmenu.h"
|
||||
#include "tegraexplorer/tconf.h"
|
||||
#include "err.h"
|
||||
#include <soc/pmc.h>
|
||||
#include "keys/keys.h"
|
||||
#include "keys/keyfile.h"
|
||||
#include "storage/mountmanager.h"
|
||||
#include "fs/fsutils.h"
|
||||
#include "fs/fstypes.h"
|
||||
#include "fs/menus/filemenu.h"
|
||||
|
||||
|
||||
hekate_config h_cfg;
|
||||
boot_cfg_t __attribute__((section ("._boot_cfg"))) b_cfg;
|
||||
|
||||
volatile nyx_storage_t *nyx_str = (nyx_storage_t *)NYX_STORAGE_ADDR;
|
||||
|
||||
// This is a safe and unused DRAM region for our payloads.
|
||||
#define RELOC_META_OFF 0x7C
|
||||
#define PATCHED_RELOC_SZ 0x94
|
||||
#define PATCHED_RELOC_STACK 0x40007000
|
||||
#define PATCHED_RELOC_ENTRY 0x40010000
|
||||
#define EXT_PAYLOAD_ADDR 0xC0000000
|
||||
#define RCM_PAYLOAD_ADDR (EXT_PAYLOAD_ADDR + ALIGN(PATCHED_RELOC_SZ, 0x10))
|
||||
#define COREBOOT_END_ADDR 0xD0000000
|
||||
#define CBFS_DRAM_EN_ADDR 0x4003e000
|
||||
#define CBFS_DRAM_MAGIC 0x4452414D // "DRAM"
|
||||
|
||||
static void *coreboot_addr;
|
||||
|
||||
void reloc_patcher(u32 payload_dst, u32 payload_src, u32 payload_size)
|
||||
{
|
||||
memcpy((u8 *)payload_src, (u8 *)IPL_LOAD_ADDR, PATCHED_RELOC_SZ);
|
||||
|
||||
volatile reloc_meta_t *relocator = (reloc_meta_t *)(payload_src + RELOC_META_OFF);
|
||||
|
||||
relocator->start = payload_dst - ALIGN(PATCHED_RELOC_SZ, 0x10);
|
||||
relocator->stack = PATCHED_RELOC_STACK;
|
||||
relocator->end = payload_dst + payload_size;
|
||||
relocator->ep = payload_dst;
|
||||
|
||||
if (payload_size == 0x7000)
|
||||
{
|
||||
memcpy((u8 *)(payload_src + ALIGN(PATCHED_RELOC_SZ, 0x10)), coreboot_addr, 0x7000); //Bootblock
|
||||
*(vu32 *)CBFS_DRAM_EN_ADDR = CBFS_DRAM_MAGIC;
|
||||
}
|
||||
}
|
||||
|
||||
int launch_payload(char *path)
|
||||
{
|
||||
gfx_clear_grey(0x1B);
|
||||
gfx_con_setpos(0, 0);
|
||||
if (!path)
|
||||
return 1;
|
||||
|
||||
if (sd_mount())
|
||||
{
|
||||
FIL fp;
|
||||
if (f_open(&fp, path, FA_READ))
|
||||
{
|
||||
EPRINTFARGS("Payload file is missing!\n(%s)", path);
|
||||
sd_unmount();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read and copy the payload to our chosen address
|
||||
void *buf;
|
||||
u32 size = f_size(&fp);
|
||||
|
||||
if (size < 0x30000)
|
||||
buf = (void *)RCM_PAYLOAD_ADDR;
|
||||
else
|
||||
{
|
||||
coreboot_addr = (void *)(COREBOOT_END_ADDR - size);
|
||||
buf = coreboot_addr;
|
||||
}
|
||||
|
||||
if (f_read(&fp, buf, size, NULL))
|
||||
{
|
||||
f_close(&fp);
|
||||
sd_unmount();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
f_close(&fp);
|
||||
|
||||
sd_unmount();
|
||||
|
||||
if (size < 0x30000)
|
||||
{
|
||||
reloc_patcher(PATCHED_RELOC_ENTRY, EXT_PAYLOAD_ADDR, ALIGN(size, 0x10));
|
||||
|
||||
hw_reinit_workaround(false, byte_swap_32(*(u32 *)(buf + size - sizeof(u32))));
|
||||
}
|
||||
else
|
||||
{
|
||||
reloc_patcher(PATCHED_RELOC_ENTRY, EXT_PAYLOAD_ADDR, 0x7000);
|
||||
hw_reinit_workaround(true, 0);
|
||||
}
|
||||
|
||||
// Some cards (Sandisk U1), do not like a fast power cycle. Wait min 100ms.
|
||||
sdmmc_storage_init_wait_sd();
|
||||
|
||||
void (*ext_payload_ptr)() = (void *)EXT_PAYLOAD_ADDR;
|
||||
|
||||
// Launch our payload.
|
||||
(*ext_payload_ptr)();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern void pivot_stack(u32 stack_top);
|
||||
|
||||
#define EXCP_EN_ADDR 0x4003FFFC
|
||||
#define EXCP_MAGIC 0x30505645 // EVP0
|
||||
#define EXCP_TYPE_ADDR 0x4003FFF8
|
||||
#define EXCP_TYPE_RESET 0x545352 // RST
|
||||
#define EXCP_TYPE_UNDEF 0x464455 // UDF
|
||||
#define EXCP_TYPE_PABRT 0x54424150 // PABT
|
||||
#define EXCP_TYPE_DABRT 0x54424144 // DABT
|
||||
#define EXCP_LR_ADDR 0x4003FFF4
|
||||
|
||||
static inline void _show_errors()
|
||||
{
|
||||
u32 *excp_enabled = (u32 *)EXCP_EN_ADDR;
|
||||
u32 *excp_type = (u32 *)EXCP_TYPE_ADDR;
|
||||
u32 *excp_lr = (u32 *)EXCP_LR_ADDR;
|
||||
|
||||
if (*excp_enabled == EXCP_MAGIC)
|
||||
h_cfg.errors |= ERR_EXCEPTION;
|
||||
|
||||
if (h_cfg.errors)
|
||||
{
|
||||
|
||||
|
||||
/*
|
||||
if (h_cfg.errors & ERR_SD_BOOT_EN)
|
||||
WPRINTF("Failed to mount SD!\n");
|
||||
|
||||
if (h_cfg.errors & ERR_LIBSYS_LP0)
|
||||
WPRINTF("Missing LP0 (sleep mode) lib!\n");
|
||||
if (h_cfg.errors & ERR_LIBSYS_MTC)
|
||||
WPRINTF("Missing or old Minerva lib!\n");
|
||||
|
||||
if (h_cfg.errors & (ERR_LIBSYS_LP0 | ERR_LIBSYS_MTC))
|
||||
WPRINTF("\nUpdate bootloader folder!\n\n");
|
||||
*/
|
||||
|
||||
if (h_cfg.errors & ERR_EXCEPTION)
|
||||
{
|
||||
gfx_clearscreen();
|
||||
WPRINTFARGS("LR %08X", *excp_lr);
|
||||
u32 exception = 0;
|
||||
|
||||
switch (*excp_type)
|
||||
{
|
||||
case EXCP_TYPE_RESET:
|
||||
exception = TE_EXCEPTION_RESET;
|
||||
break;
|
||||
case EXCP_TYPE_UNDEF:
|
||||
exception = TE_EXCEPTION_UNDEFINED;
|
||||
break;
|
||||
case EXCP_TYPE_PABRT:
|
||||
exception = TE_EXCEPTION_PREF_ABORT;
|
||||
break;
|
||||
case EXCP_TYPE_DABRT:
|
||||
exception = TE_EXCEPTION_DATA_ABORT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Clear the exception.
|
||||
*excp_enabled = 0;
|
||||
DrawError(newErrCode(exception));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ipl_main()
|
||||
{
|
||||
// Do initial HW configuration. This is compatible with consecutive reruns without a reset.
|
||||
hw_init();
|
||||
|
||||
// Pivot the stack so we have enough space.
|
||||
pivot_stack(IPL_STACK_TOP);
|
||||
|
||||
// Tegra/Horizon configuration goes to 0x80000000+, package2 goes to 0xA9800000, we place our heap in between.
|
||||
heap_init(IPL_HEAP_START);
|
||||
|
||||
#ifdef DEBUG_UART_PORT
|
||||
uart_send(DEBUG_UART_PORT, (u8 *)"hekate: Hello!\r\n", 16);
|
||||
uart_wait_idle(DEBUG_UART_PORT, UART_TX_IDLE);
|
||||
#endif
|
||||
|
||||
// Set bootloader's default configuration.
|
||||
set_default_configuration();
|
||||
|
||||
// Mount SD Card.
|
||||
h_cfg.errors |= !sd_mount() ? ERR_SD_BOOT_EN : 0;
|
||||
|
||||
TConf.minervaEnabled = !minerva_init();
|
||||
TConf.FSBuffSize = (TConf.minervaEnabled) ? 0x800000 : 0x10000;
|
||||
|
||||
if (!TConf.minervaEnabled) //!TODO: Add Tegra210B01 support to minerva.
|
||||
h_cfg.errors |= ERR_LIBSYS_MTC;
|
||||
|
||||
display_init();
|
||||
|
||||
u32 *fb = display_init_framebuffer_pitch();
|
||||
gfx_init_ctxt(fb, 720, 1280, 720);
|
||||
|
||||
gfx_con_init();
|
||||
|
||||
display_backlight_pwm_init();
|
||||
display_backlight_brightness(100, 1000);
|
||||
|
||||
// Overclock BPMP.
|
||||
bpmp_clk_rate_set(BPMP_CLK_DEFAULT_BOOST);
|
||||
minerva_change_freq(FREQ_800);
|
||||
|
||||
emummc_load_cfg();
|
||||
// Ignore whether emummc is enabled.
|
||||
h_cfg.emummc_force_disable = emu_cfg.sector == 0 && !emu_cfg.path;
|
||||
emu_cfg.enabled = !h_cfg.emummc_force_disable;
|
||||
h_cfg.emummc_force_disable = 1;
|
||||
|
||||
TConf.pkg1ID = "Unk";
|
||||
|
||||
hidInit();
|
||||
_show_errors();
|
||||
gfx_clearscreen();
|
||||
|
||||
int res = -1;
|
||||
|
||||
if (btn_read() & BTN_VOL_DOWN || DumpKeys())
|
||||
res = GetKeysFromFile("sd:/switch/prod.keys");
|
||||
|
||||
TConf.keysDumped = (res > 0) ? 0 : 1;
|
||||
|
||||
if (res > 0)
|
||||
DrawError(newErrCode(TE_ERR_KEYDUMP_FAIL));
|
||||
|
||||
if (TConf.keysDumped)
|
||||
SetKeySlots();
|
||||
|
||||
if (res == 0)
|
||||
hidWait();
|
||||
|
||||
if (FileExists("sd:/startup.te"))
|
||||
RunScript("sd:/", newFSEntry("startup.te"));
|
||||
|
||||
EnterMainMenu();
|
||||
|
||||
// Halt BPMP if we managed to get out of execution.
|
||||
while (true)
|
||||
bpmp_halt();
|
||||
}
|
||||
91
TegraExplorer/source/script/ABadIdeaVersion3.c
Normal file
91
TegraExplorer/source/script/ABadIdeaVersion3.c
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifdef WIN32
|
||||
|
||||
#include <stdio.h>
|
||||
#include "compat.h"
|
||||
#include "parser.h"
|
||||
#include "intClass.h"
|
||||
#include "StringClass.h"
|
||||
#include "eval.h"
|
||||
#include "garbageCollector.h"
|
||||
|
||||
// TODO: error handling DONE
|
||||
// TODO: unsolved arrays
|
||||
// TODO: free-ing vars & script at end
|
||||
// TODO: implement functions from tsv2
|
||||
// TODO: add len to string
|
||||
// TODO: clear old int values from array DONE
|
||||
// TODO: int and str should be statically included in OP_t DONE
|
||||
|
||||
char* readFile(char* path) {
|
||||
FILE* fp = fopen(path, "r");
|
||||
if (fp == NULL)
|
||||
return NULL;
|
||||
|
||||
fseek(fp, 0L, SEEK_END);
|
||||
size_t size = ftell(fp);
|
||||
fseek(fp, 0L, SEEK_SET);
|
||||
|
||||
char* ret = calloc(size + 1, 1);
|
||||
fread(ret, size, 1, fp);
|
||||
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
//gfx_printf("Hello world!\n");
|
||||
|
||||
//StartParse("a b c de 0x1 0x10 \"yeet\" + + ");
|
||||
/*
|
||||
Variable_t b = newStringVariable("Hello world\n", 1, 0);
|
||||
callClass("__print__", &b, NULL, NULL);
|
||||
|
||||
Variable_t a = newIntVariable(69, 0);
|
||||
Variable_t c = newIntVariable(1, 0);
|
||||
Variable_t e = newStringVariable("snake", 1, 0);
|
||||
Vector_t fuk = newVec(sizeof(Variable_t), 1);
|
||||
vecAdd(&fuk, c);
|
||||
Variable_t *d = callClass("+", &a, NULL, &fuk);
|
||||
callClass("__print__", d, NULL, NULL);
|
||||
*/
|
||||
/*
|
||||
Vector_t v = newVec(sizeof(int), 4);
|
||||
int a = 69;
|
||||
vecAdd(&v, a);
|
||||
vecAdd(&v, a);
|
||||
vecAdd(&v, a);
|
||||
vecAdd(&v, a);
|
||||
|
||||
vecForEach(int*, b, (&v))
|
||||
printf("%d\n", *b);
|
||||
|
||||
return;
|
||||
*/
|
||||
char* script = readFile("input.te");
|
||||
if (script == NULL)
|
||||
return;
|
||||
|
||||
//parseScript("#REQUIRE VER 3.0.5\nmain = { two = 1 + 1 }");
|
||||
//ParserRet_t ret = parseScript("a.b.c(1){ a.b.c() }");
|
||||
//while (1) {
|
||||
ParserRet_t ret = parseScript(script, strlen(script));
|
||||
|
||||
|
||||
setStaticVars(&ret.staticVarHolder);
|
||||
initRuntimeVars();
|
||||
|
||||
Variable_t* res = eval(ret.main.operations.data, ret.main.operations.count, 1);
|
||||
|
||||
exitRuntimeVars();
|
||||
exitStaticVars(&ret.staticVarHolder);
|
||||
exitFunction(ret.main.operations.data, ret.main.operations.count);
|
||||
vecFree(ret.staticVarHolder);
|
||||
vecFree(ret.main.operations);
|
||||
//}
|
||||
|
||||
|
||||
free(script);
|
||||
gfx_printf("done");
|
||||
}
|
||||
#endif
|
||||
188
TegraExplorer/source/script/ABadIdeaVersion3.vcxproj
Normal file
188
TegraExplorer/source/script/ABadIdeaVersion3.vcxproj
Normal file
@@ -0,0 +1,188 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{b4ea4793-03fc-4c25-b69b-a5fbad2d89d5}</ProjectGuid>
|
||||
<RootNamespace>ABadIdeaVersion3</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<EnableASAN>false</EnableASAN>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ABadIdeaVersion3.c" />
|
||||
<ClCompile Include="arrayClass.c" />
|
||||
<ClCompile Include="arrayReferenceClass.c" />
|
||||
<ClCompile Include="dictionaryClass.c" />
|
||||
<ClCompile Include="else.c" />
|
||||
<ClCompile Include="eval.c" />
|
||||
<ClCompile Include="functionClass.c" />
|
||||
<ClCompile Include="garbageCollector.c" />
|
||||
<ClCompile Include="genericClass.c" />
|
||||
<ClCompile Include="intClass.c" />
|
||||
<ClCompile Include="model.c" />
|
||||
<ClCompile Include="parser.c" />
|
||||
<ClCompile Include="scriptError.c" />
|
||||
<ClCompile Include="standardLibrary.c" />
|
||||
<ClCompile Include="StringClass.c" />
|
||||
<ClCompile Include="unsolvedArrayClass.c" />
|
||||
<ClCompile Include="vector.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="arrayClass.h" />
|
||||
<ClInclude Include="arrayReferenceClass.h" />
|
||||
<ClInclude Include="compat.h" />
|
||||
<ClInclude Include="dictionaryClass.h" />
|
||||
<ClInclude Include="else.h" />
|
||||
<ClInclude Include="eval.h" />
|
||||
<ClInclude Include="functionClass.h" />
|
||||
<ClInclude Include="garbageCollector.h" />
|
||||
<ClInclude Include="genericClass.h" />
|
||||
<ClInclude Include="intClass.h" />
|
||||
<ClInclude Include="model.h" />
|
||||
<ClInclude Include="parser.h" />
|
||||
<ClInclude Include="scriptError.h" />
|
||||
<ClInclude Include="standardLibrary.h" />
|
||||
<ClInclude Include="StringClass.h" />
|
||||
<ClInclude Include="unsolvedArrayClass.h" />
|
||||
<ClInclude Include="vector.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="input.te" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
152
TegraExplorer/source/script/ABadIdeaVersion3.vcxproj.filters
Normal file
152
TegraExplorer/source/script/ABadIdeaVersion3.vcxproj.filters
Normal file
@@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes">
|
||||
<UniqueIdentifier>{c245ac36-a649-432b-a663-98ff9d02d3f4}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\String">
|
||||
<UniqueIdentifier>{e4d90dab-a1e0-4a27-82a1-dc2c6b81f0bd}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Generic">
|
||||
<UniqueIdentifier>{1aacfe02-949c-4394-b408-28d73d702918}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Array">
|
||||
<UniqueIdentifier>{5878f4a8-cac0-40aa-8866-6109bf6688ab}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Dictionary">
|
||||
<UniqueIdentifier>{ec7391f8-d60e-46eb-965e-c2d84d48d784}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Function">
|
||||
<UniqueIdentifier>{015ca379-a813-4d13-a929-3b0efe72bb02}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Int">
|
||||
<UniqueIdentifier>{64738969-63b9-4d0e-85b9-d9a63a7e36b3}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Elseable">
|
||||
<UniqueIdentifier>{ba83a808-23f1-44c6-85e4-d0c6c77200fa}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ABadIdeaVersion3.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vector.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="parser.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="model.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="eval.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="genericClass.c">
|
||||
<Filter>Source Files\Classes\Generic</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="garbageCollector.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="functionClass.c">
|
||||
<Filter>Source Files\Classes\Function</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StringClass.c">
|
||||
<Filter>Source Files\Classes\String</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="unsolvedArrayClass.c">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="arrayClass.c">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="intClass.c">
|
||||
<Filter>Source Files\Classes\Int</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="arrayReferenceClass.c">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="standardLibrary.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="scriptError.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="else.c">
|
||||
<Filter>Source Files\Classes\Elseable</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dictionaryClass.c">
|
||||
<Filter>Source Files\Classes\Dictionary</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="compat.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="model.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vector.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="parser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="eval.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="garbageCollector.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dictionaryClass.h">
|
||||
<Filter>Source Files\Classes\Dictionary</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="arrayClass.h">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="intClass.h">
|
||||
<Filter>Source Files\Classes\Int</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StringClass.h">
|
||||
<Filter>Source Files\Classes\String</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="unsolvedArrayClass.h">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="functionClass.h">
|
||||
<Filter>Source Files\Classes\Function</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="genericClass.h">
|
||||
<Filter>Source Files\Classes\Generic</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="arrayReferenceClass.h">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="standardLibrary.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="scriptError.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="else.h">
|
||||
<Filter>Source Files\Classes\Elseable</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="input.te">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
132
TegraExplorer/source/script/StringClass.c
Normal file
132
TegraExplorer/source/script/StringClass.c
Normal file
@@ -0,0 +1,132 @@
|
||||
#include "StringClass.h"
|
||||
#include "compat.h"
|
||||
#include "intClass.h"
|
||||
#include "scriptError.h"
|
||||
#include "parser.h"
|
||||
#include <string.h>
|
||||
|
||||
char* getStringValue(Variable_t* var) {
|
||||
if (var->variableType != StringClass)
|
||||
return NULL;
|
||||
|
||||
return var->string.value;
|
||||
}
|
||||
|
||||
// Will NOT copy the string, the pointer is taken as-is
|
||||
StringClass_t createStringClass(char* in, u8 free) {
|
||||
StringClass_t a = { 0 };
|
||||
a.free = free;
|
||||
a.value = in;
|
||||
return a;
|
||||
}
|
||||
|
||||
Variable_t newStringVariable(char *x, u8 readOnly, u8 freeOnExit) {
|
||||
Variable_t var = { .variableType = StringClass, .readOnly = readOnly, .string = createStringClass(x, freeOnExit) };
|
||||
return var;
|
||||
}
|
||||
|
||||
ClassFunction(printStringVariable) {
|
||||
if (caller->variableType == StringClass) {
|
||||
StringClass_t* a = &caller->string;
|
||||
gfx_printf("%s", a->value);
|
||||
}
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunction(addStringVariables) {
|
||||
char* s1 = getStringValue(caller);
|
||||
char* s2 = getStringValue(*args);
|
||||
|
||||
char* n = malloc(strlen(s1) + strlen(s2) + 1);
|
||||
strcpy(n, s1);
|
||||
strcat(n, s2);
|
||||
|
||||
return newStringVariablePtr(n, 0, 1);
|
||||
}
|
||||
|
||||
ClassFunction(getStringLength) {
|
||||
char* s1 = getStringValue(caller);
|
||||
return newIntVariablePtr(strlen(s1));
|
||||
}
|
||||
|
||||
ClassFunction(stringBytes) {
|
||||
Variable_t v = { 0 };
|
||||
v.variableType = ByteArrayClass;
|
||||
u32 len = strlen(caller->string.value);
|
||||
v.solvedArray.vector = newVec(1, len);
|
||||
v.solvedArray.vector.count = len;
|
||||
memcpy(v.solvedArray.vector.data, caller->string.value, len);
|
||||
return copyVariableToPtr(v);
|
||||
}
|
||||
|
||||
ClassFunction(stringIndexGet) {
|
||||
u32 len = strlen(caller->string.value);
|
||||
u32 idx = args[0]->integer.value;
|
||||
if (len < idx || idx < 0) {
|
||||
SCRIPT_FATAL_ERR("Index of string out of range");
|
||||
}
|
||||
|
||||
char* a = calloc(1,2);
|
||||
a[0] = caller->string.value[idx];
|
||||
return newStringVariablePtr(a, 0, 0);
|
||||
}
|
||||
|
||||
ClassFunction(stringMinusInt){
|
||||
u32 baseStrLen = strlen(caller->string.value);
|
||||
if (baseStrLen < args[0]->integer.value){
|
||||
SCRIPT_FATAL_ERR("Index of string out of range");
|
||||
}
|
||||
char* newStr = calloc(baseStrLen - args[0]->integer.value + 1, 1);
|
||||
memcpy(newStr, caller->string.value, baseStrLen - args[0]->integer.value);
|
||||
return newStringVariablePtr(newStr, 0, 1);
|
||||
}
|
||||
|
||||
ClassFunction(stringEq){
|
||||
return newIntVariablePtr(!strcmp(caller->string.value, args[0]->string.value));
|
||||
}
|
||||
|
||||
ClassFunction(stringInEq){
|
||||
return newIntVariablePtr(strcmp(caller->string.value, args[0]->string.value));
|
||||
}
|
||||
|
||||
ClassFunction(stringSplit) {
|
||||
int valLen = strlen(args[0]->string.value);
|
||||
|
||||
char* start = caller->string.value;
|
||||
char* find = NULL;
|
||||
Vector_t arr = newVec(sizeof(char**), 1);
|
||||
char* temp;
|
||||
|
||||
while ((find = (strstr(start, args[0]->string.value))) != NULL) {
|
||||
temp = utils_copyStringSize(start, find - start);
|
||||
vecAdd(&arr, temp);
|
||||
|
||||
start = find + valLen;
|
||||
}
|
||||
|
||||
temp = utils_copyStringSize(start, caller->string.value + strlen(caller->string.value) - start);
|
||||
vecAdd(&arr, temp);
|
||||
|
||||
Variable_t a = { .variableType = StringArrayClass, .solvedArray.vector = arr };
|
||||
return copyVariableToPtr(a);
|
||||
}
|
||||
|
||||
u8 strOneIntArg[] = { IntClass };
|
||||
u8 oneStringArg[] = { StringClass };
|
||||
|
||||
ClassFunctionTableEntry_t stringFunctions[] = {
|
||||
{"print", printStringVariable, 0, 0},
|
||||
{"+", addStringVariables, 1, oneStringArg },
|
||||
{"len", getStringLength, 0, 0},
|
||||
{"bytes", stringBytes, 0, 0},
|
||||
{"get", stringIndexGet, 1, strOneIntArg},
|
||||
{"-", stringMinusInt, 1, strOneIntArg},
|
||||
{"==", stringEq, 1, oneStringArg},
|
||||
{"!=", stringInEq, 1, oneStringArg},
|
||||
{"split", stringSplit, 1, oneStringArg},
|
||||
{"/", stringSplit, 1, oneStringArg},
|
||||
};
|
||||
|
||||
Variable_t getStringMember(Variable_t* var, char* memberName) {
|
||||
return getGenericFunctionMember(var, memberName, stringFunctions, ARRAY_SIZE(stringFunctions));
|
||||
}
|
||||
10
TegraExplorer/source/script/StringClass.h
Normal file
10
TegraExplorer/source/script/StringClass.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
|
||||
StringClass_t createStringClass(char* in, u8 free);
|
||||
char* getStringValue(Variable_t* var);
|
||||
|
||||
Variable_t newStringVariable(char *x, u8 readOnly, u8 freeOnExit);
|
||||
#define newStringVariablePtr(x, readOnly, freeOnExit) copyVariableToPtr(newStringVariable(x, readOnly, freeOnExit))
|
||||
Variable_t getStringMember(Variable_t* var, char* memberName);
|
||||
308
TegraExplorer/source/script/arrayClass.c
Normal file
308
TegraExplorer/source/script/arrayClass.c
Normal file
@@ -0,0 +1,308 @@
|
||||
#include "model.h"
|
||||
#include "compat.h"
|
||||
#include "genericClass.h"
|
||||
#include "intClass.h"
|
||||
#include "arrayClass.h"
|
||||
#include "garbageCollector.h"
|
||||
#include "eval.h"
|
||||
#include "scriptError.h"
|
||||
#include "StringClass.h"
|
||||
#include <string.h>
|
||||
|
||||
u8 anotherOneIntArg[] = { IntClass };
|
||||
u8 oneStringoneFunction[] = { StringClass, FunctionClass };
|
||||
u8 doubleInt[] = {IntClass, IntClass};
|
||||
u8 oneIntOneAny[] = { IntClass, VARARGCOUNT };
|
||||
u8 anotherAnotherOneVarArg[] = { VARARGCOUNT };
|
||||
u8 oneByteArrayClass[] = {ByteArrayClass};
|
||||
u8 oneIntArrayClass[] = {IntArrayClass};
|
||||
|
||||
Variable_t arrayClassGetIdx(Variable_t *caller, s64 idx) {
|
||||
if (caller->variableType == IntArrayClass) {
|
||||
s64* arr = caller->solvedArray.vector.data;
|
||||
return newIntVariable(arr[idx]);
|
||||
}
|
||||
else if (caller->variableType == StringArrayClass) {
|
||||
char** arr = caller->solvedArray.vector.data;
|
||||
Variable_t v = newStringVariable(arr[idx], 1, 0);
|
||||
v.readOnly = 1;
|
||||
v.reference = 1;
|
||||
return v;
|
||||
}
|
||||
else if (caller->variableType == ByteArrayClass) {
|
||||
u8* arr = caller->solvedArray.vector.data;
|
||||
return newIntVariable(arr[idx]);
|
||||
}
|
||||
|
||||
return (Variable_t) { 0 };
|
||||
}
|
||||
|
||||
int arrayClassAdd(Variable_t *caller, Variable_t *add){
|
||||
if (caller->variableType == IntArrayClass) {
|
||||
if (add->variableType != IntClass) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
vecAdd(&caller->solvedArray.vector, add->integer.value);
|
||||
}
|
||||
else if (caller->variableType == StringArrayClass) {
|
||||
if (add->variableType != StringClass) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
char* str = CpyStr(add->string.value);
|
||||
vecAdd(&caller->solvedArray.vector, str);
|
||||
}
|
||||
else if (caller->variableType == ByteArrayClass) {
|
||||
if (add->variableType != IntClass) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
u8 val = (u8)(add->integer.value & 0xFF);
|
||||
vecAdd(&caller->solvedArray.vector, val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arrayClassAddRange(Variable_t* caller, Variable_t* add)
|
||||
{
|
||||
// Check if IntArrayClass or StringArrayClass or ByteArrayClass
|
||||
if ((caller->variableType == IntArrayClass) || (caller->variableType == StringArrayClass) || (caller->variableType == ByteArrayClass)) {
|
||||
if (caller->variableType != add->variableType) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (s64 i = 0; i < add->solvedArray.vector.count; i++)
|
||||
{
|
||||
Variable_t v = arrayClassGetIdx(add, i);
|
||||
if (arrayClassAdd(caller, &v))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ClassFunction(getArrayIdx) {
|
||||
s64 getVal = (*args)->integer.value;
|
||||
// Out of bounds
|
||||
if (getVal < 0 || getVal >= caller->solvedArray.vector.count) {
|
||||
SCRIPT_FATAL_ERR("Accessing index %d while array is %d long", (int)getVal, (int)caller->solvedArray.vector.count);
|
||||
}
|
||||
|
||||
Variable_t a = arrayClassGetIdx(caller, getVal);
|
||||
if (a.variableType == None)
|
||||
return NULL;
|
||||
return copyVariableToPtr(a);
|
||||
}
|
||||
|
||||
ClassFunction(getArrayLen) {
|
||||
return newIntVariablePtr(caller->solvedArray.vector.count);
|
||||
}
|
||||
|
||||
ClassFunction(arraySlice) {
|
||||
s64 skipAmount = getIntValue(*args);
|
||||
s64 takeAmount = getIntValue(args[1]);
|
||||
|
||||
if (caller->solvedArray.vector.count < (skipAmount + takeAmount) || skipAmount < 0 || takeAmount <= 0) {
|
||||
SCRIPT_FATAL_ERR("Slicing out of range of array with len %d", (int)caller->solvedArray.vector.count);
|
||||
}
|
||||
|
||||
Variable_t refSkip = { .variableType = SolvedArrayReferenceClass };
|
||||
refSkip.solvedArray.arrayClassReference = caller;
|
||||
refSkip.solvedArray.offset = skipAmount;
|
||||
refSkip.solvedArray.len = takeAmount;
|
||||
addPendingReference(caller);
|
||||
return copyVariableToPtr(refSkip);
|
||||
}
|
||||
|
||||
// TODO: arrayForEach does not like the new garbage collector
|
||||
ClassFunction(arrayForEach) {
|
||||
Vector_t* v = &caller->solvedArray.vector;
|
||||
|
||||
Callback_SetVar_t setVar = { .isTopLevel = 1, .varName = (*args)->string.value };
|
||||
Variable_t* iter = NULL;
|
||||
iter = copyVariableToPtr(newIntVariable(0));
|
||||
iter->gcDoNotFree = 1;
|
||||
runtimeVariableEdit(&setVar, iter);
|
||||
|
||||
for (int i = 0; i < v->count; i++) {
|
||||
*iter = arrayClassGetIdx(caller, i);
|
||||
iter->gcDoNotFree = 1;
|
||||
|
||||
Variable_t* res = genericCallDirect(args[1], NULL, 0);
|
||||
if (res == NULL) {
|
||||
if (scriptLastError == SCRIPT_BREAK) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iter->reference = 1;
|
||||
free(iter);
|
||||
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunction(arrayCopy) {
|
||||
Vector_t* v = &caller->solvedArray.vector;
|
||||
Vector_t copiedArray = vecCopy(v);
|
||||
Variable_t var = { .variableType = caller->variableType, .solvedArray.vector = copiedArray };
|
||||
return copyVariableToPtr(var);
|
||||
}
|
||||
|
||||
ClassFunction(arraySet) {
|
||||
s64 idx = getIntValue(*args);
|
||||
Vector_t* v = &caller->solvedArray.vector;
|
||||
if (v->count < idx || idx < 0) {
|
||||
SCRIPT_FATAL_ERR("Accessing index %d while array is %d long", (int)idx, (int)caller->solvedArray.vector.count);
|
||||
}
|
||||
|
||||
u32 oldCount = caller->solvedArray.vector.count;
|
||||
caller->solvedArray.vector.count = idx;
|
||||
if (arrayClassAdd(caller, args[1])){
|
||||
SCRIPT_FATAL_ERR("Adding the wrong type to a typed array");
|
||||
}
|
||||
caller->solvedArray.vector.count = oldCount;
|
||||
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunction(arrayAdd) {
|
||||
Variable_t* arg = *args;
|
||||
|
||||
if (caller->readOnly) {
|
||||
SCRIPT_FATAL_ERR("Array is read-only");
|
||||
}
|
||||
|
||||
if (arrayClassAdd(caller, arg)){
|
||||
SCRIPT_FATAL_ERR("Adding the wrong type to a typed array");
|
||||
}
|
||||
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunction(arrayAddRange) {
|
||||
Variable_t* arg = *args;
|
||||
|
||||
if (caller->readOnly) {
|
||||
SCRIPT_FATAL_ERR("Array is read-only");
|
||||
}
|
||||
|
||||
if (arrayClassAddRange(caller, arg)) {
|
||||
SCRIPT_FATAL_ERR("Adding the wrong type to a typed array");
|
||||
}
|
||||
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunction(arrayContains) {
|
||||
Vector_t* v = &caller->solvedArray.vector;
|
||||
Variable_t* arg = *args;
|
||||
|
||||
for (int i = 0; i < v->count; i++) {
|
||||
Variable_t iter = arrayClassGetIdx(caller, i);
|
||||
|
||||
if (iter.variableType != arg->variableType){
|
||||
SCRIPT_FATAL_ERR("type of contains does not match");
|
||||
}
|
||||
|
||||
if (caller->variableType == StringArrayClass) {
|
||||
if (!strcmp(arg->string.value, iter.string.value))
|
||||
return newIntVariablePtr(i + 1);
|
||||
}
|
||||
else {
|
||||
if (arg->integer.value == iter.integer.value)
|
||||
return newIntVariablePtr(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return newIntVariablePtr(0);
|
||||
}
|
||||
|
||||
ClassFunction(arrayMinus) {
|
||||
s64 count = getIntValue(*args);
|
||||
Vector_t* v = &caller->solvedArray.vector;
|
||||
if (v->count < count || count <= 0) {
|
||||
SCRIPT_FATAL_ERR("Accessing index %d while array is %d long", (int)count, (int)caller->solvedArray.vector.count);
|
||||
}
|
||||
|
||||
if (caller->variableType == StringArrayClass) {
|
||||
char** arr = v->data;
|
||||
for (int i = v->count - count; i < count; i++) {
|
||||
FREE(arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
v->count -= count;
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunction(bytesToStr) {
|
||||
if (caller->variableType != ByteArrayClass) {
|
||||
SCRIPT_FATAL_ERR("Need a bytearray to convert to str");
|
||||
}
|
||||
|
||||
char* buff = malloc(caller->solvedArray.vector.count + 1);
|
||||
memcpy(buff, caller->solvedArray.vector.data, caller->solvedArray.vector.count);
|
||||
buff[caller->solvedArray.vector.count] = '\0';
|
||||
return newStringVariablePtr(buff, 0, 0);
|
||||
}
|
||||
|
||||
ClassFunction(eqArray){
|
||||
Variable_t *arg = (*args);
|
||||
if (caller->solvedArray.vector.count != arg->solvedArray.vector.count || arg->variableType != caller->variableType){
|
||||
return newIntVariablePtr(0);
|
||||
}
|
||||
|
||||
s64 res = memcmp(caller->solvedArray.vector.data, arg->solvedArray.vector.data, caller->solvedArray.vector.count * caller->solvedArray.vector.elemSz);
|
||||
return newIntVariablePtr(!res);
|
||||
}
|
||||
|
||||
ClassFunction(arrayFind){
|
||||
Variable_t *arg = (*args);
|
||||
if (caller->solvedArray.vector.count <= arg->solvedArray.vector.count || arg->variableType != caller->variableType){
|
||||
return newIntVariablePtr(-1);
|
||||
}
|
||||
|
||||
u8 step = (arg->variableType == ByteArrayClass) ? 1 : 8;
|
||||
char *haystack = caller->solvedArray.vector.data;
|
||||
void *needle = arg->solvedArray.vector.data;
|
||||
|
||||
for (int i = 0; i < caller->solvedArray.vector.count - arg->solvedArray.vector.count; i++){
|
||||
if (!memcmp(haystack + (i * step), needle, step * arg->solvedArray.vector.count)){
|
||||
return newIntVariablePtr(i);
|
||||
}
|
||||
}
|
||||
|
||||
return newIntVariablePtr(-1);
|
||||
}
|
||||
|
||||
ClassFunctionTableEntry_t arrayFunctions[] = {
|
||||
{"get", getArrayIdx, 1, anotherOneIntArg },
|
||||
{"len", getArrayLen, 0, 0},
|
||||
{"slice", arraySlice, 2, doubleInt},
|
||||
{"foreach", arrayForEach, 2, oneStringoneFunction},
|
||||
{"copy", arrayCopy, 0, 0},
|
||||
{"set", arraySet, 2, oneIntOneAny},
|
||||
{"+", arrayAdd, 1, anotherAnotherOneVarArg},
|
||||
{"add", arrayAdd, 1, anotherAnotherOneVarArg},
|
||||
{"addrange", arrayAddRange, 1, anotherAnotherOneVarArg},
|
||||
{"-", arrayMinus, 1, anotherOneIntArg},
|
||||
{"contains", arrayContains, 1, anotherAnotherOneVarArg},
|
||||
{"bytestostr", bytesToStr, 0, 0},
|
||||
{"==", eqArray, 1, oneByteArrayClass},
|
||||
{"==", eqArray, 1, oneIntArrayClass},
|
||||
{"find", arrayFind, 1, oneByteArrayClass},
|
||||
{"find", arrayFind, 1, oneIntArrayClass},
|
||||
};
|
||||
|
||||
Variable_t getArrayMember(Variable_t* var, char* memberName) {
|
||||
return getGenericFunctionMember(var, memberName, arrayFunctions, ARRAY_SIZE(arrayFunctions));
|
||||
}
|
||||
5
TegraExplorer/source/script/arrayClass.h
Normal file
5
TegraExplorer/source/script/arrayClass.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
|
||||
Variable_t getArrayMember(Variable_t* var, char* memberName);
|
||||
24
TegraExplorer/source/script/arrayReferenceClass.c
Normal file
24
TegraExplorer/source/script/arrayReferenceClass.c
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "model.h"
|
||||
#include "compat.h"
|
||||
#include "genericClass.h"
|
||||
#include "intClass.h"
|
||||
#include "arrayReferenceClass.h"
|
||||
|
||||
|
||||
ClassFunction(projectArray) {
|
||||
Variable_t newArray = { .variableType = caller->solvedArray.arrayClassReference->variableType, .reference = 1, .readOnly = 1 };
|
||||
|
||||
newArray.solvedArray.vector = caller->solvedArray.arrayClassReference->solvedArray.vector;
|
||||
newArray.solvedArray.vector.data = (u8*)caller->solvedArray.arrayClassReference->solvedArray.vector.data + (caller->solvedArray.offset * caller->solvedArray.arrayClassReference->solvedArray.vector.elemSz);
|
||||
newArray.solvedArray.vector.count = caller->solvedArray.len;
|
||||
|
||||
return copyVariableToPtr(newArray);
|
||||
}
|
||||
|
||||
ClassFunctionTableEntry_t arrayReferenceFunctions[] = {
|
||||
{"project", projectArray, 0, 0},
|
||||
};
|
||||
|
||||
Variable_t getArrayReferenceMember(Variable_t* var, char* memberName) {
|
||||
return getGenericFunctionMember(var, memberName, arrayReferenceFunctions, ARRAY_SIZE(arrayReferenceFunctions));
|
||||
}
|
||||
5
TegraExplorer/source/script/arrayReferenceClass.h
Normal file
5
TegraExplorer/source/script/arrayReferenceClass.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
|
||||
Variable_t getArrayReferenceMember(Variable_t* var, char* memberName);
|
||||
23
TegraExplorer/source/script/compat.h
Normal file
23
TegraExplorer/source/script/compat.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef WIN32
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#define gfx_printf(str, ...) printf(str, ##__VA_ARGS__)
|
||||
#define gfx_vprintf(str, va) vprintf(str, va);
|
||||
#define gfx_putc(x) gfx_printf("%c", x)
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
|
||||
#define LP_VER_MJ 3
|
||||
#define LP_VER_MN 0
|
||||
#define LP_VER_BF 5
|
||||
#define LP_VER "3.0.5"
|
||||
#define FREE(x) if (x) free(x)
|
||||
#define CpyStr(x) _strdup(x)
|
||||
#include "vector.h"
|
||||
#pragma _CRT_SECURE_NO_WARNINGS
|
||||
#else
|
||||
#include "../gfx/gfx.h"
|
||||
#include <mem/heap.h>
|
||||
#include "../utils/vector.h"
|
||||
#include "../utils/utils.h"
|
||||
#endif
|
||||
61
TegraExplorer/source/script/dictionaryClass.c
Normal file
61
TegraExplorer/source/script/dictionaryClass.c
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "dictionaryClass.h"
|
||||
#include <string.h>
|
||||
#include "garbageCollector.h"
|
||||
#include "intClass.h"
|
||||
|
||||
u8 dictOneStrOneAll[] = { StringClass, VARARGCOUNT };
|
||||
|
||||
void addVariableToDict(Variable_t *dict, char* name, Variable_t *add){
|
||||
Dict_t a = {.name = CpyStr(name), .var = add};
|
||||
vecAdd(&dict->dictionary.vector, a);
|
||||
}
|
||||
|
||||
void addIntToDict(Variable_t *dict, char* name, s64 integer){
|
||||
Variable_t *v = newIntVariablePtr(integer);
|
||||
addVariableToDict(dict, name, v);
|
||||
}
|
||||
|
||||
Dict_t* getEntry(Vector_t *v, char* name) {
|
||||
vecForEach(Dict_t*, dict, v) {
|
||||
if (!strcmp(name, dict->name)) {
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ClassFunction(dictSet) {
|
||||
addPendingReference(args[1]);
|
||||
char* arg = CpyStr(args[0]->string.value);
|
||||
Dict_t* possibleEntry = getEntry(&caller->dictionary.vector, arg);
|
||||
if (possibleEntry == NULL) {
|
||||
Dict_t a = { .name = arg, .var = args[1] };
|
||||
vecAdd(&caller->dictionary.vector, a);
|
||||
}
|
||||
else {
|
||||
possibleEntry->var = args[1];
|
||||
free(arg);
|
||||
}
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunctionTableEntry_t dictFunctions[] = {
|
||||
{"set", dictSet, 2, dictOneStrOneAll},
|
||||
};
|
||||
|
||||
Variable_t getDictMember(Variable_t* var, char* memberName) {
|
||||
if (!strcmp(memberName, "set"))
|
||||
return getGenericFunctionMember(var, memberName, dictFunctions, ARRAY_SIZE(dictFunctions));
|
||||
|
||||
vecForEach(Dict_t*, dict, (&var->dictionary.vector)) {
|
||||
if (!strcmp(dict->name, memberName)) {
|
||||
Variable_t a = { 0 };
|
||||
a.variableType = ReferenceType;
|
||||
a.referenceType = dict->var;
|
||||
addPendingReference(dict->var);
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
return (Variable_t) { 0 };
|
||||
}
|
||||
8
TegraExplorer/source/script/dictionaryClass.h
Normal file
8
TegraExplorer/source/script/dictionaryClass.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
#include "compat.h"
|
||||
|
||||
Variable_t getDictMember(Variable_t* var, char* memberName);
|
||||
void addVariableToDict(Variable_t *dict, char* name, Variable_t *add);
|
||||
void addIntToDict(Variable_t *dict, char* name, s64 integer);
|
||||
22
TegraExplorer/source/script/else.c
Normal file
22
TegraExplorer/source/script/else.c
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "else.h"
|
||||
|
||||
ClassFunction(scriptElse) {
|
||||
if (!caller->integer.value) {
|
||||
Variable_t* res = genericCallDirect(args[0], NULL, 0);
|
||||
if (res == NULL)
|
||||
return NULL;
|
||||
|
||||
removePendingReference(res);
|
||||
}
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
u8 elseOneFunction[] = { FunctionClass };
|
||||
|
||||
ClassFunctionTableEntry_t elseFunctions[] = {
|
||||
{"else", scriptElse, 1, elseOneFunction},
|
||||
};
|
||||
|
||||
Variable_t getElseMember(Variable_t* var, char* memberName) {
|
||||
return getGenericFunctionMember(var, memberName, elseFunctions, ARRAY_SIZE(elseFunctions));
|
||||
}
|
||||
7
TegraExplorer/source/script/else.h
Normal file
7
TegraExplorer/source/script/else.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
#include "compat.h"
|
||||
#include "garbageCollector.h"
|
||||
|
||||
Variable_t getElseMember(Variable_t* var, char* memberName);
|
||||
276
TegraExplorer/source/script/eval.c
Normal file
276
TegraExplorer/source/script/eval.c
Normal file
@@ -0,0 +1,276 @@
|
||||
#include "model.h"
|
||||
#include "compat.h"
|
||||
#include "genericClass.h"
|
||||
#include "eval.h"
|
||||
#include "garbageCollector.h"
|
||||
#include "standardLibrary.h"
|
||||
#include "scriptError.h"
|
||||
#include "StringClass.h"
|
||||
#include "intClass.h"
|
||||
#include "unsolvedArrayClass.h"
|
||||
#include "functionClass.h"
|
||||
#include <string.h>
|
||||
|
||||
Variable_t* staticVars;
|
||||
|
||||
void setStaticVars(Vector_t* vec) {
|
||||
staticVars = vec->data;
|
||||
}
|
||||
|
||||
Vector_t runtimeVars;
|
||||
|
||||
void initRuntimeVars() {
|
||||
runtimeVars = newVec(sizeof(Dict_t), 8);
|
||||
}
|
||||
|
||||
void exitRuntimeVars() {
|
||||
vecForEach(Dict_t*, runtimeVar, (&runtimeVars)) {
|
||||
FREE(runtimeVar->name);
|
||||
removePendingReference(runtimeVar->var);
|
||||
}
|
||||
|
||||
vecFree(runtimeVars);
|
||||
runtimeVars.count = 0;
|
||||
}
|
||||
|
||||
Variable_t* opToVar(Operator_t* op, Callback_SetVar_t *setCallback, u8 possibleCallArg) {
|
||||
Variable_t* var = NULL;
|
||||
CallArgs_t* args = NULL;
|
||||
|
||||
if ((op + 1)->token == CallArgs && possibleCallArg)
|
||||
args = &(op + 1)->callArgs;
|
||||
|
||||
if (op->token == BetweenBrackets) {
|
||||
var = eval(op->variable.betweenBrackets.data, op->variable.betweenBrackets.len, 1);
|
||||
}
|
||||
else if (op->token == Variable) {
|
||||
if (op->variable.staticVariableSet) {
|
||||
if (op->variable.staticVariableRef) {
|
||||
op->variable.staticVariable = &staticVars[(int)op->variable.staticVariable];
|
||||
op->variable.staticVariableRef = 0;
|
||||
op->variable.staticVariable->readOnly = 1;
|
||||
op->variable.staticVariable->reference = 1;
|
||||
op->variable.staticVariable->gcDoNotFree = 1;
|
||||
}
|
||||
|
||||
var = op->variable.staticVariable;
|
||||
|
||||
if (var->variableType == UnresolvedArrayClass) {
|
||||
var = solveArray(var);
|
||||
}
|
||||
}
|
||||
else if (op->variable.staticVariableType > 0) {
|
||||
if (op->variable.staticVariableType == 1) {
|
||||
var = copyVariableToPtr(newIntVariable(op->variable.integerType));
|
||||
}
|
||||
else if (op->variable.staticVariableType == 2) {
|
||||
var = copyVariableToPtr(newStringVariable(op->variable.stringType, 1, 0));
|
||||
var->reference = 1;
|
||||
}
|
||||
else if (op->variable.staticVariableType == 3) {
|
||||
var = copyVariableToPtr(newFunctionVariable(createFunctionClass((Function_t) { 0 }, op->variable.staticFunction)));
|
||||
var->function.len = op->variable.staticFunctionLen;
|
||||
var->reference = 1;
|
||||
|
||||
|
||||
if (!strcmp(var->function.builtInPtr->name,"while")) {
|
||||
var->function.firstArgAsFunction = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (args != NULL) {
|
||||
if (args->action == ActionSet && args->extraAction == ActionExtraNone) {
|
||||
setCallback->isTopLevel = 1;
|
||||
setCallback->varName = op->variable.name;
|
||||
setCallback->hasVarName = 1;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
vecForEach(Dict_t*, variableArrayEntry, (&runtimeVars)) {
|
||||
if (!strcmp(variableArrayEntry->name, op->variable.name)) {
|
||||
var = variableArrayEntry->var;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (var == NULL) {
|
||||
SCRIPT_FATAL_ERR("Variable '%s' not found", op->variable.name);
|
||||
}
|
||||
|
||||
addPendingReference(var);
|
||||
}
|
||||
}
|
||||
|
||||
while (args) {
|
||||
Variable_t* varNext = NULL;
|
||||
if (args->action == ActionGet) {
|
||||
if (args->extraAction == ActionExtraMemberName && args->next != NULL && args->next->action == ActionCall) {
|
||||
varNext = callMemberFunction(var, args->extra, args->next);
|
||||
args = args->next;
|
||||
}
|
||||
else {
|
||||
varNext = genericGet(var, args);
|
||||
}
|
||||
}
|
||||
else if (args->action == ActionSet) {
|
||||
if (var->readOnly) {
|
||||
SCRIPT_FATAL_ERR("Variable which set was called on is read-only");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (args->extraAction == ActionExtraMemberName || args->extraAction == ActionExtraArrayIndex) {
|
||||
setCallback->hasVarName = (args->extraAction == ActionExtraMemberName) ? 1 : 0;
|
||||
setCallback->setVar = var;
|
||||
if (args->extraAction == ActionExtraMemberName) {
|
||||
setCallback->varName = args->extra;
|
||||
}
|
||||
else {
|
||||
setCallback->idxVar = args->extra;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SCRIPT_FATAL_ERR("Unexpected set!");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
else if (args->action == ActionCall) {
|
||||
varNext = genericCall(var, args);
|
||||
}
|
||||
|
||||
if (varNext == NULL)
|
||||
return NULL;
|
||||
|
||||
removePendingReference(var);
|
||||
|
||||
//if (!var->reference)
|
||||
// freeVariable(&var);
|
||||
|
||||
var = varNext;
|
||||
args = args->next;
|
||||
}
|
||||
|
||||
if (op->not && var) {
|
||||
Variable_t* newVar = callMemberFunctionDirect(var, "not", NULL, 0);
|
||||
removePendingReference(var);
|
||||
var = newVar;
|
||||
}
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
void runtimeVariableEdit(Callback_SetVar_t* set, Variable_t* curRes) {
|
||||
if (set->isTopLevel) {
|
||||
vecForEach(Dict_t*, variableArrayEntry, (&runtimeVars)) {
|
||||
if (!strcmp(variableArrayEntry->name, set->varName)) {
|
||||
removePendingReference(variableArrayEntry->var);
|
||||
//addPendingReference(curRes);
|
||||
variableArrayEntry->var = curRes;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Dict_t newStoredVariable = { 0 };
|
||||
newStoredVariable.name = CpyStr(set->varName);
|
||||
newStoredVariable.var = curRes;
|
||||
vecAdd(&runtimeVars, newStoredVariable);
|
||||
return;
|
||||
}
|
||||
|
||||
if (set->idxVar) {
|
||||
Variable_t* var = eval(set->idxVar->operations.data, set->idxVar->operations.count, 1);
|
||||
Variable_t* args[2] = { var, curRes };
|
||||
callMemberFunctionDirect(set->setVar, "set", args, 2);
|
||||
removePendingReference(var);
|
||||
}
|
||||
else {
|
||||
Variable_t varName = { .variableType = StringClass, .reference = 1, .string.value = set->varName };
|
||||
Variable_t* args[2] = { &varName, curRes };
|
||||
callMemberFunctionDirect(set->setVar, "set", args, 2);
|
||||
}
|
||||
|
||||
removePendingReference(curRes);
|
||||
removePendingReference(set->setVar);
|
||||
|
||||
// TODO: add non-top level sets
|
||||
}
|
||||
|
||||
Variable_t* eval(Operator_t* ops, u32 len, u8 ret) {
|
||||
Variable_t* curRes = NULL;
|
||||
Operator_t* curOp = NULL;
|
||||
Callback_SetVar_t set = { 0 };
|
||||
for (u32 i = 0; i < len; i++) {
|
||||
Operator_t* cur = &ops[i];
|
||||
|
||||
if (cur->token == CallArgs)
|
||||
continue;
|
||||
|
||||
if (cur->token == EquationSeperator) {
|
||||
scriptCurrentLine = cur->lineNumber;
|
||||
if (set.hasBeenNoticed == 1)
|
||||
runtimeVariableEdit(&set, curRes);
|
||||
else
|
||||
removePendingReference(curRes);
|
||||
|
||||
memset(&set, 0, sizeof(Callback_SetVar_t));
|
||||
curRes = NULL;
|
||||
curOp = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (curRes == NULL) {
|
||||
if (cur->token != Variable && cur->token != BetweenBrackets) {
|
||||
SCRIPT_FATAL_ERR("First token is not a variable");
|
||||
}
|
||||
else {
|
||||
curRes = opToVar(cur, &set, (len - i) > 1);
|
||||
if (!curRes) {
|
||||
if ((set.varName != NULL || set.idxVar != NULL) && set.hasBeenNoticed == 0) {
|
||||
set.hasBeenNoticed = 1;
|
||||
continue;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (curOp == NULL) {
|
||||
if (cur->token != Variable && cur->token != BetweenBrackets) {
|
||||
curOp = cur;
|
||||
}
|
||||
else {
|
||||
SCRIPT_FATAL_ERR("Expected operator");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Variable_t* rightSide = opToVar(cur, &set, (len - i) > 1);
|
||||
if (!rightSide)
|
||||
return NULL;
|
||||
|
||||
|
||||
// Issue lies here for freeing issues, curRes is corrupted
|
||||
Variable_t* result = callMemberFunctionDirect(curRes, curOp->tokenStr, &rightSide, 1);
|
||||
// Free old values
|
||||
|
||||
removePendingReference(curRes);
|
||||
removePendingReference(rightSide);
|
||||
rightSide = NULL;
|
||||
curOp = NULL;
|
||||
|
||||
curRes = result;
|
||||
}
|
||||
|
||||
if (set.hasBeenNoticed == 1) {
|
||||
runtimeVariableEdit(&set, curRes);
|
||||
return &emptyClass;
|
||||
}
|
||||
else if (!ret) {
|
||||
removePendingReference(curRes);
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
return curRes;
|
||||
}
|
||||
19
TegraExplorer/source/script/eval.h
Normal file
19
TegraExplorer/source/script/eval.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
u8 isTopLevel : 1;
|
||||
u8 hasBeenNoticed : 1;
|
||||
u8 hasVarName : 1;
|
||||
};
|
||||
Variable_t* setVar;
|
||||
char* varName;
|
||||
Function_t* idxVar;
|
||||
} Callback_SetVar_t;
|
||||
|
||||
Variable_t* eval(Operator_t* ops, u32 len, u8 ret);
|
||||
void setStaticVars(Vector_t* vec);
|
||||
void initRuntimeVars();
|
||||
void exitRuntimeVars();
|
||||
void runtimeVariableEdit(Callback_SetVar_t* set, Variable_t* curRes);
|
||||
67
TegraExplorer/source/script/functionClass.c
Normal file
67
TegraExplorer/source/script/functionClass.c
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "functionClass.h"
|
||||
#include "compat.h"
|
||||
#include "model.h"
|
||||
#include <malloc.h>
|
||||
|
||||
Function_t* getFunctionValue(Variable_t* var) {
|
||||
if (var->variableType != FunctionClass)
|
||||
return NULL;
|
||||
|
||||
FunctionClass_t* a = &var->function;
|
||||
|
||||
if (a->builtIn)
|
||||
return NULL;
|
||||
|
||||
return &a->function;
|
||||
}
|
||||
|
||||
Function_t createEmptyFunction() {
|
||||
Function_t a = { 0 };
|
||||
a.operations = newVec(sizeof(Operator_t), 0);
|
||||
return a;
|
||||
}
|
||||
|
||||
// Will NOT copy the Function, the pointer is taken as-is. Set as NULL to make it builtin
|
||||
FunctionClass_t createFunctionClass(Function_t in, ClassFunctionTableEntry_t *builtIn) {
|
||||
FunctionClass_t a = { 0 };
|
||||
if (!builtIn) {
|
||||
a.function = in;
|
||||
}
|
||||
else {
|
||||
a.builtIn = 1;
|
||||
a.builtInPtr = builtIn;
|
||||
a.len = 1;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
FunctionClass_t* creteFunctionClassPtr(Function_t in, ClassFunctionTableEntry_t* builtIn) {
|
||||
FunctionClass_t* a = malloc(sizeof(FunctionClass_t));
|
||||
*a = createFunctionClass(in, builtIn);
|
||||
return a;
|
||||
}
|
||||
|
||||
Function_t* createFunctionPtrFromFunction(Function_t in) {
|
||||
Function_t* f = malloc(sizeof(Function_t));
|
||||
*f = in;
|
||||
return f;
|
||||
}
|
||||
|
||||
// Functions are always readonly
|
||||
Variable_t newFunctionVariable(FunctionClass_t func) {
|
||||
Variable_t var = { .variableType = FunctionClass, .readOnly = 1, .function = func };
|
||||
return var;
|
||||
}
|
||||
|
||||
int countTokens(Function_t *func, u8 toCount) {
|
||||
Operator_t* ops = func->operations.data;
|
||||
int count = 0;
|
||||
for (int i = 0; i < func->operations.count; i++) {
|
||||
if (ops[i].token == toCount) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
8
TegraExplorer/source/script/functionClass.h
Normal file
8
TegraExplorer/source/script/functionClass.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
|
||||
Function_t createEmptyFunction();
|
||||
Function_t* createFunctionPtrFromFunction(Function_t in);
|
||||
Variable_t newFunctionVariable(FunctionClass_t func);
|
||||
FunctionClass_t createFunctionClass(Function_t in, ClassFunctionTableEntry_t* builtIn);
|
||||
int countTokens(Function_t* func, u8 toCount);
|
||||
26
TegraExplorer/source/script/garbageCollector.c
Normal file
26
TegraExplorer/source/script/garbageCollector.c
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
#include "compat.h"
|
||||
#include "garbageCollector.h"
|
||||
|
||||
void modReference(Variable_t* ref, u8 add) {
|
||||
if (ref == NULL || ref->gcDoNotFree)
|
||||
return;
|
||||
|
||||
if (add) {
|
||||
ref->tagCount++;
|
||||
}
|
||||
else {
|
||||
ref->tagCount--;
|
||||
if (ref->tagCount <= 0) {
|
||||
// TODO: move to parser.c
|
||||
if (ref->variableType == FunctionClass && ref->function.builtIn && ref->function.origin != NULL)
|
||||
modReference(ref->function.origin, 0);
|
||||
|
||||
if (ref->variableType == SolvedArrayReferenceClass)
|
||||
modReference(ref->solvedArray.arrayClassReference, 0);
|
||||
|
||||
freeVariable(&ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
TegraExplorer/source/script/garbageCollector.h
Normal file
8
TegraExplorer/source/script/garbageCollector.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "model.h"
|
||||
|
||||
//void removePendingReference(Variable_t* ref);
|
||||
|
||||
void modReference(Variable_t* ref, u8 add);
|
||||
|
||||
#define removePendingReference(ref) modReference(ref, 0)
|
||||
#define addPendingReference(ref) modReference(ref, 1)
|
||||
297
TegraExplorer/source/script/genericClass.c
Normal file
297
TegraExplorer/source/script/genericClass.c
Normal file
@@ -0,0 +1,297 @@
|
||||
#include "genericClass.h"
|
||||
#include "model.h"
|
||||
#include "intClass.h"
|
||||
#include "compat.h"
|
||||
#include "eval.h"
|
||||
#include <string.h>
|
||||
#include "garbageCollector.h"
|
||||
#include "StringClass.h"
|
||||
#include "arrayClass.h"
|
||||
#include "arrayReferenceClass.h"
|
||||
#include "functionClass.h"
|
||||
#include "scriptError.h"
|
||||
#include "saveClass.h"
|
||||
#include "unsolvedArrayClass.h"
|
||||
#include "else.h"
|
||||
#include "dictionaryClass.h"
|
||||
|
||||
Variable_t* copyVariableToPtr(Variable_t var) {
|
||||
Variable_t* a = malloc(sizeof(Variable_t));
|
||||
*a = var;
|
||||
a->tagCount = 0;
|
||||
addPendingReference(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
MemberGetters_t memberGetters[] = {
|
||||
{IntClass, getIntegerMember},
|
||||
{StringClass, getStringMember},
|
||||
{IntArrayClass, getArrayMember},
|
||||
{StringArrayClass, getArrayMember},
|
||||
{ByteArrayClass, getArrayMember},
|
||||
{SolvedArrayReferenceClass, getArrayReferenceMember},
|
||||
{UnresolvedArrayClass, getUnsolvedArrayMember},
|
||||
{ElseClass, getElseMember},
|
||||
{DictionaryClass, getDictMember},
|
||||
#ifndef WIN32
|
||||
{SaveClass, getSaveMember},
|
||||
#endif
|
||||
};
|
||||
|
||||
Variable_t* genericGet(Variable_t* var, CallArgs_t* ref) {
|
||||
if (ref->extraAction == ActionExtraMemberName) {
|
||||
for (u32 i = 0; i < ARRAY_SIZE(memberGetters); i++) {
|
||||
if (var->variableType == memberGetters[i].classType) {
|
||||
Variable_t member = memberGetters[i].func(var, ref->extra);
|
||||
if (member.variableType == None)
|
||||
break;
|
||||
|
||||
if (member.variableType == ReferenceType) {
|
||||
return member.referenceType;
|
||||
}
|
||||
|
||||
addPendingReference(var); // So caller doesn't fall out of scope. Don't forget to free!
|
||||
return copyVariableToPtr(member);
|
||||
}
|
||||
}
|
||||
|
||||
SCRIPT_FATAL_ERR("Did not find member '%s'", ref->extra);
|
||||
}
|
||||
else if (ref->extraAction == ActionExtraArrayIndex) {
|
||||
Function_t* idx = ref->extra;
|
||||
Variable_t *solvedIdx = eval(idx->operations.data, idx->operations.count, 1);
|
||||
|
||||
if (solvedIdx->variableType != IntClass) {
|
||||
SCRIPT_FATAL_ERR("Index is not an integer");
|
||||
}
|
||||
|
||||
Variable_t* res = callMemberFunctionDirect(var, "get", &solvedIdx, 1);
|
||||
removePendingReference(solvedIdx);
|
||||
return res;
|
||||
}
|
||||
|
||||
SCRIPT_FATAL_ERR("???");
|
||||
}
|
||||
|
||||
Variable_t* genericCallDirect(Variable_t* var, Variable_t** args, u8 len) {
|
||||
if (var->variableType != FunctionClass){
|
||||
SCRIPT_FATAL_ERR("Call on non function class");
|
||||
}
|
||||
|
||||
if (var->function.builtIn) {
|
||||
for (u32 i = 0; i < var->function.len; i++) {
|
||||
if (var->function.builtInPtr[i].argCount == len || var->function.builtInPtr[i].argCount == VARARGCOUNT) {
|
||||
int valid = 1;
|
||||
if (var->function.builtInPtr[i].argCount != VARARGCOUNT) {
|
||||
for (u32 j = 0; j < var->function.builtInPtr[i].argCount; j++) {
|
||||
if (var->function.builtInPtr[i].argTypes[j] != args[j]->variableType && var->function.builtInPtr[i].argTypes[j] != VARARGCOUNT) {
|
||||
valid = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
return var->function.builtInPtr[i].func(var->function.origin, args, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Variable_t *ret = eval(var->function.function.operations.data, var->function.function.operations.count, 1);
|
||||
if (ret == NULL)
|
||||
return NULL;
|
||||
removePendingReference(ret);
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
SCRIPT_FATAL_ERR("Arguments do not match function defenition(s)");
|
||||
}
|
||||
|
||||
Variable_t* genericCall(Variable_t* var, CallArgs_t* ref) {
|
||||
if (var->variableType != FunctionClass){
|
||||
SCRIPT_FATAL_ERR("Call on non function class");
|
||||
}
|
||||
Function_t* f = ref->extra;
|
||||
if (var->function.builtIn) {
|
||||
if (f->operations.count == 0) {
|
||||
return genericCallDirect(var, NULL, 0);
|
||||
}
|
||||
else {
|
||||
//Vector_t argsHolder = newVec(sizeof(Variable_t*), 1);
|
||||
Variable_t** argsHolder = NULL;
|
||||
if (var->function.builtInPtr->argCount != 0)
|
||||
argsHolder = malloc(sizeof(Variable_t*) * var->function.builtInPtr->argCount);
|
||||
int argCount = 0;
|
||||
int lasti = 0;
|
||||
Operator_t* ops = f->operations.data;
|
||||
|
||||
int tooManyArgs = 0;
|
||||
|
||||
// Loops trough the function to get all args out
|
||||
for (int i = 0; i < f->operations.count; i++) {
|
||||
if (ops[i].token == EquationSeperator || i + 1 == f->operations.count) {
|
||||
if (i + 1 == f->operations.count)
|
||||
i++;
|
||||
|
||||
if (argCount == var->function.builtInPtr->argCount) {
|
||||
tooManyArgs = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (var->function.firstArgAsFunction && argCount == 0) {
|
||||
Function_t f = { .operations = vecFromArray(&ops[lasti], i - lasti, sizeof(Operator_t)) };
|
||||
Variable_t var = newFunctionVariable(createFunctionClass(f, NULL));
|
||||
var.reference = 1;
|
||||
Variable_t* varPtr = copyVariableToPtr(var);
|
||||
//vecAdd(&argsHolder, varPtr);
|
||||
argsHolder[argCount++] = varPtr;
|
||||
}
|
||||
else {
|
||||
Variable_t* var = eval(&ops[lasti], i - lasti, 1);
|
||||
if (var == NULL)
|
||||
return NULL; // maybe free first?
|
||||
|
||||
//vecAdd(&argsHolder, var);
|
||||
argsHolder[argCount++] = var;
|
||||
}
|
||||
|
||||
lasti = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Variable_t* res = NULL;
|
||||
|
||||
if (!tooManyArgs)
|
||||
res = genericCallDirect(var, argsHolder, argCount);
|
||||
else {
|
||||
SCRIPT_FATAL_ERR("Too many args provided (got more than %d)", argCount);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < argCount; i++)
|
||||
removePendingReference(argsHolder[i]);
|
||||
|
||||
//vecForEach(Variable_t**, tofree, (&argsHolder))
|
||||
// removePendingReference(*tofree);
|
||||
|
||||
FREE(argsHolder);
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (f->operations.count){
|
||||
if (eval(f->operations.data, f->operations.count, 0) == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (eval(var->function.function.operations.data, var->function.function.operations.count, 0) == NULL)
|
||||
return NULL;
|
||||
|
||||
return &emptyClass;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add staticStorage
|
||||
Variable_t getGenericFunctionMember(Variable_t* var, char* memberName, ClassFunctionTableEntry_t* entries, u8 len) {
|
||||
Variable_t newVar = {.readOnly = 1, .variableType = FunctionClass};
|
||||
newVar.function.origin = var;
|
||||
newVar.function.builtIn = 1;
|
||||
for (u32 i = 0; i < len; i++) {
|
||||
if (!strcmp(entries[i].name, memberName)) {
|
||||
newVar.function.builtInPtr = &entries[i];
|
||||
|
||||
u32 j = i;
|
||||
for (; j < len && !strcmp(entries[j].name, memberName); j++);
|
||||
newVar.function.len = j - i;
|
||||
|
||||
return newVar;
|
||||
}
|
||||
}
|
||||
|
||||
return (Variable_t){ 0 };
|
||||
}
|
||||
|
||||
Variable_t* callMemberFunction(Variable_t* var, char* memberName, CallArgs_t* args) {
|
||||
for (u32 i = 0; i < ARRAY_SIZE(memberGetters); i++) {
|
||||
if (var->variableType == memberGetters[i].classType) {
|
||||
Variable_t funcRef = memberGetters[i].func(var, memberName);
|
||||
if (funcRef.variableType == None) {
|
||||
SCRIPT_FATAL_ERR("Did not find member '%s'", memberName);
|
||||
}
|
||||
|
||||
Variable_t* ptr = &funcRef;
|
||||
if (funcRef.variableType == ReferenceType) {
|
||||
ptr = funcRef.referenceType;
|
||||
}
|
||||
|
||||
return genericCall(ptr, args);
|
||||
}
|
||||
}
|
||||
|
||||
SCRIPT_FATAL_ERR("Could not find function table for given type");
|
||||
}
|
||||
|
||||
Variable_t* callMemberFunctionDirect(Variable_t* var, char* memberName, Variable_t** args, u8 argsLen) {
|
||||
for (u32 i = 0; i < ARRAY_SIZE(memberGetters); i++) {
|
||||
if (var->variableType == memberGetters[i].classType) {
|
||||
Variable_t funcRef = memberGetters[i].func(var, memberName);
|
||||
if (funcRef.variableType == None) {
|
||||
SCRIPT_FATAL_ERR("Did not find member '%s'", memberName);
|
||||
}
|
||||
|
||||
Variable_t* ptr = &funcRef;
|
||||
if (funcRef.variableType == ReferenceType) {
|
||||
ptr = funcRef.referenceType;
|
||||
}
|
||||
|
||||
return genericCallDirect(ptr, args, argsLen);
|
||||
}
|
||||
}
|
||||
|
||||
SCRIPT_FATAL_ERR("Could not find function table for given type");
|
||||
}
|
||||
|
||||
void freeVariableInternal(Variable_t* referencedTarget) {
|
||||
switch (referencedTarget->variableType) {
|
||||
case StringClass:
|
||||
FREE(referencedTarget->string.value);
|
||||
break;
|
||||
case StringArrayClass:
|
||||
vecForEach(char**, stringsInArray, (&referencedTarget->solvedArray.vector)) {
|
||||
FREE(*stringsInArray);
|
||||
}
|
||||
case ByteArrayClass:
|
||||
case IntArrayClass:
|
||||
vecFree(referencedTarget->solvedArray.vector);
|
||||
break;
|
||||
case DictionaryClass:;
|
||||
vecForEach(Dict_t*, dict, (&referencedTarget->dictionary.vector)) {
|
||||
modReference(dict->var, 0);
|
||||
free(dict->name);
|
||||
}
|
||||
FREE(referencedTarget->dictionary.vector.data);
|
||||
break;
|
||||
case SaveClass:;
|
||||
#ifndef WIN32
|
||||
save_free_contexts(&referencedTarget->save->saveCtx);
|
||||
f_close(&referencedTarget->save->saveFile);
|
||||
FREE(referencedTarget->save);
|
||||
#endif // !WIN32
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void freeVariable(Variable_t** target) {
|
||||
// Add specific freeing logic here
|
||||
Variable_t* referencedTarget = *target;
|
||||
|
||||
if (!referencedTarget->reference) {
|
||||
freeVariableInternal(referencedTarget);
|
||||
}
|
||||
|
||||
|
||||
FREE(referencedTarget);
|
||||
*target = NULL;
|
||||
}
|
||||
25
TegraExplorer/source/script/genericClass.h
Normal file
25
TegraExplorer/source/script/genericClass.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
|
||||
Variable_t* copyVariableToPtr(Variable_t var);
|
||||
|
||||
#define VARARGCOUNT 255
|
||||
|
||||
#define ClassFunction(name) Variable_t* name(Variable_t* caller, Variable_t** args, u8 argsLen)
|
||||
|
||||
|
||||
typedef Variable_t (*getMemberFunction)(Variable_t*, char*);
|
||||
|
||||
typedef struct {
|
||||
u8 classType;
|
||||
getMemberFunction func;
|
||||
} MemberGetters_t;
|
||||
|
||||
Variable_t getGenericFunctionMember(Variable_t* var, char* memberName, ClassFunctionTableEntry_t* entries, u8 len);
|
||||
Variable_t* genericGet(Variable_t* var, CallArgs_t* ref);
|
||||
Variable_t* genericCallDirect(Variable_t* var, Variable_t** args, u8 len);
|
||||
Variable_t* callMemberFunctionDirect(Variable_t* var, char* memberName, Variable_t** args, u8 argsLen);
|
||||
Variable_t* genericCall(Variable_t* var, CallArgs_t* ref);
|
||||
void freeVariable(Variable_t** target);
|
||||
Variable_t* callMemberFunction(Variable_t* var, char* memberName, CallArgs_t* args);
|
||||
void freeVariableInternal(Variable_t* referencedTarget);
|
||||
91
TegraExplorer/source/script/intClass.c
Normal file
91
TegraExplorer/source/script/intClass.c
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "intClass.h"
|
||||
#include "StringClass.h"
|
||||
#include "compat.h"
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <utils/sprintf.h>
|
||||
#endif
|
||||
|
||||
IntClass_t createIntClass(s64 in) {
|
||||
IntClass_t a = { in };
|
||||
return a;
|
||||
}
|
||||
|
||||
Variable_t newIntVariable(s64 x) {
|
||||
// Integers are always read-only
|
||||
Variable_t var = { .variableType = IntClass, .readOnly = 1, .integer = createIntClass(x) };
|
||||
return var;
|
||||
}
|
||||
|
||||
ClassFunction(printIntVariable) {
|
||||
IntClass_t* a = &caller->integer;
|
||||
gfx_printf("%d", (int)a->value);
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunction(intToStr) {
|
||||
#ifndef WIN32
|
||||
char buff[64] = { 0 };
|
||||
s_printf(buff, "%d", getIntValue(caller));
|
||||
return newStringVariablePtr(CpyStr(buff), 0, 1);
|
||||
#else
|
||||
return newIntVariablePtr(0);
|
||||
#endif // !WIN32
|
||||
}
|
||||
|
||||
#define IntOpFunction(name, op) ClassFunction(name) { s64 i1 = getIntValue(caller); s64 i2 = getIntValue(*args); return newIntVariablePtr((i1 op i2)); }
|
||||
|
||||
IntOpFunction(addInt, +)
|
||||
IntOpFunction(minusInt, -)
|
||||
IntOpFunction(multInt, *)
|
||||
IntOpFunction(divInt, /)
|
||||
IntOpFunction(modInt, %)
|
||||
IntOpFunction(smallerInt, <)
|
||||
IntOpFunction(biggerInt, >)
|
||||
IntOpFunction(smallerEqInt, <=)
|
||||
IntOpFunction(biggerEqInt, >=)
|
||||
IntOpFunction(eqInt, ==)
|
||||
IntOpFunction(notEqInt, !=)
|
||||
IntOpFunction(logicAndInt, &&)
|
||||
IntOpFunction(logicOrInt, ||)
|
||||
IntOpFunction(andInt, &)
|
||||
IntOpFunction(orInt, |)
|
||||
IntOpFunction(bitshiftLeftInt, <<)
|
||||
IntOpFunction(bitshiftRightInt, >>)
|
||||
|
||||
ClassFunction(notInt) {
|
||||
return newIntVariablePtr(!(getIntValue(caller)));
|
||||
}
|
||||
|
||||
u8 oneVarArgInt[] = { VARARGCOUNT };
|
||||
u8 oneIntArgInt[] = { IntClass };
|
||||
|
||||
#define IntOpFunctionEntry(opName, functionName) {opName, functionName, 1, oneIntArgInt}
|
||||
|
||||
ClassFunctionTableEntry_t intFunctions[] = {
|
||||
{"print", printIntVariable, 0, 0},
|
||||
{"not", notInt, 0, 0},
|
||||
{"str", intToStr, 0, 0},
|
||||
IntOpFunctionEntry("+", addInt),
|
||||
IntOpFunctionEntry("-", minusInt),
|
||||
IntOpFunctionEntry("*", multInt),
|
||||
IntOpFunctionEntry("/", divInt),
|
||||
IntOpFunctionEntry("%", modInt),
|
||||
IntOpFunctionEntry("<", smallerInt),
|
||||
IntOpFunctionEntry(">", biggerInt),
|
||||
IntOpFunctionEntry("<=", smallerEqInt),
|
||||
IntOpFunctionEntry(">=", biggerEqInt),
|
||||
IntOpFunctionEntry("==", eqInt),
|
||||
IntOpFunctionEntry("!=", notEqInt),
|
||||
IntOpFunctionEntry("&&", logicAndInt),
|
||||
IntOpFunctionEntry("||", logicOrInt),
|
||||
IntOpFunctionEntry("&", andInt),
|
||||
IntOpFunctionEntry("|", orInt),
|
||||
IntOpFunctionEntry("<<", bitshiftLeftInt),
|
||||
IntOpFunctionEntry(">>", bitshiftRightInt),
|
||||
};
|
||||
|
||||
Variable_t getIntegerMember(Variable_t* var, char* memberName) {
|
||||
return getGenericFunctionMember(var, memberName, intFunctions, ARRAY_SIZE(intFunctions));
|
||||
}
|
||||
13
TegraExplorer/source/script/intClass.h
Normal file
13
TegraExplorer/source/script/intClass.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
|
||||
|
||||
|
||||
#define getIntValue(var) (var)->integer.value
|
||||
|
||||
IntClass_t createIntClass(s64 in);
|
||||
|
||||
Variable_t newIntVariable(s64 x);
|
||||
#define newIntVariablePtr(x) copyVariableToPtr(newIntVariable(x))
|
||||
Variable_t getIntegerMember(Variable_t* var, char* memberName);
|
||||
43
TegraExplorer/source/script/model.c
Normal file
43
TegraExplorer/source/script/model.c
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "model.h"
|
||||
#include "compat.h"
|
||||
#include "StringClass.h"
|
||||
#include "intClass.h"
|
||||
|
||||
TokenConvertion_t tokenConvertions[] = {
|
||||
{SmallerEqual, "<="},
|
||||
{BiggerEqual, ">="},
|
||||
{NotEqual, "!="},
|
||||
{LogicAnd, "&&"},
|
||||
{LogicOr, "||"},
|
||||
{EqualEqual, "=="},
|
||||
|
||||
{BitShiftLeft, "<<"},
|
||||
{BitShiftRight, ">>"},
|
||||
|
||||
{Not, "!"},
|
||||
{Plus, "+"},
|
||||
{Equals, "="},
|
||||
{Minus, "-"},
|
||||
{Multiply, "*"},
|
||||
{Division, "/"},
|
||||
{Modulo, "%"},
|
||||
|
||||
{LeftSquareBracket, "["},
|
||||
{LeftCurlyBracket, "{"},
|
||||
{LeftBracket, "("},
|
||||
{RightSquareBracket, "]"},
|
||||
{RightCurlyBracket, "}"},
|
||||
{RightBracket, ")"},
|
||||
|
||||
{Smaller, "<"},
|
||||
{Bigger, ">"},
|
||||
|
||||
{And, "&"},
|
||||
{Or, "|"},
|
||||
{Dot, "."},
|
||||
{EquationSeperator, ","},
|
||||
};
|
||||
|
||||
u32 tokenConvertionCount = ARRAY_SIZE(tokenConvertions);
|
||||
|
||||
Variable_t emptyClass = { .variableType = EmptyClass, .readOnly = 1, .reference = 1, .gcDoNotFree = 1 };
|
||||
276
TegraExplorer/source/script/model.h
Normal file
276
TegraExplorer/source/script/model.h
Normal file
@@ -0,0 +1,276 @@
|
||||
#pragma once
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef long long s64;
|
||||
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma pack(1)
|
||||
typedef struct {
|
||||
void* data;
|
||||
u32 capacity;
|
||||
u32 count;
|
||||
u8 elemSz;
|
||||
} Vector_t;
|
||||
#else
|
||||
#include "../utils/vector.h"
|
||||
#include <libs/nx_savedata/save.h>
|
||||
#pragma pack(1)
|
||||
#endif
|
||||
|
||||
|
||||
typedef enum {
|
||||
None = 0,
|
||||
IntClass,
|
||||
FunctionClass,
|
||||
StringClass,
|
||||
ByteArrayClass,
|
||||
StringArrayClass,
|
||||
IntArrayClass,
|
||||
UnresolvedArrayClass,
|
||||
DictionaryClass,
|
||||
EmptyClass,
|
||||
SolvedArrayReferenceClass,
|
||||
SaveClass,
|
||||
ElseClass,
|
||||
ReferenceType,
|
||||
} VariableType_t;
|
||||
|
||||
typedef enum {
|
||||
Invalid = 0,
|
||||
Variable,
|
||||
BetweenBrackets,
|
||||
Not,
|
||||
|
||||
Plus,
|
||||
Equals,
|
||||
Minus,
|
||||
Multiply,
|
||||
Division,
|
||||
Modulo,
|
||||
|
||||
LeftSquareBracket,
|
||||
LeftCurlyBracket,
|
||||
LeftBracket,
|
||||
RightSquareBracket,
|
||||
RightCurlyBracket,
|
||||
RightBracket,
|
||||
|
||||
Smaller,
|
||||
SmallerEqual,
|
||||
Bigger,
|
||||
BiggerEqual,
|
||||
EqualEqual,
|
||||
NotEqual,
|
||||
LogicAnd,
|
||||
LogicOr,
|
||||
|
||||
BitShiftLeft,
|
||||
BitShiftRight,
|
||||
And,
|
||||
Or,
|
||||
|
||||
EquationSeperator,
|
||||
Dot,
|
||||
CallArgs,
|
||||
} Token_t;
|
||||
|
||||
typedef enum {
|
||||
ActionGet = 0,
|
||||
ActionSet,
|
||||
ActionCall,
|
||||
} ActionType_t;
|
||||
|
||||
typedef enum {
|
||||
ActionExtraNone = 0,
|
||||
ActionExtraArrayIndex,
|
||||
ActionExtraMemberName,
|
||||
ActionExtraCallArgs,
|
||||
ActionExtraCallArgsFunction
|
||||
} ActionExtraType_t;
|
||||
|
||||
|
||||
// Change to a Vector_t with Operator_t's
|
||||
typedef struct {
|
||||
Vector_t operations; // Operation_t. Equations seperated by EquationSep
|
||||
} Function_t;
|
||||
|
||||
struct _ClassFunctionTableEntry_t;
|
||||
struct _Variable_t;
|
||||
|
||||
typedef struct _FunctionClass_t {
|
||||
union {
|
||||
struct {
|
||||
u8 builtIn : 1;
|
||||
u8 firstArgAsFunction : 1;
|
||||
};
|
||||
u8 unionFunctionOptions;
|
||||
};
|
||||
union {
|
||||
Function_t function;
|
||||
struct {
|
||||
struct _ClassFunctionTableEntry_t* builtInPtr;
|
||||
struct _Variable_t* origin;
|
||||
u8 len;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} FunctionClass_t;
|
||||
|
||||
typedef enum {
|
||||
ArrayType_Int = 0,
|
||||
ArrayType_String,
|
||||
ArrayType_Byte
|
||||
} ArrayType_t;
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
Vector_t vector; // vector of typeof(value)
|
||||
struct {
|
||||
struct _Variable_t* arrayClassReference;
|
||||
u32 offset;
|
||||
u32 len;
|
||||
};
|
||||
};
|
||||
|
||||
} ArrayClass_t;
|
||||
|
||||
typedef struct _UnsolvedArrayClass_t {
|
||||
Vector_t operations; // Operator_t
|
||||
} UnsolvedArrayClass_t;
|
||||
|
||||
typedef struct _DictionaryClass_t {
|
||||
Vector_t vector; // vector of typeof(Dict_t)
|
||||
} DictionaryClass_t;
|
||||
|
||||
typedef struct _IntClass_t {
|
||||
s64 value;
|
||||
} IntClass_t;
|
||||
|
||||
typedef struct _StringClass_t {
|
||||
char* value;
|
||||
struct {
|
||||
u8 free : 1;
|
||||
};
|
||||
} StringClass_t;
|
||||
|
||||
#ifndef WIN32
|
||||
typedef struct {
|
||||
save_ctx_t saveCtx;
|
||||
FIL saveFile;
|
||||
} SaveClass_t;
|
||||
#endif
|
||||
|
||||
typedef struct _Variable_t {
|
||||
//void* variable;
|
||||
union {
|
||||
FunctionClass_t function;
|
||||
UnsolvedArrayClass_t unsolvedArray;
|
||||
DictionaryClass_t dictionary;
|
||||
IntClass_t integer;
|
||||
StringClass_t string;
|
||||
ArrayClass_t solvedArray;
|
||||
#ifndef WIN32
|
||||
SaveClass_t *save;
|
||||
#endif
|
||||
struct _Variable_t* referenceType;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
u8 variableType : 5;
|
||||
u8 readOnly : 1;
|
||||
u8 reference : 1;
|
||||
u8 gcDoNotFree : 1;
|
||||
};
|
||||
};
|
||||
u8 tagCount;
|
||||
} Variable_t;
|
||||
|
||||
typedef struct _CallArgs_t {
|
||||
struct {
|
||||
u8 action : 4;
|
||||
u8 extraAction : 4;
|
||||
};
|
||||
void* extra; // Function_t for arrayIdx, char* for member, Function_t for funcCall, Function_t x2 for funcCallArgs
|
||||
struct _CallArgs_t* next;
|
||||
} CallArgs_t;
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
u32 len;
|
||||
} Array_t;
|
||||
|
||||
typedef Variable_t* (*ClassFunction)(Variable_t* caller, Variable_t** args, u8 argsLen);
|
||||
|
||||
typedef struct _ClassFunctionTableEntry_t {
|
||||
char* name;
|
||||
ClassFunction func;
|
||||
u8 argCount;
|
||||
u8* argTypes;
|
||||
} ClassFunctionTableEntry_t;
|
||||
|
||||
typedef struct _VariableReference_t {
|
||||
union {
|
||||
struct {
|
||||
u8 staticVariableSet : 1;
|
||||
u8 staticVariableRef : 1;
|
||||
u8 staticVariableType : 2; // 0 = ref, 1 = int, 2 = str, 3 = stdlib func
|
||||
};
|
||||
u8 staticVariableOptionsUnion;
|
||||
};
|
||||
|
||||
union {
|
||||
Variable_t* staticVariable;
|
||||
char* name;
|
||||
Array_t betweenBrackets;
|
||||
s64 integerType;
|
||||
char* stringType;
|
||||
struct {
|
||||
ClassFunctionTableEntry_t* staticFunction;
|
||||
u8 staticFunctionLen;
|
||||
};
|
||||
};
|
||||
} VariableReference_t;
|
||||
|
||||
//typedef Variable_t* (*classFunctionTable)(VariableReference_t*);
|
||||
|
||||
typedef Variable_t* (*classFunctionTable)(char*, Variable_t*, VariableReference_t*, Vector_t*);
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
Variable_t* var;
|
||||
} Dict_t;
|
||||
|
||||
typedef struct {
|
||||
u8 token : 8;
|
||||
char strToken[3];
|
||||
} TokenConvertion_t;
|
||||
|
||||
extern TokenConvertion_t tokenConvertions[];
|
||||
extern u32 tokenConvertionCount;
|
||||
|
||||
typedef struct {
|
||||
Vector_t operations; // Operator_t
|
||||
} Equation_t;
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
u8 token : 7;
|
||||
u8 not : 1;
|
||||
};
|
||||
union {
|
||||
VariableReference_t variable;
|
||||
CallArgs_t callArgs;
|
||||
char* tokenStr;
|
||||
s64 lineNumber;
|
||||
};
|
||||
// probably should add u16 lineNum here
|
||||
} Operator_t;
|
||||
|
||||
extern Variable_t emptyClass;
|
||||
|
||||
#pragma pack()
|
||||
670
TegraExplorer/source/script/parser.c
Normal file
670
TegraExplorer/source/script/parser.c
Normal file
@@ -0,0 +1,670 @@
|
||||
#include "model.h"
|
||||
#include "compat.h"
|
||||
#include "compat.h"
|
||||
#include "parser.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "intClass.h"
|
||||
#include "StringClass.h"
|
||||
#include "unsolvedArrayClass.h"
|
||||
#include "functionClass.h"
|
||||
|
||||
#include "scriptError.h"
|
||||
#include "standardLibrary.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#include "../tegraexplorer/tconf.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#endif
|
||||
|
||||
static inline int isValidWord(char c) {
|
||||
char r = c | 0x20;
|
||||
return ((r >= 'a' && r <= 'z') || c == '_');
|
||||
}
|
||||
|
||||
static inline int isValidNum(char c) {
|
||||
return (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
static inline int isValidVar(char c) {
|
||||
return (isValidWord(c) || isValidNum(c));
|
||||
}
|
||||
|
||||
static inline int isValidHexNum(char c) {
|
||||
char r = c | 0x20;
|
||||
return (isValidNum(r) || (r >= 'a' && r <= 'f'));
|
||||
}
|
||||
|
||||
char* getTokenText(u8 token) {
|
||||
for (u32 i = 0; i < tokenConvertionCount; i++) {
|
||||
if (tokenConvertions[i].token == token)
|
||||
return tokenConvertions[i].strToken;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* utils_copyStringSize(const char* in, int size) {
|
||||
if (size > strlen(in) || size < 0)
|
||||
size = strlen(in);
|
||||
|
||||
char* out = calloc(size + 1, 1);
|
||||
//strncpy(out, in, size);
|
||||
if (size)
|
||||
memcpy(out, in, size);
|
||||
return out;
|
||||
}
|
||||
|
||||
#define ELIFC(c) else if (*in == c)
|
||||
|
||||
Vector_t script;
|
||||
s64 lineNumber;
|
||||
|
||||
enum TokenType {
|
||||
Token_Variable = 0,
|
||||
Token_String,
|
||||
Token_Int,
|
||||
Token_Token,
|
||||
Token_Err,
|
||||
Token_Fatal_Err,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
History_Function = 0,
|
||||
History_Bracket,
|
||||
History_Array,
|
||||
} StackHistory_t;
|
||||
|
||||
char* end;
|
||||
|
||||
u8 nextToken(char** inPtr, void** val) {
|
||||
char* in = *inPtr;
|
||||
u8 ret = Token_Err;
|
||||
while (ret == Token_Err) {
|
||||
if (*in == '#') {
|
||||
if (!memcmp(in + 1, "REQUIRE ", 8)) {
|
||||
if (!memcmp(in + 9, "VER ", 4)) {
|
||||
char* verStart = in + 13;
|
||||
char* verEnd = verStart;
|
||||
|
||||
while (isValidNum(*verEnd) || *verEnd == '.')
|
||||
verEnd++;
|
||||
|
||||
u8 outdated = (verEnd - verStart != strlen(LP_VER));
|
||||
|
||||
if (!outdated){
|
||||
outdated = (memcmp(LP_VER, verStart, verEnd - verStart) < 0);
|
||||
}
|
||||
|
||||
if (outdated) {
|
||||
printScriptError(SCRIPT_LEXER_FATAL, "Script requires a newer TegraExplorer version!");
|
||||
return Token_Fatal_Err;
|
||||
}
|
||||
}
|
||||
else if (!memcmp(in + 9, "MINERVA", 7)) {
|
||||
#ifdef WIN32
|
||||
u8 minervaEnabled = 0;
|
||||
#else
|
||||
u8 minervaEnabled = TConf.minervaEnabled;
|
||||
#endif
|
||||
if (!minervaEnabled) {
|
||||
printScriptError(SCRIPT_LEXER_FATAL, "Extended memory required.\nPut the bootloader folder from hekate on your sd!");
|
||||
return Token_Fatal_Err;
|
||||
}
|
||||
}
|
||||
else if (!memcmp(in + 9, "KEYS", 4)) {
|
||||
#ifdef WIN32
|
||||
u8 gotKeys = 0;
|
||||
#else
|
||||
u8 gotKeys = TConf.keysDumped;
|
||||
#endif
|
||||
if (!gotKeys){
|
||||
printScriptError(SCRIPT_LEXER_FATAL, "Keys required.\nMake sure you're on the latest version of TegraExplorer!");
|
||||
return Token_Fatal_Err;
|
||||
}
|
||||
}
|
||||
else if (!memcmp(in + 9, "SD", 2)) {
|
||||
#ifdef WIN32
|
||||
u8 gotSd = 0;
|
||||
#else
|
||||
u8 gotSd = sd_mount();
|
||||
#endif
|
||||
if (!gotSd){
|
||||
printScriptError(SCRIPT_LEXER_FATAL, "Sd required.");
|
||||
return Token_Fatal_Err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (*in && *in != '\n')
|
||||
in++;
|
||||
|
||||
lineNumber++;
|
||||
scriptCurrentLine = lineNumber;
|
||||
}
|
||||
else if (isValidWord(*in)) {
|
||||
char* startWord = in;
|
||||
in++;
|
||||
while (isValidVar(*in))
|
||||
in++;
|
||||
|
||||
char* str = utils_copyStringSize(startWord, in - startWord);
|
||||
|
||||
//gfx_printf("Variable: '%s'\n", str);
|
||||
ret = Token_Variable;
|
||||
*val = str;
|
||||
break;
|
||||
}
|
||||
else if (isValidNum(*in) || (*in == '-' && isValidNum(in[1]))) {
|
||||
s64 parse = 0;
|
||||
u8 negative = (*in == '-');
|
||||
if (negative)
|
||||
in++;
|
||||
|
||||
if (*in == '0' && (in[1] | 0x20) == 'x') {
|
||||
in += 2;
|
||||
while (isValidHexNum(*in)) {
|
||||
parse = parse * 16 + (*in & 0x0F) + (*in >= 'A' ? 9 : 0);
|
||||
in++;
|
||||
}
|
||||
}
|
||||
else while (isValidNum(*in)) {
|
||||
parse = parse * 10 + *in++ - '0';
|
||||
}
|
||||
|
||||
if (negative)
|
||||
parse *= -1;
|
||||
|
||||
//gfx_printf("Integer: '%d'\n", parse);
|
||||
ret = Token_Int;
|
||||
|
||||
s64* parsePersistent = malloc(sizeof(s64));
|
||||
*parsePersistent = parse;
|
||||
|
||||
*val = parsePersistent;
|
||||
break;
|
||||
}
|
||||
ELIFC('"') {
|
||||
char* startStr = ++in;
|
||||
int len = 0;
|
||||
while (*in != '"') {
|
||||
in++;
|
||||
}
|
||||
len = in - startStr;
|
||||
|
||||
char* storage = malloc(len + 1);
|
||||
|
||||
int pos = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (startStr[i] == '\\') {
|
||||
if (startStr[i + 1] == 'n') {
|
||||
storage[pos++] = '\n';
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (startStr[i + 1] == 'r') {
|
||||
storage[pos++] = '\r';
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
storage[pos++] = startStr[i];
|
||||
}
|
||||
storage[pos] = '\0';
|
||||
|
||||
//gfx_printf("String: '%s'\n", storage);
|
||||
ret = Token_String;
|
||||
*val = storage;
|
||||
}
|
||||
else if (*in == '\0' || in > end) {
|
||||
*inPtr = in;
|
||||
return ret;
|
||||
}
|
||||
else if (*in == '\n'){
|
||||
lineNumber++;
|
||||
scriptCurrentLine = lineNumber;
|
||||
}
|
||||
else {
|
||||
for (u32 i = 0; i < tokenConvertionCount; i++) {
|
||||
TokenConvertion_t t = tokenConvertions[i];
|
||||
if (!memcmp(t.strToken, in, (t.strToken[1] == '\0') ? 1 : 2)) {
|
||||
//gfx_printf("Token: '%s'\n", t.strToken);
|
||||
ret = Token_Token;
|
||||
*val = (void*)t.token;
|
||||
|
||||
if (t.strToken[1] != '\0')
|
||||
in++;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
in++;
|
||||
}
|
||||
|
||||
*inPtr = in;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define CreateVariableReferenceStatic(var) VariableReference_t reference = { .staticVariable = var, .staticVariableSet = 1, .staticVariableRef = 1 }
|
||||
#define CreateVariableReferenceStr(str) VariableReference_t reference = { .name = str }
|
||||
|
||||
void setNextActionOperator(Vector_t *opHolder, ActionType_t action, ActionExtraType_t actionExtra, void *extra) {
|
||||
Operator_t* ops = opHolder->data;
|
||||
Operator_t* lastOp = &ops[opHolder->count - 1];
|
||||
|
||||
if (lastOp->token == CallArgs) {
|
||||
CallArgs_t* last = &lastOp->callArgs;
|
||||
for (; last->next != NULL; last = last->next);
|
||||
last->next = calloc(sizeof(CallArgs_t), 1);
|
||||
last->next->action = action;
|
||||
last->next->extra = extra;
|
||||
last->next->extraAction = actionExtra;
|
||||
}
|
||||
else {
|
||||
Operator_t newOp = { .token = CallArgs };
|
||||
newOp.callArgs.action = action;
|
||||
newOp.callArgs.extra = extra;
|
||||
newOp.callArgs.extraAction = actionExtra;
|
||||
vecAdd(opHolder, newOp);
|
||||
}
|
||||
}
|
||||
|
||||
CallArgs_t* getLastRef(CallArgs_t* ref) {
|
||||
for (; ref->next != NULL; ref = ref->next);
|
||||
return ref;
|
||||
}
|
||||
|
||||
int isLastVarSet(Operator_t* opHolder) {
|
||||
return (opHolder->token == CallArgs && getLastRef(&opHolder->callArgs)->action == ActionSet);
|
||||
}
|
||||
|
||||
int isLastVarCall(Operator_t* opHolder) {
|
||||
return (opHolder->token == CallArgs && getLastRef(&opHolder->callArgs)->action == ActionCall);
|
||||
}
|
||||
|
||||
ParserRet_t parseScript(char* in, u32 len) {
|
||||
Vector_t functionStack; // Function_t
|
||||
Vector_t stackHistoryHolder; // StaticHistory_t
|
||||
Vector_t staticVariableHolder; // Variable_t
|
||||
|
||||
functionStack = newVec(sizeof(Function_t), 0);
|
||||
Function_t firstFunction = createEmptyFunction();
|
||||
vecAdd(&functionStack, firstFunction);
|
||||
|
||||
staticVariableHolder = newVec(sizeof(Variable_t), 0);
|
||||
|
||||
stackHistoryHolder = newVec(sizeof(StackHistory_t), 1);
|
||||
StackHistory_t firstHistory = History_Function;
|
||||
vecAdd(&stackHistoryHolder, firstHistory);
|
||||
u8 notNext = 0;
|
||||
lineNumber = 1;
|
||||
scriptCurrentLine = 1;
|
||||
end = in + len;
|
||||
|
||||
while (*in && in <= end) {
|
||||
Function_t* lastFunc = getStackEntry(&functionStack);
|
||||
StackHistory_t* lastHistory = getStackEntry(&stackHistoryHolder);
|
||||
|
||||
Operator_t* lastOp = NULL;
|
||||
|
||||
if (lastFunc) {
|
||||
lastOp = getStackEntry(&lastFunc->operations);
|
||||
}
|
||||
|
||||
void* var = NULL;
|
||||
u8 tokenType = nextToken(&in, &var);
|
||||
|
||||
if (tokenType == Token_Err)
|
||||
break;
|
||||
|
||||
if (tokenType == Token_Fatal_Err)
|
||||
return (ParserRet_t) { 0 };
|
||||
|
||||
Operator_t op = { .token = Variable };
|
||||
|
||||
if (tokenType >= Token_Variable && tokenType <= Token_Int && lastOp) {
|
||||
if (lastOp->token == Variable || lastOp->token == BetweenBrackets || (lastOp->token == CallArgs && !isLastVarSet(lastOp))) {
|
||||
op.token = EquationSeperator;
|
||||
op.lineNumber = lineNumber;
|
||||
vecAdd(&lastFunc->operations, op);
|
||||
op.token = Variable;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenType == Token_Variable) {
|
||||
u8 stdLen = 0;
|
||||
ClassFunctionTableEntry_t* cfte = searchStdLib(var, &stdLen);
|
||||
|
||||
if (cfte == NULL) {
|
||||
CreateVariableReferenceStr(var);
|
||||
op.variable = reference;
|
||||
}
|
||||
else {
|
||||
VariableReference_t reference = { .staticVariableType = 3, .staticFunction = cfte, .staticFunctionLen = stdLen };
|
||||
op.variable = reference;
|
||||
}
|
||||
}
|
||||
else if (tokenType == Token_Int) {
|
||||
/*
|
||||
Variable_t a = newIntVariable(*((s64*)var));
|
||||
a.gcDoNotFree = 1;
|
||||
free(var);
|
||||
vecAdd(&staticVariableHolder, a);
|
||||
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
|
||||
*/
|
||||
|
||||
VariableReference_t reference = { .staticVariableType = 1, .integerType = *((s64*)var) };
|
||||
op.variable = reference;
|
||||
free(var);
|
||||
}
|
||||
else if (tokenType == Token_String) {
|
||||
/*
|
||||
Variable_t a = newStringVariable(var, 1, 1);
|
||||
a.gcDoNotFree = 1;
|
||||
vecAdd(&staticVariableHolder, a);
|
||||
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
|
||||
*/
|
||||
|
||||
VariableReference_t reference = { .staticVariableType = 2, .stringType = var };
|
||||
op.variable = reference;
|
||||
}
|
||||
else if (tokenType == Token_Token) {
|
||||
u8 token = (u8)var;
|
||||
|
||||
if (token == Equals && lastOp) {
|
||||
if (lastOp->token == Variable) {
|
||||
if (lastOp->variable.staticVariableSet) {
|
||||
SCRIPT_PARSER_ERR("Trying to assign to a static variable");
|
||||
}
|
||||
else {
|
||||
setNextActionOperator(&lastFunc->operations, ActionSet, ActionExtraNone, NULL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (lastOp->token == CallArgs) {
|
||||
CallArgs_t* last = getLastRef(&lastOp->callArgs);
|
||||
last->action = ActionSet;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
SCRIPT_PARSER_ERR("Trying to assign to non-object");
|
||||
}
|
||||
}
|
||||
else if (token == LeftCurlyBracket) {
|
||||
Function_t templateFunction = createEmptyFunction();
|
||||
vecAdd(&functionStack, templateFunction);
|
||||
|
||||
StackHistory_t functionHistory = History_Function;
|
||||
vecAdd(&stackHistoryHolder, functionHistory);
|
||||
continue;
|
||||
}
|
||||
else if (token == RightCurlyBracket) {
|
||||
if (stackHistoryHolder.count != 1 && *lastHistory == History_Function) {
|
||||
Function_t *popFunc = popStackEntry(&functionStack);
|
||||
popStackEntry(&stackHistoryHolder);
|
||||
|
||||
lastFunc = getStackEntry(&functionStack);
|
||||
|
||||
if (lastFunc) { // TODO: Add check for null deref
|
||||
lastOp = getStackEntry(&lastFunc->operations);
|
||||
}
|
||||
|
||||
if (lastOp && (lastOp->token == Variable || (lastOp->token == CallArgs && !isLastVarSet(lastOp)))) {
|
||||
if (lastOp->token == Variable) {
|
||||
SCRIPT_PARSER_ERR("GET variable before {}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set last arg to {}
|
||||
CallArgs_t* lastCall = getLastRef(&lastOp->callArgs);
|
||||
if (lastCall->extraAction == ActionExtraCallArgs) {
|
||||
Function_t* funcArgs = lastCall->extra;
|
||||
if (funcArgs->operations.count != 0) {
|
||||
op.token = EquationSeperator;
|
||||
op.lineNumber = lineNumber;
|
||||
vecAdd(&funcArgs->operations, op);
|
||||
op.token = Variable;
|
||||
}
|
||||
|
||||
Variable_t a = newFunctionVariable(createFunctionClass(*popFunc, NULL));
|
||||
vecAdd(&staticVariableHolder, a);
|
||||
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
|
||||
op.variable = reference;
|
||||
vecAdd(&funcArgs->operations, op);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Variable_t a = newFunctionVariable(createFunctionClass(*popFunc, NULL));
|
||||
vecAdd(&staticVariableHolder, a);
|
||||
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
|
||||
op.variable = reference;
|
||||
vecAdd(&lastFunc->operations, op);
|
||||
|
||||
op.token = EquationSeperator;
|
||||
op.lineNumber = lineNumber;
|
||||
}
|
||||
else {
|
||||
SCRIPT_PARSER_ERR("Stack count is 1 or state is not a function");
|
||||
}
|
||||
}
|
||||
else if (token == Dot) {
|
||||
if (lastOp && (lastOp->token == Variable || lastOp->token == BetweenBrackets || (lastOp->token == CallArgs && !isLastVarSet(lastOp)))) {
|
||||
tokenType = nextToken(&in, &var);
|
||||
if (tokenType != Token_Variable) {
|
||||
SCRIPT_PARSER_ERR("Acessing member with non-dynamic token");
|
||||
}
|
||||
else {
|
||||
setNextActionOperator(&lastFunc->operations, ActionGet, ActionExtraMemberName, var);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SCRIPT_PARSER_ERR("Member access on non-variable");
|
||||
}
|
||||
}
|
||||
else if (token == LeftBracket) {
|
||||
Function_t templateFunction = createEmptyFunction();
|
||||
vecAdd(&functionStack, templateFunction);
|
||||
|
||||
StackHistory_t functionHistory = History_Bracket;
|
||||
vecAdd(&stackHistoryHolder, functionHistory);
|
||||
continue;
|
||||
}
|
||||
else if (token == RightBracket) {
|
||||
if (*lastHistory == History_Bracket) {
|
||||
Function_t* bstack = popStackEntry(&functionStack);
|
||||
popStackEntry(&stackHistoryHolder);
|
||||
lastFunc = getStackEntry(&functionStack);
|
||||
|
||||
if (lastFunc) { // TODO: Add check for null deref
|
||||
lastOp = getStackEntry(&lastFunc->operations);
|
||||
}
|
||||
|
||||
if (lastOp && (lastOp->token == Variable || lastOp->token == BetweenBrackets || (lastOp->token == CallArgs && !isLastVarSet(lastOp) && !isLastVarCall(lastOp)))) {
|
||||
Function_t* newBStack = malloc(sizeof(Function_t));
|
||||
*newBStack = *bstack;
|
||||
setNextActionOperator(&lastFunc->operations, ActionCall, ActionExtraCallArgs, newBStack); // Maybe pass NULL if array is empty?
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
if (lastOp && isLastVarCall(lastOp)) {
|
||||
op.token = EquationSeperator;
|
||||
vecAdd(&lastFunc->operations, op);
|
||||
}
|
||||
|
||||
if (!countTokens(bstack, EquationSeperator)) {
|
||||
op.variable.betweenBrackets.data = bstack->operations.data;
|
||||
op.variable.betweenBrackets.len = bstack->operations.count;
|
||||
op.token = BetweenBrackets;
|
||||
}
|
||||
else {
|
||||
SCRIPT_PARSER_ERR("Priority brackets can only contain 1 argument");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
SCRIPT_PARSER_ERR(") without (");
|
||||
}
|
||||
}
|
||||
else if (token == LeftSquareBracket) {
|
||||
Function_t templateFunction = createEmptyFunction();
|
||||
vecAdd(&functionStack, templateFunction);
|
||||
|
||||
StackHistory_t functionHistory = History_Array;
|
||||
vecAdd(&stackHistoryHolder, functionHistory);
|
||||
continue;
|
||||
}
|
||||
else if (token == RightSquareBracket) {
|
||||
if (*lastHistory == History_Array) {
|
||||
Function_t* astack = popStackEntry(&functionStack);
|
||||
popStackEntry(&stackHistoryHolder);
|
||||
lastFunc = getStackEntry(&functionStack);
|
||||
|
||||
if (lastFunc) { // TODO: Add check for null deref
|
||||
lastOp = getStackEntry(&lastFunc->operations);
|
||||
}
|
||||
|
||||
if (lastOp && (lastOp->token == Variable || (lastOp->token == CallArgs && !isLastVarSet(lastOp)))) {
|
||||
if (!countTokens(astack, EquationSeperator)) {
|
||||
Function_t* newAStack = malloc(sizeof(Function_t));
|
||||
*newAStack = *astack;
|
||||
setNextActionOperator(&lastFunc->operations, ActionGet, ActionExtraArrayIndex, newAStack);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// We're just assuming that it's a new array lol
|
||||
|
||||
op.token = EquationSeperator;
|
||||
vecAdd(&lastFunc->operations, op);
|
||||
op.token = Variable;
|
||||
|
||||
|
||||
Variable_t a = createUnsolvedArrayVariable(astack);
|
||||
vecAdd(&staticVariableHolder, a);
|
||||
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
|
||||
op.variable = reference;
|
||||
|
||||
//gfx_printf("[FATAL] indexes cannot contain mutiple arguments");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: optimize output to a typed array, if possible
|
||||
Variable_t a = createUnsolvedArrayVariable(astack);
|
||||
vecAdd(&staticVariableHolder, a);
|
||||
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
|
||||
op.variable = reference;
|
||||
/*
|
||||
vecAdd(&lastFunc->operations, op);
|
||||
op.token = EquationSeperator;
|
||||
op.lineNumber = lineNumber;
|
||||
*/
|
||||
}
|
||||
}
|
||||
else {
|
||||
SCRIPT_PARSER_ERR("] without [");
|
||||
}
|
||||
}
|
||||
else if (token == Not) {
|
||||
notNext = !notNext;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
op.token = token;
|
||||
|
||||
for (u32 i = 0; i < tokenConvertionCount; i++) {
|
||||
if (token == tokenConvertions[i].token) {
|
||||
op.tokenStr = tokenConvertions[i].strToken;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (notNext) {
|
||||
op.not = 1;
|
||||
notNext = 0;
|
||||
}
|
||||
|
||||
vecAdd(&lastFunc->operations, op);
|
||||
}
|
||||
|
||||
if (functionStack.count != 1 || stackHistoryHolder.count != 1) {
|
||||
SCRIPT_PARSER_ERR("There seems to be an open bracket somewhere. EOF reached");
|
||||
}
|
||||
|
||||
|
||||
ParserRet_t parse = { .main = (*(Function_t*)getStackEntry(&functionStack)), .staticVarHolder = staticVariableHolder, .valid = 1 };
|
||||
|
||||
vecFree(functionStack);
|
||||
vecFree(stackHistoryHolder);
|
||||
scriptCurrentLine = 1;
|
||||
return parse;
|
||||
}
|
||||
|
||||
|
||||
void exitFunction(Operator_t* start, u32 len) {
|
||||
for (u32 i = 0; i < len; i++) {
|
||||
if (start[i].token == Variable) {
|
||||
if (start[i].variable.staticVariableOptionsUnion == 0) {
|
||||
FREE(start[i].variable.name);
|
||||
}
|
||||
|
||||
if (start[i].variable.staticVariableType == 2) {
|
||||
FREE(start[i].variable.stringType);
|
||||
}
|
||||
}
|
||||
else if (start[i].token == BetweenBrackets) {
|
||||
exitFunction(start[i].variable.betweenBrackets.data, start[i].variable.betweenBrackets.len);
|
||||
FREE(start[i].variable.betweenBrackets.data);
|
||||
}
|
||||
else if (start[i].token == CallArgs) {
|
||||
CallArgs_t* call = &start[i].callArgs;
|
||||
|
||||
while (call != NULL) {
|
||||
if (call->extraAction == ActionExtraArrayIndex) {
|
||||
Function_t* f = call->extra;
|
||||
exitFunction(f->operations.data, f->operations.count);
|
||||
vecFree(f->operations);
|
||||
FREE(f);
|
||||
}
|
||||
else if (call->extraAction == ActionExtraMemberName) {
|
||||
FREE(call->extra);
|
||||
}
|
||||
else if (call->extraAction == ActionExtraCallArgs) {
|
||||
Function_t* f = call->extra;
|
||||
exitFunction(f->operations.data, f->operations.count);
|
||||
vecFree(f->operations);
|
||||
FREE(f);
|
||||
}
|
||||
|
||||
CallArgs_t* nextCall = call->next;
|
||||
if (call != &start[i].callArgs) {
|
||||
FREE(call);
|
||||
}
|
||||
|
||||
call = nextCall;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exitStaticVars(Vector_t* v) {
|
||||
vecForEach(Variable_t*, staticVar, v) {
|
||||
if (staticVar->variableType == FunctionClass) {
|
||||
if (!staticVar->function.builtIn) {
|
||||
exitFunction(staticVar->function.function.operations.data, staticVar->function.function.operations.count);
|
||||
vecFree(staticVar->function.function.operations);
|
||||
}
|
||||
}
|
||||
else {
|
||||
freeVariableInternal(staticVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
TegraExplorer/source/script/parser.h
Normal file
16
TegraExplorer/source/script/parser.h
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
#pragma once
|
||||
#include "compat.h"
|
||||
|
||||
typedef struct {
|
||||
Function_t main;
|
||||
Vector_t staticVarHolder;
|
||||
u8 valid;
|
||||
} ParserRet_t;
|
||||
|
||||
#define SCRIPT_PARSER_ERR(message, ...) printScriptError(SCRIPT_PARSER_FATAL, message, ##__VA_ARGS__); return (ParserRet_t){0}
|
||||
|
||||
void exitStaticVars(Vector_t* v);
|
||||
void exitFunction(Operator_t* start, u32 len);
|
||||
ParserRet_t parseScript(char* in, u32 len);
|
||||
char* utils_copyStringSize(const char* in, int size);
|
||||
123
TegraExplorer/source/script/saveClass.c
Normal file
123
TegraExplorer/source/script/saveClass.c
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "saveClass.h"
|
||||
#include "compat.h"
|
||||
#include "intClass.h"
|
||||
#include "dictionaryClass.h"
|
||||
|
||||
u8 oneStringArgSave[] = {StringClass};
|
||||
u8 oneStrOneByteArrayArgSave[] = {StringClass, ByteArrayClass};
|
||||
u8 oneStringOneIntArgSave[] = {StringClass, IntClass};
|
||||
|
||||
ClassFunction(readFile){
|
||||
Variable_t *arg = (*args);
|
||||
save_data_file_ctx_t dataArc;
|
||||
if (!save_open_file(&caller->save->saveCtx, &dataArc, arg->string.value, OPEN_MODE_READ))
|
||||
return NULL;
|
||||
|
||||
u64 totalSize;
|
||||
save_data_file_get_size(&dataArc, &totalSize);
|
||||
|
||||
u8 *buff = malloc(totalSize);
|
||||
|
||||
save_data_file_read(&dataArc, &totalSize, 0, buff, totalSize);
|
||||
|
||||
Variable_t a = {.variableType = ByteArrayClass};
|
||||
a.solvedArray.vector = vecFromArray(buff, totalSize, 1);
|
||||
return copyVariableToPtr(a);
|
||||
}
|
||||
|
||||
ClassFunction(writeFile){
|
||||
Variable_t *arg = (*args);
|
||||
save_data_file_ctx_t dataArc;
|
||||
if (!save_open_file(&caller->save->saveCtx, &dataArc, arg->string.value, OPEN_MODE_WRITE))
|
||||
return newIntVariablePtr(1);
|
||||
|
||||
u64 outBytes = 0;
|
||||
if (!save_data_file_write(&dataArc, &outBytes, 0, args[1]->solvedArray.vector.data, args[1]->solvedArray.vector.count)){
|
||||
return newIntVariablePtr(3);
|
||||
};
|
||||
|
||||
if (outBytes != args[1]->solvedArray.vector.count){
|
||||
return newIntVariablePtr(4);
|
||||
}
|
||||
|
||||
return newIntVariablePtr(0);
|
||||
}
|
||||
|
||||
ClassFunction(getFiles){
|
||||
Variable_t* resPtr = newIntVariablePtr(0);
|
||||
Variable_t ret = {.variableType = DictionaryClass, .dictionary.vector = newVec(sizeof(Dict_t), 4)};
|
||||
addVariableToDict(&ret, "result", resPtr);
|
||||
|
||||
save_data_directory_ctx_t ctx;
|
||||
if (!save_open_directory(&caller->save->saveCtx, &ctx, "/", OPEN_DIR_MODE_ALL)){
|
||||
resPtr->integer.value = 1;
|
||||
return copyVariableToPtr(ret);
|
||||
}
|
||||
|
||||
u64 entryCount = 0;
|
||||
if (!save_data_directory_get_entry_count(&ctx, &entryCount)){
|
||||
resPtr->integer.value = 2;
|
||||
return copyVariableToPtr(ret);
|
||||
}
|
||||
|
||||
directory_entry_t* entries = malloc(sizeof(directory_entry_t) * entryCount);
|
||||
u64 entryCountDirRead = 0;
|
||||
if (!save_data_directory_read(&ctx, &entryCountDirRead, entries, entryCount)){
|
||||
resPtr->integer.value = 3;
|
||||
|
||||
return copyVariableToPtr(ret);
|
||||
}
|
||||
|
||||
Variable_t fileNamesArray = {.variableType = StringArrayClass, .solvedArray.vector = newVec(sizeof(char*), 0)};
|
||||
Variable_t dirNamesArray = {.variableType = StringArrayClass, .solvedArray.vector = newVec(sizeof(char*), 0)};
|
||||
Variable_t fileSizeArray = {.variableType = IntArrayClass, .solvedArray.vector = newVec(sizeof(s64), 0)};
|
||||
|
||||
for (int i = 0; i < entryCountDirRead; i++){
|
||||
char *add = CpyStr(entries[i].name);
|
||||
if (entries[i].type == DIR_ENT_TYPE_FILE){
|
||||
vecAdd(&fileNamesArray.solvedArray.vector, add);
|
||||
s64 fileSize = entries[i].size;
|
||||
vecAdd(&fileSizeArray.solvedArray.vector, fileSize);
|
||||
}
|
||||
else {
|
||||
vecAdd(&dirNamesArray.solvedArray.vector, add);
|
||||
}
|
||||
}
|
||||
|
||||
free(entries);
|
||||
|
||||
addVariableToDict(&ret, "files", copyVariableToPtr(fileNamesArray));
|
||||
addVariableToDict(&ret, "folders", copyVariableToPtr(dirNamesArray));
|
||||
addVariableToDict(&ret, "fileSizes", copyVariableToPtr(fileSizeArray));
|
||||
|
||||
return copyVariableToPtr(ret);
|
||||
}
|
||||
|
||||
ClassFunction(saveClassCommit){
|
||||
return newIntVariablePtr(!save_commit(&caller->save->saveCtx));
|
||||
}
|
||||
|
||||
ClassFunction(writeFileSize) {
|
||||
Variable_t* arg = (*args);
|
||||
save_data_file_ctx_t dataArc;
|
||||
if (!save_open_file(&caller->save->saveCtx, &dataArc, arg->string.value, OPEN_MODE_WRITE))
|
||||
return newIntVariablePtr(1);
|
||||
|
||||
if (!save_data_file_set_size(&dataArc, args[1]->integer.value)) {
|
||||
return newIntVariablePtr(2);
|
||||
};
|
||||
|
||||
return newIntVariablePtr(0);
|
||||
}
|
||||
|
||||
ClassFunctionTableEntry_t saveFunctions[] = {
|
||||
{"read", readFile, 1, oneStringArgSave},
|
||||
{"write", writeFile, 2, oneStrOneByteArrayArgSave},
|
||||
//{"readdir", getFiles, 1, oneStringArgSave}, // Seems broken?
|
||||
{"commit", saveClassCommit, 0, 0},
|
||||
{"resize", writeFileSize, 2, oneStringOneIntArgSave},
|
||||
};
|
||||
|
||||
Variable_t getSaveMember(Variable_t* var, char* memberName) {
|
||||
return getGenericFunctionMember(var, memberName, saveFunctions, ARRAY_SIZE(saveFunctions));
|
||||
}
|
||||
4
TegraExplorer/source/script/saveClass.h
Normal file
4
TegraExplorer/source/script/saveClass.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
|
||||
Variable_t getSaveMember(Variable_t* var, char* memberName);
|
||||
21
TegraExplorer/source/script/scriptError.c
Normal file
21
TegraExplorer/source/script/scriptError.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "scriptError.h"
|
||||
#include "compat.h"
|
||||
#include <stdarg.h>
|
||||
#include "../hid/hid.h"
|
||||
|
||||
s64 scriptCurrentLine;
|
||||
u8 scriptLastError = 0;
|
||||
|
||||
void printScriptError(u8 errLevel, char* message, ...) {
|
||||
va_list args;
|
||||
scriptLastError = errLevel;
|
||||
va_start(args, message);
|
||||
gfx_printf("\n\n[%s] ", (errLevel == SCRIPT_FATAL) ? "FATAL" : (errLevel == SCRIPT_PARSER_FATAL) ? "PARSE_FATAL" : "WARN");
|
||||
gfx_vprintf(message, args);
|
||||
if (errLevel < SCRIPT_WARN)
|
||||
gfx_printf("\nError occured on or near line %d\n", (u32)scriptCurrentLine);
|
||||
va_end(args);
|
||||
#ifndef WIN32
|
||||
hidWait();
|
||||
#endif
|
||||
}
|
||||
19
TegraExplorer/source/script/scriptError.h
Normal file
19
TegraExplorer/source/script/scriptError.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
|
||||
enum {
|
||||
SCRIPT_FATAL = 0,
|
||||
SCRIPT_PARSER_FATAL,
|
||||
SCRIPT_WARN,
|
||||
SCRIPT_LEXER_FATAL,
|
||||
SCRIPT_BREAK,
|
||||
};
|
||||
|
||||
extern s64 scriptCurrentLine;
|
||||
extern u8 scriptLastError;
|
||||
|
||||
void printScriptError(u8 errLevel, char* message, ...);
|
||||
|
||||
#define SCRIPT_FATAL_ERR(message, ...) printScriptError(SCRIPT_FATAL, message, ##__VA_ARGS__); return NULL
|
||||
#define SCRIPT_WARN_ERR(message, ...) printScriptError(SCRIPT_WARN, message, ##__VA_ARGS__)
|
||||
|
||||
637
TegraExplorer/source/script/standardLibrary.c
Normal file
637
TegraExplorer/source/script/standardLibrary.c
Normal file
@@ -0,0 +1,637 @@
|
||||
#include "model.h"
|
||||
#include "compat.h"
|
||||
#include "genericClass.h"
|
||||
#include "eval.h"
|
||||
#include "garbageCollector.h"
|
||||
#include "intClass.h"
|
||||
#include "standardLibrary.h"
|
||||
#include "scriptError.h"
|
||||
#include <string.h>
|
||||
#include "dictionaryClass.h"
|
||||
#include "StringClass.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#include "../storage/mountmanager.h"
|
||||
#include "../keys/keys.h"
|
||||
#include "../fs/readers/folderReader.h"
|
||||
#include "../fs/fscopy.h"
|
||||
#include <mem/heap.h>
|
||||
#include "../keys/nca.h"
|
||||
#include "../hid/hid.h"
|
||||
#include "../gfx/menu.h"
|
||||
#include "../gfx/gfxutils.h"
|
||||
#include "../tegraexplorer/tconf.h"
|
||||
#include "../storage/emummc.h"
|
||||
#include <utils/util.h>
|
||||
#include "../fs/fsutils.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include "../storage/emmcfile.h"
|
||||
#include <soc/fuse.h>
|
||||
#endif
|
||||
// Takes [int, function]. Returns elseable.
|
||||
ClassFunction(stdIf) {
|
||||
s64 value = getIntValue(args[0]);
|
||||
|
||||
if (value) {
|
||||
Variable_t* res = genericCallDirect(args[1], NULL, 0);
|
||||
if (res == NULL)
|
||||
return NULL;
|
||||
|
||||
removePendingReference(res);
|
||||
}
|
||||
|
||||
Variable_t* ret = newIntVariablePtr(value);
|
||||
ret->variableType = ElseClass;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Takes [int]. Returns empty.
|
||||
ClassFunction(stdSleep)
|
||||
{
|
||||
s64 value = getIntValue(args[0]);
|
||||
|
||||
if (value)
|
||||
{
|
||||
msleep(value);
|
||||
}
|
||||
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
// Takes [function, function]. Returns empty. Works by evaling the first function and running the 2nd if true.
|
||||
ClassFunction(stdWhile) {
|
||||
Variable_t* result = eval(args[0]->function.function.operations.data, args[0]->function.function.operations.count, 1);
|
||||
if (result == NULL || result->variableType != IntClass)
|
||||
return NULL;
|
||||
|
||||
while (result->integer.value) {
|
||||
removePendingReference(result);
|
||||
Variable_t* res = genericCallDirect(args[1], NULL, 0);
|
||||
if (res == NULL) {
|
||||
if (scriptLastError == SCRIPT_BREAK) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
removePendingReference(res);
|
||||
|
||||
result = eval(args[0]->function.function.operations.data, args[0]->function.function.operations.count, 1);
|
||||
if (result == NULL || result->variableType != IntClass)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
removePendingReference(result);
|
||||
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
// Takes [???]. Returns empty. Works by calling .print on every argument
|
||||
ClassFunction(stdPrint) {
|
||||
for (int i = 0; i < argsLen; i++) {
|
||||
Variable_t* res = callMemberFunctionDirect(args[i], "print", NULL, 0);
|
||||
if (res == NULL)
|
||||
return NULL;
|
||||
if (i + 1 != argsLen)
|
||||
gfx_putc(' ');
|
||||
}
|
||||
|
||||
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
// Takes [???]. Returns empty. Calls stdPrint
|
||||
ClassFunction(stdPrintLn) {
|
||||
stdPrint(caller, args, argsLen);
|
||||
gfx_printf("\n");
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
// Takes none. Returns none. Returning NULL will cause a cascade of errors and will exit runtime
|
||||
ClassFunction(stdExit) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Takes none. Returns none. See stdExit. stdWhile and array.foreach look for SCRIPT_BREAK and break when seen.
|
||||
ClassFunction(stdBreak) {
|
||||
scriptLastError = SCRIPT_BREAK;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Takes none. Returns empty dictionary.
|
||||
ClassFunction(stdDict) {
|
||||
Variable_t a = { 0 };
|
||||
a.variableType = DictionaryClass;
|
||||
a.dictionary.vector = newVec(sizeof(Dict_t), 0);
|
||||
return copyVariableToPtr(a);
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
|
||||
int mountMmc(u8 mmc, char *part){
|
||||
if (connectMMC(mmc))
|
||||
return 1;
|
||||
|
||||
if (mountMMCPart(part).err)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Takes [str]. Returns int (0=success). str=partition to mount
|
||||
ClassFunction(stdMountSysmmc){
|
||||
return newIntVariablePtr(mountMmc(MMC_CONN_EMMC, args[0]->string.value));
|
||||
}
|
||||
|
||||
ClassFunction(stdMountEmummc){
|
||||
if (!emu_cfg.enabled){
|
||||
SCRIPT_FATAL_ERR("emummc is not enabled");
|
||||
}
|
||||
|
||||
return newIntVariablePtr(mountMmc(MMC_CONN_EMUMMC, args[0]->string.value));
|
||||
}
|
||||
|
||||
// Takes [str]. Returns int (0=success) str=path to save
|
||||
ClassFunction(stdMountSave){
|
||||
|
||||
Variable_t *arg = (*args);
|
||||
Variable_t var = {.variableType = SaveClass};
|
||||
SaveClass_t* save = calloc(1, sizeof(SaveClass_t));
|
||||
if (f_open(&save->saveFile, arg->string.value, FA_READ | FA_WRITE))
|
||||
return NULL;
|
||||
save_init(&save->saveCtx, &save->saveFile, dumpedKeys.save_mac_key, 0);
|
||||
if (!save_process(&save->saveCtx))
|
||||
return NULL;
|
||||
|
||||
var.save = save;
|
||||
return copyVariableToPtr(var);
|
||||
}
|
||||
|
||||
// Takes [int, int, int]. Returns empty. 0: posX, 1: posY, 2: hexColor
|
||||
ClassFunction(stdSetPixel) {
|
||||
u32 color = getIntValue(args[2]);
|
||||
gfx_set_pixel_horz(args[0]->integer.value, args[1]->integer.value, color);
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunction(stdSetPixels){
|
||||
gfx_box(getIntValue(args[0]), getIntValue(args[1]), getIntValue(args[2]), getIntValue(args[3]), (u32)getIntValue(args[4]));
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunction(stdSetPrintPos){
|
||||
if (getIntValue(args[0]) > 0 && getIntValue(args[1]) > 0){
|
||||
gfx_con_setpos((getIntValue(args[0]) % 78) * 16, (getIntValue(args[1]) % 42) * 16);
|
||||
}
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
// Takes [str]. Returns empty. str: path to dir
|
||||
ClassFunction(stdReadDir){
|
||||
Variable_t* resPtr = newIntVariablePtr(0);
|
||||
Variable_t ret = {.variableType = DictionaryClass, .dictionary.vector = newVec(sizeof(Dict_t), 4)};
|
||||
addVariableToDict(&ret, "result", resPtr);
|
||||
|
||||
Variable_t fileNamesArray = {.variableType = StringArrayClass, .solvedArray.vector = newVec(sizeof(char*), 0)};
|
||||
Variable_t dirNamesArray = {.variableType = StringArrayClass, .solvedArray.vector = newVec(sizeof(char*), 0)};
|
||||
Variable_t fileSizeArray = {.variableType = IntArrayClass, .solvedArray.vector = newVec(sizeof(s64), 0)};
|
||||
|
||||
DIR dir;
|
||||
if ((resPtr->integer.value = f_opendir(&dir, args[0]->string.value))){
|
||||
return copyVariableToPtr(ret);
|
||||
}
|
||||
|
||||
FILINFO fno;
|
||||
while (!(resPtr->integer.value = f_readdir(&dir, &fno)) && fno.fname[0]){
|
||||
char *name = CpyStr(fno.fname);
|
||||
if (fno.fattrib & AM_DIR){
|
||||
vecAdd(&dirNamesArray.solvedArray.vector, name);
|
||||
}
|
||||
else {
|
||||
vecAdd(&fileNamesArray.solvedArray.vector, name);
|
||||
s64 size = fno.fsize;
|
||||
vecAdd(&fileSizeArray.solvedArray.vector, size);
|
||||
}
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
|
||||
addVariableToDict(&ret, "files", copyVariableToPtr(fileNamesArray));
|
||||
addVariableToDict(&ret, "folders", copyVariableToPtr(dirNamesArray));
|
||||
addVariableToDict(&ret, "fileSizes", copyVariableToPtr(fileSizeArray));
|
||||
|
||||
return copyVariableToPtr(ret);
|
||||
}
|
||||
|
||||
char *abxyNames[] = {
|
||||
"y",
|
||||
"x",
|
||||
"b",
|
||||
"a"
|
||||
};
|
||||
|
||||
char *ulrdNames[] = {
|
||||
"down",
|
||||
"up",
|
||||
"right",
|
||||
"left",
|
||||
};
|
||||
|
||||
char *powNames[] = {
|
||||
"power",
|
||||
"volplus",
|
||||
"volminus",
|
||||
};
|
||||
|
||||
Variable_t *hidToVar(u32 raw){
|
||||
Variable_t ret = {.variableType = DictionaryClass, .dictionary.vector = newVec(sizeof(Dict_t), 9)};
|
||||
addIntToDict(&ret, "raw", raw);
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(abxyNames); i++){
|
||||
addIntToDict(&ret, abxyNames[i], raw & 0x1);
|
||||
raw >>= 1;
|
||||
}
|
||||
|
||||
raw >>= 12;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(ulrdNames); i++){
|
||||
addIntToDict(&ret, ulrdNames[i], raw & 0x1);
|
||||
raw >>= 1;
|
||||
}
|
||||
|
||||
raw >>= 4;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(powNames); i++){
|
||||
addIntToDict(&ret, powNames[i], raw & 0x1);
|
||||
raw >>= 1;
|
||||
}
|
||||
|
||||
return copyVariableToPtr(ret);
|
||||
}
|
||||
|
||||
ClassFunction(stdRead){
|
||||
Input_t *i = hidRead();
|
||||
u32 raw = i->buttons;
|
||||
return hidToVar(raw);
|
||||
}
|
||||
|
||||
// Takes [int]. Returns dict[a,b,x,y,down,up,right,left,power,volplus,volminus,raw]. int: mask for hidWaitMask
|
||||
ClassFunction(stdPauseMask){
|
||||
Input_t *i = hidWaitMask((u32)getIntValue(*args));
|
||||
u32 raw = i->buttons;
|
||||
return hidToVar(raw);
|
||||
}
|
||||
|
||||
// Takes none. Returns dict (same as stdPauseMask).
|
||||
ClassFunction(stdPause){
|
||||
Variable_t a = {.integer.value = 0xFFFFFFFF};
|
||||
Variable_t *b = &a;
|
||||
return stdPauseMask(caller, &b, 1);
|
||||
}
|
||||
|
||||
// Takes [str, str]. Returns int (0=success). 0: src path, 1: dst path
|
||||
ClassFunction(stdFileCopy){
|
||||
ErrCode_t e = FileCopy(args[0]->string.value, args[1]->string.value, COPY_MODE_PRINT);
|
||||
return newIntVariablePtr(e.err);
|
||||
}
|
||||
|
||||
ClassFunction(stdMkdir){
|
||||
return newIntVariablePtr(f_mkdir(args[0]->string.value));
|
||||
}
|
||||
|
||||
// Broken????
|
||||
ClassFunction(stdGetMemUsage){
|
||||
heap_monitor_t mon;
|
||||
heap_monitor(&mon, false);
|
||||
Dict_t a = {.name = CpyStr("used"), .var = newIntVariablePtr((s64)mon.used)};
|
||||
Dict_t b = {.name = CpyStr("total"), .var = newIntVariablePtr((s64)mon.total)};
|
||||
Variable_t ret = {.variableType = DictionaryClass, .dictionary.vector = newVec(sizeof(Dict_t), 2)};
|
||||
vecAdd(&ret.dictionary.vector, a);
|
||||
vecAdd(&ret.dictionary.vector, b);
|
||||
return copyVariableToPtr(ret);
|
||||
}
|
||||
|
||||
// Takes [int]. Returns empty. int: hex color
|
||||
ClassFunction(stdColor){
|
||||
gfx_con_setcol((u32)getIntValue(*args) | 0xFF000000, gfx_con.fillbg, gfx_con.bgcol);
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
// Takes [str]. Returns int. str: path to nca
|
||||
ClassFunction(stdGetNcaType){
|
||||
int type = GetNcaType(args[0]->string.value);
|
||||
return newIntVariablePtr(type);
|
||||
}
|
||||
|
||||
// Takes [str[], int, int[]]. Returns int (index). str[]: names of entries, int: starting index, int[]: colors & options. The 3rd argument is optional
|
||||
ClassFunction(stdMenuFull){
|
||||
if (argsLen > 2){
|
||||
if (args[2]->solvedArray.vector.count < args[0]->solvedArray.vector.count){
|
||||
SCRIPT_FATAL_ERR("invalid menu args");
|
||||
}
|
||||
}
|
||||
|
||||
Vector_t v = newVec(sizeof(MenuEntry_t), args[0]->solvedArray.vector.count);
|
||||
|
||||
vecDefArray(char**, menuEntryNames, args[0]->solvedArray.vector);
|
||||
vecDefArray(s64*, menuEntryOptions, args[2]->solvedArray.vector);
|
||||
|
||||
for (int i = 0; i < args[0]->solvedArray.vector.count; i++){
|
||||
MenuEntry_t a = {.name = menuEntryNames[i]};
|
||||
if (argsLen > 2){
|
||||
u32 options = (u32)menuEntryOptions[i];
|
||||
if (options & BIT(26)){
|
||||
a.icon = 128;
|
||||
}
|
||||
else if (options & BIT(27)){
|
||||
a.icon = 127;
|
||||
}
|
||||
|
||||
a.optionUnion = options;
|
||||
}
|
||||
else {
|
||||
a.optionUnion = COLORTORGB(COLOR_WHITE);
|
||||
}
|
||||
|
||||
vecAdd(&v, a);
|
||||
}
|
||||
|
||||
u32 x=0,y=0;
|
||||
gfx_con_getpos(&x,&y);
|
||||
int res = newMenu(&v, getIntValue(args[1]), ScreenDefaultLenX - ((x + 1) / 16), 40 - ((y + 1) / 16) - 1, ENABLEB | ALWAYSREDRAW, 0);
|
||||
vecFree(v);
|
||||
return newIntVariablePtr(res);
|
||||
}
|
||||
|
||||
ClassFunction(stdHasEmu){
|
||||
return newIntVariablePtr(emu_cfg.enabled);
|
||||
}
|
||||
|
||||
ClassFunction(stdClear){
|
||||
gfx_clearscreen();
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunction(stdRmDir){
|
||||
return newIntVariablePtr(FolderDelete(args[0]->string.value).err);
|
||||
}
|
||||
|
||||
ClassFunction(stdGetMs){
|
||||
return newIntVariablePtr(get_tmr_ms());
|
||||
}
|
||||
|
||||
ClassFunction(stdFileExists){
|
||||
return newIntVariablePtr(FileExists(args[0]->string.value));
|
||||
}
|
||||
|
||||
ClassFunction(stdFileDel){
|
||||
return newIntVariablePtr(f_unlink(args[0]->string.value));
|
||||
}
|
||||
|
||||
ClassFunction(stdCopyDir){
|
||||
return newIntVariablePtr(FolderCopy(args[0]->string.value, args[1]->string.value).err);
|
||||
}
|
||||
|
||||
ClassFunction(stdFileMove){
|
||||
return newIntVariablePtr(f_rename(args[0]->string.value, args[1]->string.value));
|
||||
}
|
||||
|
||||
ClassFunction(stdFileRead){
|
||||
u32 fSize = 0;
|
||||
u8 *buff = sd_file_read(args[0]->string.value, &fSize);
|
||||
if (buff == NULL){
|
||||
SCRIPT_FATAL_ERR("Failed to read file");
|
||||
}
|
||||
|
||||
Vector_t vec = vecFromArray(buff, fSize, sizeof(u8));
|
||||
Variable_t v = {.variableType = ByteArrayClass, .solvedArray.vector = vec};
|
||||
return copyVariableToPtr(v);
|
||||
}
|
||||
|
||||
ClassFunction(stdFileReadSize) {
|
||||
u32 fSize = 0;
|
||||
u8* buff = sd_file_read(args[0]->string.value, &fSize);
|
||||
if (buff == NULL) {
|
||||
SCRIPT_FATAL_ERR("Failed to read file");
|
||||
}
|
||||
|
||||
return newIntVariablePtr(fSize);
|
||||
}
|
||||
|
||||
ClassFunction(stdFileWrite){
|
||||
return newIntVariablePtr(sd_save_to_file(args[1]->solvedArray.vector.data, args[1]->solvedArray.vector.count, args[0]->string.value));
|
||||
}
|
||||
|
||||
extern int launch_payload(char *path);
|
||||
|
||||
ClassFunction(stdLaunchPayload){
|
||||
return newIntVariablePtr(launch_payload(args[0]->string.value));
|
||||
}
|
||||
|
||||
int emmcFile(char *sdFile, char *sysPart, u8 mmc, u8 write){
|
||||
|
||||
if (!emu_cfg.enabled && mmc == MMC_CONN_EMUMMC){
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (connectMMC(mmc))
|
||||
return 1;
|
||||
|
||||
return DumpOrWriteEmmcPart(sdFile, sysPart, write, 1).err;
|
||||
}
|
||||
|
||||
ClassFunction(stdEmmcFileRead){
|
||||
return newIntVariablePtr(emmcFile(args[0]->string.value, args[1]->string.value, MMC_CONN_EMMC, 0));
|
||||
}
|
||||
|
||||
ClassFunction(stdEmmcFileWrite){
|
||||
return newIntVariablePtr(emmcFile(args[0]->string.value, args[1]->string.value, MMC_CONN_EMMC, 1));
|
||||
}
|
||||
|
||||
ClassFunction(stdEmummcFileRead){
|
||||
return newIntVariablePtr(emmcFile(args[0]->string.value, args[1]->string.value, MMC_CONN_EMUMMC, 0));
|
||||
}
|
||||
|
||||
ClassFunction(stdEmummcFileWrite){
|
||||
return newIntVariablePtr(emmcFile(args[0]->string.value, args[1]->string.value, MMC_CONN_EMUMMC, 1));
|
||||
}
|
||||
|
||||
ClassFunction(stdCombinePaths){
|
||||
if (argsLen < 2 || args[0]->variableType != StringClass){
|
||||
SCRIPT_FATAL_ERR("stdCombinePaths needs 2+ args");
|
||||
}
|
||||
|
||||
char *res = CpyStr(args[0]->string.value);
|
||||
|
||||
for (int i = 1; i < argsLen; i++){
|
||||
if (args[i]->variableType != StringClass){
|
||||
SCRIPT_FATAL_ERR("stdCombinePaths needs 2+ args");
|
||||
}
|
||||
|
||||
char *temp = CombinePaths(res, args[i]->string.value);
|
||||
free(res);
|
||||
res = temp;
|
||||
}
|
||||
|
||||
return newStringVariablePtr(res, 0, 1);
|
||||
}
|
||||
|
||||
ClassFunction(stdEscPaths){
|
||||
return newStringVariablePtr(EscapeFolder(args[0]->string.value), 0, 1);
|
||||
}
|
||||
|
||||
ClassFunction(stdGetCwd){
|
||||
return newStringVariablePtr(CpyStr(TConf.scriptCWD), 0, 1);
|
||||
}
|
||||
|
||||
ClassFunction(stdPower){
|
||||
power_set_state(MAX(0, (getIntValue(args[0]) % POWER_OFF_REBOOT)));
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunction(stdIsPatched){
|
||||
return newIntVariablePtr(fuse_check_patched_rcm());
|
||||
}
|
||||
|
||||
ClassFunction(stdHwType){
|
||||
return newIntVariablePtr(fuse_read_hw_type());
|
||||
}
|
||||
|
||||
#else
|
||||
#define STUBBED(name) ClassFunction(name) { return newIntVariablePtr(0); }
|
||||
|
||||
STUBBED(stdMountSysmmc)
|
||||
STUBBED(stdMountSave)
|
||||
STUBBED(stdSetPixel)
|
||||
STUBBED(stdReadDir)
|
||||
STUBBED(stdFileCopy)
|
||||
STUBBED(stdMkdir)
|
||||
STUBBED(stdGetMemUsage)
|
||||
STUBBED(stdGetNcaType)
|
||||
STUBBED(stdPause)
|
||||
STUBBED(stdRead)
|
||||
STUBBED(stdPauseMask)
|
||||
STUBBED(stdColor)
|
||||
STUBBED(stdMenuFull)
|
||||
STUBBED(stdMountEmummc)
|
||||
STUBBED(stdHasEmu)
|
||||
STUBBED(stdGetMs)
|
||||
STUBBED(stdClear)
|
||||
STUBBED(stdRmDir)
|
||||
STUBBED(stdFileExists)
|
||||
STUBBED(stdFileDel)
|
||||
STUBBED(stdCopyDir)
|
||||
STUBBED(stdFileMove)
|
||||
STUBBED(stdLaunchPayload)
|
||||
STUBBED(stdFileWrite)
|
||||
STUBBED(stdFileRead)
|
||||
STUBBED(stdFileReadSize)
|
||||
STUBBED(stdCombinePaths)
|
||||
STUBBED(stdEmmcFileWrite)
|
||||
STUBBED(stdEmmcFileRead)
|
||||
STUBBED(stdEmummcFileRead)
|
||||
STUBBED(stdEmummcFileWrite)
|
||||
STUBBED(stdEscPaths)
|
||||
STUBBED(stdGetCwd)
|
||||
STUBBED(stdPower)
|
||||
STUBBED(stdSetPrintPos)
|
||||
STUBBED(stdSetPixels)
|
||||
STUBBED(stdIsPatched)
|
||||
STUBBED(stdHwType)
|
||||
#endif
|
||||
|
||||
u8 oneIntoneFunction[] = { IntClass, FunctionClass };
|
||||
u8 doubleFunctionClass[] = { FunctionClass, FunctionClass };
|
||||
u8 threeIntsStd[] = { IntClass, IntClass, IntClass, IntClass, IntClass };
|
||||
u8 twoStringArgStd[] = {StringClass, StringClass};
|
||||
u8 menuArgsStd[] = {StringArrayClass, IntClass, IntArrayClass};
|
||||
u8 oneStringOneByteArrayStd[] = {StringClass, ByteArrayClass};
|
||||
|
||||
ClassFunctionTableEntry_t standardFunctionDefenitions[] = {
|
||||
// Flow control
|
||||
{"if", stdIf, 2, oneIntoneFunction},
|
||||
{"while", stdWhile, 2, doubleFunctionClass},
|
||||
{"exit", stdExit, 0, 0},
|
||||
{"break", stdBreak, 0, 0},
|
||||
|
||||
// Class creation
|
||||
{"readsave", stdMountSave, 1, twoStringArgStd},
|
||||
{"dict", stdDict, 0, 0},
|
||||
|
||||
// Utils
|
||||
{"print", stdPrint, VARARGCOUNT, 0},
|
||||
{"println", stdPrintLn, VARARGCOUNT, 0},
|
||||
{"printpos", stdSetPrintPos, 2, threeIntsStd},
|
||||
{"setpixel", stdSetPixel, 3, threeIntsStd},
|
||||
{"setpixels", stdSetPixels, 5, threeIntsStd},
|
||||
{"emu", stdHasEmu, 0, 0},
|
||||
{"cwd", stdGetCwd, 0, 0},
|
||||
{"clear", stdClear, 0, 0},
|
||||
{"timer", stdGetMs, 0, 0},
|
||||
{"pause", stdPauseMask, 1, threeIntsStd},
|
||||
{"pause", stdPause, 0, 0},
|
||||
{"hidread", stdRead, 0, 0},
|
||||
{"color", stdColor, 1, threeIntsStd},
|
||||
{"menu", stdMenuFull, 3, menuArgsStd},
|
||||
{"menu", stdMenuFull, 2, menuArgsStd},
|
||||
{"power", stdPower, 1, threeIntsStd},
|
||||
{"sleep", stdSleep, 1, threeIntsStd},
|
||||
|
||||
// System
|
||||
{"mountsys", stdMountSysmmc, 1, twoStringArgStd},
|
||||
{"mountemu", stdMountEmummc, 1, twoStringArgStd},
|
||||
{"ncatype", stdGetNcaType, 1, twoStringArgStd},
|
||||
{"emmcread", stdEmmcFileRead, 2, twoStringArgStd},
|
||||
{"emmcwrite", stdEmmcFileWrite, 2, twoStringArgStd},
|
||||
{"emummcread", stdEmummcFileRead, 2, twoStringArgStd},
|
||||
{"emummcwrite", stdEmummcFileWrite, 2, twoStringArgStd},
|
||||
{"fuse_patched", stdIsPatched, 0, 0},
|
||||
{"fuse_hwtype", stdHwType, 0, 0},
|
||||
|
||||
// FileSystem
|
||||
// Dir
|
||||
{"readdir", stdReadDir, 1, twoStringArgStd},
|
||||
{"deldir", stdRmDir, 1, twoStringArgStd},
|
||||
{"mkdir", stdMkdir, 1, twoStringArgStd},
|
||||
{"copydir", stdCopyDir, 2, twoStringArgStd},
|
||||
|
||||
// File
|
||||
{"copyfile", stdFileCopy, 2, twoStringArgStd},
|
||||
{"movefile", stdFileMove, 2, twoStringArgStd},
|
||||
{"delfile", stdFileDel, 1, twoStringArgStd},
|
||||
{"readfile", stdFileRead, 1, twoStringArgStd},
|
||||
{"getfilesize", stdFileReadSize, 1, twoStringArgStd},
|
||||
{"writefile", stdFileWrite, 2, oneStringOneByteArrayStd},
|
||||
|
||||
// Utils
|
||||
{"fsexists", stdFileExists, 1, twoStringArgStd},
|
||||
{"payload", stdLaunchPayload, 1, twoStringArgStd},
|
||||
{"combinepath", stdCombinePaths, VARARGCOUNT, 0},
|
||||
{"escapepath", stdEscPaths, 1, twoStringArgStd},
|
||||
};
|
||||
|
||||
ClassFunctionTableEntry_t* searchStdLib(char* funcName, u8 *len) {
|
||||
u8 lenInternal = 0;
|
||||
*len = 0;
|
||||
ClassFunctionTableEntry_t *ret = NULL;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(standardFunctionDefenitions); i++) {
|
||||
if (!strcmp(funcName, standardFunctionDefenitions[i].name)) {
|
||||
lenInternal++;
|
||||
if (ret == NULL){
|
||||
ret = &standardFunctionDefenitions[i];
|
||||
}
|
||||
}
|
||||
else if (lenInternal != 0){
|
||||
*len = lenInternal;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
*len = lenInternal;
|
||||
return ret;
|
||||
}
|
||||
4
TegraExplorer/source/script/standardLibrary.h
Normal file
4
TegraExplorer/source/script/standardLibrary.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
|
||||
ClassFunctionTableEntry_t* searchStdLib(char* funcName, u8 *len);
|
||||
193
TegraExplorer/source/script/unsolvedArrayClass.c
Normal file
193
TegraExplorer/source/script/unsolvedArrayClass.c
Normal file
@@ -0,0 +1,193 @@
|
||||
#include "unsolvedArrayClass.h"
|
||||
#include "eval.h"
|
||||
#include "compat.h"
|
||||
#include "intClass.h"
|
||||
#include "scriptError.h"
|
||||
#include "garbageCollector.h"
|
||||
#include <string.h>
|
||||
|
||||
Variable_t* solveArray(Variable_t *unsolvedArray) {
|
||||
if (unsolvedArray->unsolvedArray.operations.count <= 0) {
|
||||
return copyVariableToPtr(*unsolvedArray);
|
||||
// Return empty unsolved array that turns into a solved array once something is put into it
|
||||
}
|
||||
|
||||
int lasti = 0;
|
||||
Operator_t* ops = unsolvedArray->unsolvedArray.operations.data;
|
||||
u8 type = None;
|
||||
Vector_t v = { 0 };
|
||||
|
||||
|
||||
for (int i = 0; i < unsolvedArray->unsolvedArray.operations.count; i++) {
|
||||
if (ops[i].token == EquationSeperator || i + 1 == unsolvedArray->unsolvedArray.operations.count) {
|
||||
if (i + 1 == unsolvedArray->unsolvedArray.operations.count)
|
||||
i++;
|
||||
|
||||
Variable_t* var = eval(&ops[lasti], i - lasti, 1);
|
||||
if (var == NULL)
|
||||
return NULL;
|
||||
|
||||
if (v.data == NULL) {
|
||||
if (var->variableType == IntClass) {
|
||||
v = newVec(sizeof(s64), 1);
|
||||
type = IntClass;
|
||||
}
|
||||
else if (var->variableType == StringClass) {
|
||||
v = newVec(sizeof(char*), 1);
|
||||
type = StringClass;
|
||||
}
|
||||
else {
|
||||
SCRIPT_FATAL_ERR("Unknown array type");
|
||||
}
|
||||
}
|
||||
|
||||
if (var->variableType == type) {
|
||||
if (var->variableType == IntClass) {
|
||||
vecAdd(&v, var->integer.value);
|
||||
}
|
||||
else {
|
||||
char* str = CpyStr(var->string.value);
|
||||
vecAdd(&v, str);
|
||||
}
|
||||
}
|
||||
else {
|
||||
SCRIPT_FATAL_ERR("Variable type is not the same as array type");
|
||||
}
|
||||
|
||||
removePendingReference(var);
|
||||
|
||||
lasti = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Variable_t arrayVar = { .variableType = (type == IntClass) ? IntArrayClass : StringArrayClass, .solvedArray.vector = v };
|
||||
return copyVariableToPtr(arrayVar);
|
||||
}
|
||||
|
||||
Variable_t createUnsolvedArrayVariable(Function_t* f) {
|
||||
Variable_t var = { 0 };
|
||||
Vector_t holder = { 0 };
|
||||
u8 varType = Invalid;
|
||||
|
||||
// Foreach to attempt to create the array. Should fail if calcs are done or types are not equal
|
||||
if (f->operations.count > 0) {
|
||||
vecForEach(Operator_t*, curOp, (&f->operations)) {
|
||||
if (holder.data == NULL) {
|
||||
if (curOp->variable.staticVariableType == 1) {
|
||||
varType = IntClass;
|
||||
holder = newVec(sizeof(s64), 4);
|
||||
vecAdd(&holder, (curOp->variable.integerType));
|
||||
}
|
||||
else if (curOp->variable.staticVariableType == 2) {
|
||||
if (!strcmp(curOp->variable.stringType, "BYTE[]")) {
|
||||
varType = ByteArrayClass; // Repurpose varType
|
||||
holder = newVec(sizeof(u8), 4);
|
||||
FREE(curOp->variable.stringType);
|
||||
}
|
||||
else {
|
||||
varType = StringClass;
|
||||
holder = newVec(sizeof(char*), 4);
|
||||
vecAdd(&holder, (curOp->variable.stringType));
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((curOp - 1)->token == EquationSeperator && curOp->token == Variable) {
|
||||
if (curOp->variable.staticVariableType == 1) {
|
||||
if (varType == IntClass) {
|
||||
vecAdd(&holder, curOp->variable.integerType);
|
||||
}
|
||||
else if (varType == ByteArrayClass) {
|
||||
u8 var = (u8)(curOp->variable.integerType & 0xFF);
|
||||
vecAdd(&holder, var);
|
||||
}
|
||||
}
|
||||
else if (curOp->variable.staticVariableType == 2) {
|
||||
if (varType == StringClass) {
|
||||
vecAdd(&holder, curOp->variable.stringType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
vecFree(holder);
|
||||
holder.data = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (curOp->token == EquationSeperator) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
vecFree(holder);
|
||||
holder.data = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (varType != Invalid) {
|
||||
if (varType == IntClass) {
|
||||
var.variableType = IntArrayClass;
|
||||
}
|
||||
else if (varType == StringClass) {
|
||||
var.variableType = StringArrayClass;
|
||||
}
|
||||
else {
|
||||
var.variableType = varType;
|
||||
}
|
||||
|
||||
vecFree(f->operations);
|
||||
var.solvedArray.vector = holder;
|
||||
var.readOnly = 1;
|
||||
}
|
||||
else {
|
||||
var.unsolvedArray.operations = f->operations;
|
||||
var.variableType = UnresolvedArrayClass;
|
||||
}
|
||||
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
ClassFunction(retZero){
|
||||
return newIntVariablePtr(0);
|
||||
}
|
||||
|
||||
u8 anotherOneVarArg[] = { VARARGCOUNT };
|
||||
u8 unsolvedArrayStr[] = { StringClass };
|
||||
|
||||
ClassFunction(createTypedArray) {
|
||||
Vector_t v = { 0 };
|
||||
Variable_t* arg = *args;
|
||||
|
||||
if (arg->variableType == IntClass) {
|
||||
v = newVec(sizeof(s64), 1);
|
||||
vecAdd(&v, arg->integer.value);
|
||||
}
|
||||
else if (arg->variableType == StringClass) {
|
||||
v = newVec(sizeof(char*), 1);
|
||||
char* str = CpyStr(arg->string.value);
|
||||
vecAdd(&v, str);
|
||||
}
|
||||
else {
|
||||
SCRIPT_FATAL_ERR("Unknown array type");
|
||||
}
|
||||
|
||||
Variable_t arrayVar = { .variableType = (arg->variableType == IntClass) ? IntArrayClass : StringArrayClass, .solvedArray.vector = v };
|
||||
*caller = arrayVar;
|
||||
|
||||
return &emptyClass;
|
||||
}
|
||||
|
||||
ClassFunctionTableEntry_t unsolvedArrayFunctions[] = {
|
||||
{"+", createTypedArray, 1, anotherOneVarArg},
|
||||
{"add", createTypedArray, 1, anotherOneVarArg},
|
||||
{"len", retZero, 0, 0},
|
||||
};
|
||||
|
||||
Variable_t getUnsolvedArrayMember(Variable_t* var, char* memberName) {
|
||||
return getGenericFunctionMember(var, memberName, unsolvedArrayFunctions, ARRAY_SIZE(unsolvedArrayFunctions));
|
||||
}
|
||||
8
TegraExplorer/source/script/unsolvedArrayClass.h
Normal file
8
TegraExplorer/source/script/unsolvedArrayClass.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
#include "compat.h"
|
||||
|
||||
Variable_t createUnsolvedArrayVariable(Function_t* f);
|
||||
Variable_t* solveArray(Variable_t* unsolvedArray);
|
||||
Variable_t getUnsolvedArrayMember(Variable_t* var, char* memberName);
|
||||
108
TegraExplorer/source/script/vector.c
Normal file
108
TegraExplorer/source/script/vector.c
Normal file
@@ -0,0 +1,108 @@
|
||||
#ifdef WIN32
|
||||
#include "vector.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
Vector_t newVec(u8 typesz, u32 preallocate) {
|
||||
if (preallocate) {
|
||||
Vector_t res = {
|
||||
.data = calloc(preallocate, typesz),
|
||||
.capacity = preallocate * typesz,
|
||||
.count = 0,
|
||||
.elemSz = typesz
|
||||
};
|
||||
|
||||
// check .data != null;
|
||||
return res;
|
||||
}
|
||||
else {
|
||||
Vector_t res = {
|
||||
.data = NULL,
|
||||
.capacity = 1 * typesz,
|
||||
.count = 0,
|
||||
.elemSz = typesz
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
Vector_t vecFromArray(void* array, u32 count, u32 typesz)
|
||||
{
|
||||
Vector_t res = {
|
||||
.data = array,
|
||||
.capacity = count * typesz,
|
||||
.count = count,
|
||||
.elemSz = typesz
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
int vecAddElem(Vector_t* v, void* elem, u8 sz) {
|
||||
if (!v || !elem || v->elemSz != sz)
|
||||
return 0;
|
||||
|
||||
if (v->data == NULL) {
|
||||
v->data = calloc(1, v->elemSz);
|
||||
}
|
||||
|
||||
u32 usedbytes = v->count * sz;
|
||||
if (usedbytes >= v->capacity)
|
||||
{
|
||||
v->capacity *= 2;
|
||||
void* buff = malloc(v->capacity);
|
||||
if (!buff)
|
||||
return 0;
|
||||
memcpy(buff, v->data, v->capacity / 2);
|
||||
free(v->data);
|
||||
v->data = buff;
|
||||
}
|
||||
|
||||
memcpy((char*)v->data + usedbytes, elem, sz);
|
||||
v->count++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vector_t vecCopyOffset(Vector_t* orig, u32 offset) {
|
||||
Vector_t dst = newVec(orig->elemSz, orig->count - offset);
|
||||
memcpy(dst.data, ((u8*)orig->data + orig->elemSz * offset), (orig->count - offset) * orig->elemSz);
|
||||
dst.count = orig->count - offset;
|
||||
return dst;
|
||||
}
|
||||
|
||||
Vector_t vecCopy(Vector_t* orig) {
|
||||
return vecCopyOffset(orig, 0);
|
||||
}
|
||||
|
||||
|
||||
void* getStackEntry(Vector_t *stack) {
|
||||
if (stack->count <= 0)
|
||||
return NULL;
|
||||
|
||||
return ((u8*)stack->data + (stack->elemSz * (stack->count - 1)));
|
||||
}
|
||||
|
||||
// This will stay valid until the queue is modified
|
||||
void* popStackEntry(Vector_t* stack) {
|
||||
if (stack->count <= 0)
|
||||
return NULL;
|
||||
|
||||
void* a = getStackEntry(stack);
|
||||
stack->count--;
|
||||
return a;
|
||||
}
|
||||
|
||||
void vecRem(Vector_t *vec, int idx) {
|
||||
if (vec->count <= 0 || idx >= vec->count)
|
||||
return;
|
||||
|
||||
if (idx == (vec->count - 1)) {
|
||||
vec->count--;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy((u8*)vec->data + (vec->elemSz * idx), (u8*)vec->data + (vec->elemSz * (idx + 1)), (vec->count - idx - 1) * vec->elemSz);
|
||||
vec->count--;
|
||||
}
|
||||
#endif
|
||||
24
TegraExplorer/source/script/vector.h
Normal file
24
TegraExplorer/source/script/vector.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifdef WIN32
|
||||
#pragma once
|
||||
|
||||
#include "model.h"
|
||||
|
||||
int vecAddElem(Vector_t* v, void* elem, u8 sz);
|
||||
Vector_t newVec(u8 typesz, u32 preallocate);
|
||||
Vector_t vecCopy(Vector_t* orig);
|
||||
Vector_t vecCopyOffset(Vector_t* orig, u32 offset);
|
||||
Vector_t vecFromArray(void* array, u32 count, u32 typesz);
|
||||
|
||||
#define vecAdd(vec, element) vecAddElem(vec, &element, sizeof(element))
|
||||
#define vecGetArrayPtr(vec, type) (type)(vec)->data
|
||||
#define vecGetArray(vec, type) (type)(vec).data
|
||||
#define vecFreePtr(vec) FREE(vec->data)
|
||||
#define vecFree(vec) FREE(vec.data)
|
||||
#define vecGetCapacity(vec) (vec.capacity / vec.elemSz)
|
||||
|
||||
#define vecForEach(type, varname, vecPtr) for (type varname = vecPtr->data; ((u8*)varname - (u8*)vecPtr->data) < (vecPtr->count * vecPtr->elemSz); varname++)
|
||||
|
||||
void* getStackEntry(Vector_t* stack);
|
||||
void* popStackEntry(Vector_t* stack);
|
||||
void vecRem(Vector_t * vec, int idx);
|
||||
#endif
|
||||
77
TegraExplorer/source/start.S
Normal file
77
TegraExplorer/source/start.S
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.section .text._start
|
||||
.arm
|
||||
|
||||
.extern _reloc_ipl
|
||||
.type _reloc_ipl, %function
|
||||
|
||||
.extern memset
|
||||
.type memset, %function
|
||||
|
||||
.extern _irq_setup
|
||||
.type _irq_setup, %function
|
||||
|
||||
.globl _start
|
||||
.type _start, %function
|
||||
_start:
|
||||
ADR R0, _start
|
||||
LDR R1, =__ipl_start
|
||||
CMP R0, R1
|
||||
BEQ _real_start
|
||||
|
||||
/* If we are not in the right location already, copy a relocator to upper IRAM. */
|
||||
ADR R2, _reloc_ipl
|
||||
LDR R3, =0x4003FF00
|
||||
MOV R4, #(_real_start - _reloc_ipl)
|
||||
_copy_loop:
|
||||
LDMIA R2!, {R5}
|
||||
STMIA R3!, {R5}
|
||||
SUBS R4, #4
|
||||
BNE _copy_loop
|
||||
|
||||
/* Use the relocator to copy ourselves into the right place. */
|
||||
LDR R2, =__ipl_end
|
||||
SUB R2, R2, R1
|
||||
LDR R3, =_real_start
|
||||
LDR R4, =0x4003FF00
|
||||
BX R4
|
||||
|
||||
_reloc_ipl:
|
||||
LDMIA R0!, {R4-R7}
|
||||
STMIA R1!, {R4-R7}
|
||||
SUBS R2, #0x10
|
||||
BNE _reloc_ipl
|
||||
/* Jump to the relocated entry. */
|
||||
BX R3
|
||||
|
||||
_real_start:
|
||||
/* Initially, we place our stack in IRAM but will move it to SDRAM later. */
|
||||
LDR SP, =0x4003FF00
|
||||
LDR R0, =__bss_start
|
||||
EOR R1, R1, R1
|
||||
LDR R2, =__bss_end
|
||||
SUB R2, R2, R0
|
||||
BL memset
|
||||
BL _irq_setup
|
||||
B .
|
||||
|
||||
.globl pivot_stack
|
||||
.type pivot_stack, %function
|
||||
pivot_stack:
|
||||
MOV SP, R0
|
||||
BX LR
|
||||
187
TegraExplorer/source/storage/emmcfile.c
Normal file
187
TegraExplorer/source/storage/emmcfile.c
Normal file
@@ -0,0 +1,187 @@
|
||||
#include "emmcfile.h"
|
||||
#include "../gfx/gfx.h"
|
||||
#include "emummc.h"
|
||||
#include "mountmanager.h"
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include "nx_emmc.h"
|
||||
#include <mem/heap.h>
|
||||
#include "../err.h"
|
||||
#include "../tegraexplorer/tconf.h"
|
||||
#include "../gfx/gfxutils.h"
|
||||
#include "../hid/hid.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include <string.h>
|
||||
#include "../fs/fsutils.h"
|
||||
#include "nx_emmc_bis.h"
|
||||
|
||||
// Uses default storage in nx_emmc.c
|
||||
// Expects the correct mmc & partition to be set
|
||||
ErrCode_t EmmcDumpToFile(const char *path, u32 lba_start, u32 lba_end, u8 force, u8 crypt){
|
||||
FIL fp;
|
||||
u32 curLba = lba_start;
|
||||
u32 totalSectors = lba_end - lba_start + 1;
|
||||
|
||||
if (FileExists(path) && !force){
|
||||
return newErrCode(TE_WARN_FILE_EXISTS);
|
||||
}
|
||||
|
||||
int res = f_open(&fp, path, FA_CREATE_ALWAYS | FA_WRITE);
|
||||
if (res){
|
||||
return newErrCode(res);
|
||||
}
|
||||
|
||||
ErrCode_t err = newErrCode(0);
|
||||
|
||||
u8 *buff = malloc(TConf.FSBuffSize);
|
||||
|
||||
if (!buff)
|
||||
return newErrCode(TE_ERR_MEM_ALLOC_FAIL);
|
||||
|
||||
u32 x, y;
|
||||
gfx_con_getpos(&x, &y);
|
||||
|
||||
while (totalSectors > 0){
|
||||
u32 num = MIN(totalSectors, TConf.FSBuffSize / NX_EMMC_BLOCKSIZE);
|
||||
|
||||
int readRes = 0;
|
||||
if (crypt)
|
||||
readRes = !nx_emmc_bis_read(curLba, num, buff);
|
||||
else
|
||||
readRes = emummc_storage_read(&emmc_storage, curLba, num, buff);
|
||||
|
||||
if (!readRes){
|
||||
err = newErrCode(TE_ERR_EMMC_READ_FAIL);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((res = f_write(&fp, buff, num * NX_EMMC_BLOCKSIZE, NULL))){
|
||||
err = newErrCode(res);
|
||||
break;
|
||||
}
|
||||
|
||||
curLba += num;
|
||||
totalSectors -= num;
|
||||
|
||||
u32 percent = ((curLba - lba_start) * 100) / ((lba_end - lba_start + 1));
|
||||
gfx_printf("[%3d%%]", percent);
|
||||
gfx_con_setpos(x, y);
|
||||
}
|
||||
|
||||
f_close(&fp);
|
||||
free(buff);
|
||||
return err;
|
||||
}
|
||||
|
||||
ErrCode_t EmmcRestoreFromFile(const char *path, u32 lba_start, u32 lba_end, u8 force, u8 crypt){
|
||||
FIL fp;
|
||||
u32 curLba = lba_start;
|
||||
u32 totalSectorsDest = lba_end - lba_start + 1;
|
||||
|
||||
int res = f_open(&fp, path, FA_OPEN_ALWAYS | FA_READ);
|
||||
if (res)
|
||||
return newErrCode(res);
|
||||
|
||||
u64 totalSizeSrc = f_size(&fp);
|
||||
u32 totalSectorsSrc = totalSizeSrc / NX_EMMC_BLOCKSIZE;
|
||||
|
||||
if (totalSectorsSrc > totalSectorsDest) // We don't close the file here, oh well
|
||||
return newErrCode(TE_ERR_FILE_TOO_BIG_FOR_DEST);
|
||||
|
||||
if (totalSectorsSrc < totalSectorsDest && !force)
|
||||
return newErrCode(TE_WARN_FILE_TOO_SMALL_FOR_DEST);
|
||||
|
||||
u8 *buff = malloc(TConf.FSBuffSize);
|
||||
ErrCode_t err = newErrCode(0);
|
||||
|
||||
if (!buff)
|
||||
return newErrCode(TE_ERR_MEM_ALLOC_FAIL);
|
||||
|
||||
u32 x, y;
|
||||
gfx_con_getpos(&x, &y);
|
||||
|
||||
while (totalSectorsSrc > 0){
|
||||
u32 num = MIN(totalSectorsSrc, TConf.FSBuffSize / NX_EMMC_BLOCKSIZE);
|
||||
|
||||
if ((res = f_read(&fp, buff, num * NX_EMMC_BLOCKSIZE, NULL))){
|
||||
err = newErrCode(res);
|
||||
break;
|
||||
}
|
||||
|
||||
int writeRes = 0;
|
||||
if (crypt)
|
||||
writeRes = !nx_emmc_bis_write(curLba, num, buff);
|
||||
else
|
||||
writeRes = emummc_storage_write(&emmc_storage, curLba, num, buff);
|
||||
|
||||
if (!writeRes){
|
||||
err = newErrCode(TE_ERR_EMMC_WRITE_FAIL);
|
||||
break;
|
||||
}
|
||||
|
||||
curLba += num;
|
||||
totalSectorsSrc -= num;
|
||||
|
||||
u32 percent = ((curLba - lba_start) * 100) / ((totalSizeSrc / NX_EMMC_BLOCKSIZE));
|
||||
gfx_printf("[%3d%%]", percent);
|
||||
gfx_con_setpos(x, y);
|
||||
}
|
||||
|
||||
f_close(&fp);
|
||||
free(buff);
|
||||
return err;
|
||||
}
|
||||
|
||||
int isSystemPartCrypt(emmc_part_t *part){
|
||||
switch (part->index){
|
||||
case 0:
|
||||
case 1:
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ErrCode_t DumpOrWriteEmmcPart(const char *path, const char *part, u8 write, u8 force){
|
||||
const u32 BOOT_PART_SIZE = emmc_storage.ext_csd.boot_mult << 17;
|
||||
u32 lba_start = 0;
|
||||
u32 lba_end = 0;
|
||||
u8 crypt = false;
|
||||
|
||||
if (!sd_mount())
|
||||
return newErrCode(TE_ERR_NO_SD);
|
||||
|
||||
if (TConf.currentMMCConnected == MMC_CONN_None)
|
||||
return newErrCode(TE_ERR_PARTITION_NOT_FOUND);
|
||||
|
||||
if (!memcmp(part, "BOOT0", 5)){
|
||||
emummc_storage_set_mmc_partition(&emmc_storage, 1);
|
||||
lba_end = (BOOT_PART_SIZE / NX_EMMC_BLOCKSIZE) - 1;
|
||||
}
|
||||
else if (!memcmp(part, "BOOT1", 5)){
|
||||
emummc_storage_set_mmc_partition(&emmc_storage, 2);
|
||||
lba_end = (BOOT_PART_SIZE / NX_EMMC_BLOCKSIZE) - 1;
|
||||
}
|
||||
else {
|
||||
emummc_storage_set_mmc_partition(&emmc_storage, 0);
|
||||
|
||||
emmc_part_t *system_part = nx_emmc_part_find(GetCurGPT(), part);
|
||||
if (!system_part)
|
||||
return newErrCode(TE_ERR_PARTITION_NOT_FOUND);
|
||||
|
||||
if (isSystemPartCrypt(system_part) && TConf.keysDumped){
|
||||
nx_emmc_bis_init(system_part);
|
||||
crypt = true;
|
||||
lba_start = 0;
|
||||
lba_end = system_part->lba_end - system_part->lba_start;
|
||||
}
|
||||
else {
|
||||
lba_start = system_part->lba_start;
|
||||
lba_end = system_part->lba_end;
|
||||
}
|
||||
}
|
||||
|
||||
return ((write) ? EmmcRestoreFromFile(path, lba_start, lba_end, force, crypt) : EmmcDumpToFile(path, lba_start, lba_end, force, crypt));
|
||||
}
|
||||
4
TegraExplorer/source/storage/emmcfile.h
Normal file
4
TegraExplorer/source/storage/emmcfile.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "../err.h"
|
||||
|
||||
ErrCode_t DumpOrWriteEmmcPart(const char *path, const char *part, u8 write, u8 force);
|
||||
301
TegraExplorer/source/storage/emummc.c
Normal file
301
TegraExplorer/source/storage/emummc.c
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright (c) 2019 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "emummc.h"
|
||||
#include <storage/sdmmc.h>
|
||||
#include "../config.h"
|
||||
#include <utils/ini.h>
|
||||
#include <gfx_utils.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <mem/heap.h>
|
||||
#include "../storage/nx_emmc.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include <utils/list.h>
|
||||
#include <utils/types.h>
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
emummc_cfg_t emu_cfg = { 0 };
|
||||
|
||||
void emummc_load_cfg()
|
||||
{
|
||||
emu_cfg.enabled = 0;
|
||||
emu_cfg.path = NULL;
|
||||
emu_cfg.sector = 0;
|
||||
emu_cfg.id = 0;
|
||||
emu_cfg.file_based_part_size = 0;
|
||||
emu_cfg.active_part = 0;
|
||||
emu_cfg.fs_ver = 0;
|
||||
if (!emu_cfg.nintendo_path)
|
||||
emu_cfg.nintendo_path = (char *)malloc(0x80);
|
||||
if (!emu_cfg.emummc_file_based_path)
|
||||
emu_cfg.emummc_file_based_path = (char *)malloc(0x80);
|
||||
|
||||
emu_cfg.nintendo_path[0] = 0;
|
||||
emu_cfg.emummc_file_based_path[0] = 0;
|
||||
|
||||
LIST_INIT(ini_sections);
|
||||
if (ini_parse(&ini_sections, "emuMMC/emummc.ini", false))
|
||||
{
|
||||
LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link)
|
||||
{
|
||||
if (ini_sec->type == INI_CHOICE)
|
||||
{
|
||||
if (strcmp(ini_sec->name, "emummc"))
|
||||
continue;
|
||||
|
||||
LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link)
|
||||
{
|
||||
if (!strcmp("enabled", kv->key))
|
||||
emu_cfg.enabled = atoi(kv->val);
|
||||
else if (!strcmp("sector", kv->key))
|
||||
emu_cfg.sector = strtol(kv->val, NULL, 16);
|
||||
else if (!strcmp("id", kv->key))
|
||||
emu_cfg.id = strtol(kv->val, NULL, 16);
|
||||
else if (!strcmp("path", kv->key))
|
||||
emu_cfg.path = kv->val;
|
||||
else if (!strcmp("nintendo_path", kv->key))
|
||||
strcpy(emu_cfg.nintendo_path, kv->val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool emummc_set_path(char *path)
|
||||
{
|
||||
FIL fp;
|
||||
bool found = false;
|
||||
|
||||
strcpy(emu_cfg.emummc_file_based_path, path);
|
||||
strcat(emu_cfg.emummc_file_based_path, "/raw_based");
|
||||
|
||||
if (!f_open(&fp, emu_cfg.emummc_file_based_path, FA_READ))
|
||||
{
|
||||
if (!f_read(&fp, &emu_cfg.sector, 4, NULL))
|
||||
if (emu_cfg.sector)
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(emu_cfg.emummc_file_based_path, path);
|
||||
strcat(emu_cfg.emummc_file_based_path, "/file_based");
|
||||
|
||||
if (!f_stat(emu_cfg.emummc_file_based_path, NULL))
|
||||
{
|
||||
emu_cfg.sector = 0;
|
||||
emu_cfg.path = path;
|
||||
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
emu_cfg.enabled = 1;
|
||||
emu_cfg.id = 0;
|
||||
strcpy(emu_cfg.nintendo_path, path);
|
||||
strcat(emu_cfg.nintendo_path, "/Nintendo");
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static int emummc_raw_get_part_off(int part_idx)
|
||||
{
|
||||
switch (part_idx)
|
||||
{
|
||||
case 0:
|
||||
return 2;
|
||||
case 1:
|
||||
return 0;
|
||||
case 2:
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
int emummc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc)
|
||||
{
|
||||
FILINFO fno;
|
||||
emu_cfg.active_part = 0;
|
||||
|
||||
// Always init eMMC even when in emuMMC. eMMC is needed from the emuMMC driver anyway.
|
||||
if (!sdmmc_storage_init_mmc(storage, sdmmc, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_HS400))
|
||||
return 2;
|
||||
|
||||
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
||||
return 0;
|
||||
|
||||
if (!sd_mount())
|
||||
goto out;
|
||||
|
||||
if (!emu_cfg.sector)
|
||||
{
|
||||
strcpy(emu_cfg.emummc_file_based_path, emu_cfg.path);
|
||||
strcat(emu_cfg.emummc_file_based_path, "/eMMC");
|
||||
|
||||
if (f_stat(emu_cfg.emummc_file_based_path, &fno))
|
||||
{
|
||||
EPRINTF("Failed to open eMMC folder.");
|
||||
goto out;
|
||||
}
|
||||
f_chmod(emu_cfg.emummc_file_based_path, AM_ARC, AM_ARC);
|
||||
|
||||
strcat(emu_cfg.emummc_file_based_path, "/00");
|
||||
if (f_stat(emu_cfg.emummc_file_based_path, &fno))
|
||||
{
|
||||
EPRINTF("Failed to open emuMMC rawnand.");
|
||||
goto out;
|
||||
}
|
||||
emu_cfg.file_based_part_size = fno.fsize >> 9;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int emummc_storage_end(sdmmc_storage_t *storage)
|
||||
{
|
||||
sdmmc_storage_end(storage);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int emummc_storage_read(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf)
|
||||
{
|
||||
FIL fp;
|
||||
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
||||
return sdmmc_storage_read(storage, sector, num_sectors, buf);
|
||||
else if (emu_cfg.sector)
|
||||
{
|
||||
sector += emu_cfg.sector;
|
||||
sector += emummc_raw_get_part_off(emu_cfg.active_part) * 0x2000;
|
||||
return sdmmc_storage_read(&sd_storage, sector, num_sectors, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!emu_cfg.active_part)
|
||||
{
|
||||
u32 file_part = sector / emu_cfg.file_based_part_size;
|
||||
sector = sector % emu_cfg.file_based_part_size;
|
||||
if (file_part >= 10)
|
||||
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 2, 10);
|
||||
else
|
||||
{
|
||||
emu_cfg.emummc_file_based_path[strlen(emu_cfg.emummc_file_based_path) - 2] = '0';
|
||||
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 1, 10);
|
||||
}
|
||||
}
|
||||
if (f_open(&fp, emu_cfg.emummc_file_based_path, FA_READ))
|
||||
{
|
||||
EPRINTF("Failed to open emuMMC image.");
|
||||
return 0;
|
||||
}
|
||||
f_lseek(&fp, (u64)sector << 9);
|
||||
if (f_read(&fp, buf, (u64)num_sectors << 9, NULL))
|
||||
{
|
||||
EPRINTF("Failed to read emuMMC image.");
|
||||
f_close(&fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
f_close(&fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int emummc_storage_write(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf)
|
||||
{
|
||||
FIL fp;
|
||||
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
||||
return sdmmc_storage_write(storage, sector, num_sectors, buf);
|
||||
else if (emu_cfg.sector)
|
||||
{
|
||||
sector += emu_cfg.sector;
|
||||
sector += emummc_raw_get_part_off(emu_cfg.active_part) * 0x2000;
|
||||
return sdmmc_storage_write(&sd_storage, sector, num_sectors, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!emu_cfg.active_part)
|
||||
{
|
||||
u32 file_part = sector / emu_cfg.file_based_part_size;
|
||||
sector = sector % emu_cfg.file_based_part_size;
|
||||
if (file_part >= 10)
|
||||
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 2, 10);
|
||||
else
|
||||
{
|
||||
emu_cfg.emummc_file_based_path[strlen(emu_cfg.emummc_file_based_path) - 2] = '0';
|
||||
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 1, 10);
|
||||
}
|
||||
}
|
||||
if (f_open(&fp, emu_cfg.emummc_file_based_path, FA_WRITE))
|
||||
{
|
||||
gfx_printf("e5\n");
|
||||
return 0;
|
||||
}
|
||||
f_lseek(&fp, (u64)sector << 9);
|
||||
if (f_write(&fp, buf, (u64)num_sectors << 9, NULL))
|
||||
{
|
||||
gfx_printf("e6\n");
|
||||
f_close(&fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
f_close(&fp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int emummc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition)
|
||||
{
|
||||
emu_cfg.active_part = partition;
|
||||
|
||||
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
||||
sdmmc_storage_set_mmc_partition(storage, partition);
|
||||
else if (emu_cfg.sector)
|
||||
return 1;
|
||||
else
|
||||
{
|
||||
strcpy(emu_cfg.emummc_file_based_path, emu_cfg.path);
|
||||
strcat(emu_cfg.emummc_file_based_path, "/eMMC");
|
||||
|
||||
switch (partition)
|
||||
{
|
||||
case 0:
|
||||
strcat(emu_cfg.emummc_file_based_path, "/00");
|
||||
break;
|
||||
case 1:
|
||||
strcat(emu_cfg.emummc_file_based_path, "/BOOT0");
|
||||
break;
|
||||
case 2:
|
||||
strcat(emu_cfg.emummc_file_based_path, "/BOOT1");
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
60
TegraExplorer/source/storage/emummc.h
Normal file
60
TegraExplorer/source/storage/emummc.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2019 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef EMUMMC_H
|
||||
#define EMUMMC_H
|
||||
|
||||
#include <storage/sdmmc.h>
|
||||
#include <utils/types.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
EMUMMC_TYPE_NONE = 0,
|
||||
EMUMMC_TYPE_PARTITION = 1,
|
||||
EMUMMC_TYPE_FILES = 2,
|
||||
} emummc_type_t;
|
||||
|
||||
typedef enum {
|
||||
EMUMMC_MMC_NAND = 0,
|
||||
EMUMMC_MMC_SD = 1,
|
||||
EMUMMC_MMC_GC = 2,
|
||||
} emummc_mmc_t;
|
||||
|
||||
typedef struct _emummc_cfg_t
|
||||
{
|
||||
int enabled;
|
||||
u64 sector;
|
||||
u16 id;
|
||||
char *path;
|
||||
char *nintendo_path;
|
||||
// Internal.
|
||||
char *emummc_file_based_path;
|
||||
u32 file_based_part_size;
|
||||
u32 active_part;
|
||||
int fs_ver;
|
||||
} emummc_cfg_t;
|
||||
|
||||
extern emummc_cfg_t emu_cfg;
|
||||
|
||||
void emummc_load_cfg();
|
||||
bool emummc_set_path(char *path);
|
||||
int emummc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc);
|
||||
int emummc_storage_end(sdmmc_storage_t *storage);
|
||||
int emummc_storage_read(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf);
|
||||
int emummc_storage_write(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf);
|
||||
int emummc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition);
|
||||
|
||||
#endif
|
||||
159
TegraExplorer/source/storage/gptmenu.c
Normal file
159
TegraExplorer/source/storage/gptmenu.c
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "gptmenu.h"
|
||||
#include "../gfx/gfx.h"
|
||||
#include "../gfx/menu.h"
|
||||
#include "../gfx/gfxutils.h"
|
||||
#include "../utils/vector.h"
|
||||
#include "mountmanager.h"
|
||||
#include <utils/list.h>
|
||||
#include <string.h>
|
||||
#include "nx_emmc.h"
|
||||
#include <mem/heap.h>
|
||||
#include "../fs/menus/explorer.h"
|
||||
#include "../err.h"
|
||||
#include "../tegraexplorer/tconf.h"
|
||||
#include "emmcfile.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include "../fs/fsutils.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
MenuEntry_t GptMenuHeader[] = {
|
||||
{.optionUnion = COLORTORGB(COLOR_ORANGE), .name = "<- Back"},
|
||||
{.optionUnion = COLORTORGB(COLOR_GREY) | SKIPBIT, .name = "Clipboard -> Partition\n"},
|
||||
{.optionUnion = COLORTORGB(COLOR_BLUE), .name = "BOOT0", .icon = 128, .showSize = 1, .size = 4, .sizeDef = 2},
|
||||
{.optionUnion = COLORTORGB(COLOR_BLUE), .name = "BOOT1", .icon = 128, .showSize = 1, .size = 4, .sizeDef = 2}
|
||||
};
|
||||
|
||||
const char *GptFSEntries[] = {
|
||||
"PRODINFOF",
|
||||
"SAFE",
|
||||
"SYSTEM",
|
||||
"USER"
|
||||
};
|
||||
|
||||
void GptMenu(u8 MMCType){
|
||||
if (connectMMC(MMCType))
|
||||
return;
|
||||
|
||||
GptMenuHeader[1].optionUnion = (TConf.explorerCopyMode == CMODE_Copy) ? (COLORTORGB(COLOR_ORANGE)) : (COLORTORGB(COLOR_GREY) | SKIPBIT);
|
||||
|
||||
Vector_t GptMenu = newVec(sizeof(MenuEntry_t), 15);
|
||||
GptMenu.count = ARR_LEN(GptMenuHeader);
|
||||
memcpy(GptMenu.data, GptMenuHeader, sizeof(MenuEntry_t) * ARR_LEN(GptMenuHeader));
|
||||
|
||||
link_t *gpt = GetCurGPT();
|
||||
|
||||
LIST_FOREACH_ENTRY(emmc_part_t, part, gpt, link) {
|
||||
MenuEntry_t entry = {.optionUnion = COLORTORGB(COLOR_VIOLET), .icon = 128, .name = part->name};
|
||||
u64 total = (part->lba_end - part->lba_start) / 2 + 1;
|
||||
u8 type = 1;
|
||||
while (total > 1024){
|
||||
total /= 1024;
|
||||
type++;
|
||||
}
|
||||
|
||||
if (type > 3)
|
||||
type = 3;
|
||||
|
||||
entry.showSize = 1;
|
||||
entry.size = total;
|
||||
entry.sizeDef = type;
|
||||
|
||||
for (int i = 0; i < ARR_LEN(GptFSEntries); i++){
|
||||
if (!strcmp(part->name, GptFSEntries[i])){
|
||||
entry.optionUnion = COLORTORGB(COLOR_WHITE);
|
||||
entry.icon = 127;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vecAddElem(&GptMenu, entry);
|
||||
}
|
||||
|
||||
vecDefArray(MenuEntry_t*, entries, GptMenu);
|
||||
int res = 0;
|
||||
while (1){
|
||||
gfx_clearscreen();
|
||||
gfx_printf((MMCType == MMC_CONN_EMMC) ? "-- Emmc --\n\n" : "-- Emummc --\n\n");
|
||||
|
||||
res = newMenu(&GptMenu, res, 40, 20, ALWAYSREDRAW | ENABLEB, GptMenu.count);
|
||||
|
||||
if (res < 1){
|
||||
break;
|
||||
}
|
||||
else if (res == 1){
|
||||
gfx_clearscreen();
|
||||
char *fileName = CpyStr(strrchr(TConf.srcCopy, '/') + 1);
|
||||
|
||||
gfx_printf("Are you sure you want to flash %s? ", fileName);
|
||||
if (MakeYesNoHorzMenu(3, COLOR_DEFAULT)){
|
||||
RESETCOLOR;
|
||||
gfx_printf("\nFlashing %s... ", fileName);
|
||||
ErrCode_t a = DumpOrWriteEmmcPart(TConf.srcCopy, fileName, 1, 0);
|
||||
if (a.err){
|
||||
if (a.err == TE_WARN_FILE_TOO_SMALL_FOR_DEST){
|
||||
gfx_printf("\r%s is too small for the destination. Flash anyway? ", fileName);
|
||||
if (MakeYesNoHorzMenu(3, COLOR_DEFAULT)){
|
||||
RESETCOLOR;
|
||||
gfx_printf("\nFlashing %s... ", fileName);
|
||||
a = DumpOrWriteEmmcPart(TConf.srcCopy, fileName, 1, 1);
|
||||
}
|
||||
else {
|
||||
a.err = 0;
|
||||
}
|
||||
}
|
||||
DrawError(a);
|
||||
}
|
||||
}
|
||||
free(fileName);
|
||||
}
|
||||
else if (entries[res].icon == 127){
|
||||
unmountMMCPart();
|
||||
ErrCode_t err = (TConf.keysDumped) ? mountMMCPart(entries[res].name) : newErrCode(TE_ERR_KEYDUMP_FAIL);
|
||||
if (err.err){
|
||||
DrawError(err);
|
||||
}
|
||||
else {
|
||||
if (TConf.curExplorerLoc > LOC_SD)
|
||||
ResetCopyParams();
|
||||
|
||||
TConf.curExplorerLoc = LOC_EMMC;
|
||||
FileExplorer("bis:/");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!sd_mount())
|
||||
continue;
|
||||
|
||||
gfx_clearscreen();
|
||||
gfx_printf("Do you want to dump %s? ", entries[res].name);
|
||||
if (MakeYesNoHorzMenu(3, COLOR_DEFAULT)){
|
||||
gfx_putc('\n');
|
||||
RESETCOLOR;
|
||||
gfx_printf("Dumping %s... ", entries[res].name);
|
||||
|
||||
f_mkdir("sd:/tegraexplorer");
|
||||
f_mkdir("sd:/tegraexplorer/Dumps");
|
||||
|
||||
char *path = CombinePaths("sd:/tegraexplorer/Dumps", entries[res].name);
|
||||
|
||||
ErrCode_t a = DumpOrWriteEmmcPart(path, entries[res].name, 0, 0);
|
||||
if (a.err){
|
||||
if (a.err == TE_WARN_FILE_EXISTS){
|
||||
gfx_printf("\rDest file for %s exists already. Overwrite? ", entries[res].name);
|
||||
if (MakeYesNoHorzMenu(3, COLOR_DEFAULT)){
|
||||
RESETCOLOR;
|
||||
gfx_printf("\nDumping %s... ", entries[res].name);
|
||||
a = DumpOrWriteEmmcPart(path, entries[res].name, 0, 1);
|
||||
}
|
||||
else {
|
||||
a.err = 0;
|
||||
}
|
||||
}
|
||||
DrawError(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(GptMenu.data);
|
||||
}
|
||||
5
TegraExplorer/source/storage/gptmenu.h
Normal file
5
TegraExplorer/source/storage/gptmenu.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <utils/types.h>
|
||||
|
||||
void GptMenu(u8 MMCType);
|
||||
92
TegraExplorer/source/storage/mountmanager.c
Normal file
92
TegraExplorer/source/storage/mountmanager.c
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "mountmanager.h"
|
||||
#include "emummc.h"
|
||||
#include "../tegraexplorer/tconf.h"
|
||||
#include "nx_emmc.h"
|
||||
#include "../keys/keys.h"
|
||||
#include <sec/se.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include "nx_emmc_bis.h"
|
||||
#include "../config.h"
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
void SetKeySlots(){
|
||||
if (TConf.keysDumped){
|
||||
se_aes_key_set(0, dumpedKeys.bis_key[0], AES_128_KEY_SIZE);
|
||||
se_aes_key_set(1, dumpedKeys.bis_key[0] + AES_128_KEY_SIZE, AES_128_KEY_SIZE);
|
||||
se_aes_key_set(2, dumpedKeys.bis_key[1], AES_128_KEY_SIZE);
|
||||
se_aes_key_set(3, dumpedKeys.bis_key[1] + AES_128_KEY_SIZE, AES_128_KEY_SIZE);
|
||||
se_aes_key_set(4, dumpedKeys.bis_key[2], AES_128_KEY_SIZE);
|
||||
se_aes_key_set(5, dumpedKeys.bis_key[2] + AES_128_KEY_SIZE, AES_128_KEY_SIZE);
|
||||
|
||||
// Not for bis but whatever
|
||||
se_aes_key_set(6, dumpedKeys.header_key + 0x00, 0x10);
|
||||
se_aes_key_set(7, dumpedKeys.header_key + 0x10, 0x10);
|
||||
se_aes_key_set(8, dumpedKeys.save_mac_key, 0x10);
|
||||
}
|
||||
}
|
||||
|
||||
LIST_INIT(curGpt);
|
||||
|
||||
void unmountMMCPart(){
|
||||
if (TConf.connectedMMCMounted)
|
||||
f_unmount("bis:");
|
||||
TConf.connectedMMCMounted = 0;
|
||||
}
|
||||
|
||||
void disconnectMMC(){
|
||||
unmountMMCPart();
|
||||
|
||||
if (TConf.currentMMCConnected != MMC_CONN_None){
|
||||
TConf.currentMMCConnected = MMC_CONN_None;
|
||||
emummc_storage_end(&emmc_storage);
|
||||
nx_emmc_gpt_free(&curGpt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int connectMMC(u8 mmcType){
|
||||
if (mmcType == TConf.currentMMCConnected)
|
||||
return 0;
|
||||
|
||||
disconnectMMC();
|
||||
h_cfg.emummc_force_disable = (mmcType == MMC_CONN_EMMC) ? 1 : 0;
|
||||
int res = emummc_storage_init_mmc(&emmc_storage, &emmc_sdmmc);
|
||||
if (!res){
|
||||
TConf.currentMMCConnected = mmcType;
|
||||
emummc_storage_set_mmc_partition(&emmc_storage, 0);
|
||||
nx_emmc_gpt_parse(&curGpt, &emmc_storage);
|
||||
}
|
||||
|
||||
return res; // deal with the errors later lol
|
||||
}
|
||||
|
||||
ErrCode_t mountMMCPart(const char *partition){
|
||||
if (TConf.currentMMCConnected == MMC_CONN_None)
|
||||
return newErrCode(TE_ERR_PARTITION_NOT_FOUND);
|
||||
|
||||
unmountMMCPart();
|
||||
|
||||
emummc_storage_set_mmc_partition(&emmc_storage, 0); // why i have to do this twice beats me
|
||||
|
||||
emmc_part_t *system_part = nx_emmc_part_find(&curGpt, partition);
|
||||
if (!system_part)
|
||||
return newErrCode(TE_ERR_PARTITION_NOT_FOUND);
|
||||
|
||||
nx_emmc_bis_init(system_part);
|
||||
|
||||
int res = 0;
|
||||
if ((res = f_mount(&emmc_fs, "bis:", 1)))
|
||||
return newErrCode(res);
|
||||
|
||||
TConf.connectedMMCMounted = 1;
|
||||
|
||||
|
||||
return newErrCode(0);
|
||||
}
|
||||
|
||||
link_t *GetCurGPT(){
|
||||
if (TConf.currentMMCConnected != MMC_CONN_None)
|
||||
return &curGpt;
|
||||
return NULL;
|
||||
}
|
||||
17
TegraExplorer/source/storage/mountmanager.h
Normal file
17
TegraExplorer/source/storage/mountmanager.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
#include <utils/list.h>
|
||||
#include "../err.h"
|
||||
|
||||
enum {
|
||||
MMC_CONN_None = 0,
|
||||
MMC_CONN_EMMC,
|
||||
MMC_CONN_EMUMMC
|
||||
};
|
||||
|
||||
int connectMMC(u8 mmcType);
|
||||
ErrCode_t mountMMCPart(const char *partition);
|
||||
void SetKeySlots();
|
||||
void unmountMMCPart();
|
||||
link_t *GetCurGPT();
|
||||
void disconnectMMC();
|
||||
86
TegraExplorer/source/storage/nx_emmc.c
Normal file
86
TegraExplorer/source/storage/nx_emmc.c
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "nx_emmc.h"
|
||||
#include "emummc.h"
|
||||
#include <mem/heap.h>
|
||||
#include <storage/mbr_gpt.h>
|
||||
#include <utils/list.h>
|
||||
|
||||
sdmmc_t emmc_sdmmc;
|
||||
sdmmc_storage_t emmc_storage;
|
||||
FATFS emmc_fs;
|
||||
|
||||
void nx_emmc_gpt_parse(link_t *gpt, sdmmc_storage_t *storage)
|
||||
{
|
||||
gpt_t *gpt_buf = (gpt_t *)calloc(NX_GPT_NUM_BLOCKS, NX_EMMC_BLOCKSIZE);
|
||||
|
||||
emummc_storage_read(storage, NX_GPT_FIRST_LBA, NX_GPT_NUM_BLOCKS, gpt_buf);
|
||||
|
||||
for (u32 i = 0; i < gpt_buf->header.num_part_ents; i++)
|
||||
{
|
||||
emmc_part_t *part = (emmc_part_t *)calloc(sizeof(emmc_part_t), 1);
|
||||
|
||||
if (gpt_buf->entries[i].lba_start < gpt_buf->header.first_use_lba)
|
||||
continue;
|
||||
|
||||
part->index = i;
|
||||
part->lba_start = gpt_buf->entries[i].lba_start;
|
||||
part->lba_end = gpt_buf->entries[i].lba_end;
|
||||
part->attrs = gpt_buf->entries[i].attrs;
|
||||
|
||||
// ASCII conversion. Copy only the LSByte of the UTF-16LE name.
|
||||
for (u32 j = 0; j < 36; j++)
|
||||
part->name[j] = gpt_buf->entries[i].name[j];
|
||||
part->name[35] = 0;
|
||||
|
||||
list_append(gpt, &part->link);
|
||||
}
|
||||
|
||||
free(gpt_buf);
|
||||
}
|
||||
|
||||
void nx_emmc_gpt_free(link_t *gpt)
|
||||
{
|
||||
LIST_FOREACH_SAFE(iter, gpt)
|
||||
free(CONTAINER_OF(iter, emmc_part_t, link));
|
||||
}
|
||||
|
||||
emmc_part_t *nx_emmc_part_find(link_t *gpt, const char *name)
|
||||
{
|
||||
LIST_FOREACH_ENTRY(emmc_part_t, part, gpt, link)
|
||||
if (!strcmp(part->name, name))
|
||||
return part;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int nx_emmc_part_read(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf)
|
||||
{
|
||||
// The last LBA is inclusive.
|
||||
if (part->lba_start + sector_off > part->lba_end)
|
||||
return 0;
|
||||
return emummc_storage_read(storage, part->lba_start + sector_off, num_sectors, buf);
|
||||
}
|
||||
|
||||
int nx_emmc_part_write(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf)
|
||||
{
|
||||
// The last LBA is inclusive.
|
||||
if (part->lba_start + sector_off > part->lba_end)
|
||||
return 0;
|
||||
return emummc_storage_write(storage, part->lba_start + sector_off, num_sectors, buf);
|
||||
}
|
||||
49
TegraExplorer/source/storage/nx_emmc.h
Normal file
49
TegraExplorer/source/storage/nx_emmc.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _NX_EMMC_H_
|
||||
#define _NX_EMMC_H_
|
||||
|
||||
#include <storage/sdmmc.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <utils/types.h>
|
||||
#include <utils/list.h>
|
||||
|
||||
#define NX_GPT_FIRST_LBA 1
|
||||
#define NX_GPT_NUM_BLOCKS 33
|
||||
#define NX_EMMC_BLOCKSIZE 512
|
||||
|
||||
typedef struct _emmc_part_t
|
||||
{
|
||||
u32 index;
|
||||
u32 lba_start;
|
||||
u32 lba_end;
|
||||
u64 attrs;
|
||||
char name[37];
|
||||
link_t link;
|
||||
} emmc_part_t;
|
||||
|
||||
extern sdmmc_t emmc_sdmmc;
|
||||
extern sdmmc_storage_t emmc_storage;
|
||||
extern FATFS emmc_fs;
|
||||
|
||||
void nx_emmc_gpt_parse(link_t *gpt, sdmmc_storage_t *storage);
|
||||
void nx_emmc_gpt_free(link_t *gpt);
|
||||
emmc_part_t *nx_emmc_part_find(link_t *gpt, const char *name);
|
||||
int nx_emmc_part_read(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf);
|
||||
int nx_emmc_part_write(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf);
|
||||
|
||||
#endif
|
||||
387
TegraExplorer/source/storage/nx_emmc_bis.c
Normal file
387
TegraExplorer/source/storage/nx_emmc_bis.c
Normal file
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
* eMMC BIS driver for Nintendo Switch
|
||||
*
|
||||
* Copyright (c) 2019 shchmue
|
||||
* Copyright (c) 2019-2020 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <memory_map.h>
|
||||
|
||||
#include <mem/heap.h>
|
||||
#include <sec/se.h>
|
||||
#include "../storage/nx_emmc.h"
|
||||
#include "nx_emmc_bis.h"
|
||||
#include <storage/sdmmc.h>
|
||||
#include <utils/types.h>
|
||||
|
||||
#define MAX_CLUSTER_CACHE_ENTRIES 32768
|
||||
#define CLUSTER_LOOKUP_EMPTY_ENTRY 0xFFFFFFFF
|
||||
#define SECTORS_PER_CLUSTER 0x20
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 cluster_num; // index of the cluster in the partition
|
||||
u32 visit_count; // used for debugging/access analysis
|
||||
u8 dirty; // has been modified without writeback flag
|
||||
u8 align[7];
|
||||
u8 cluster[XTS_CLUSTER_SIZE]; // the cached cluster itself
|
||||
} cluster_cache_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 emmc_buffer[XTS_CLUSTER_SIZE];
|
||||
cluster_cache_t cluster_cache[];
|
||||
} bis_cache_t;
|
||||
|
||||
static u8 ks_crypt = 0;
|
||||
static u8 ks_tweak = 0;
|
||||
static u8 cache_filled = 0;
|
||||
static u32 dirty_cluster_count = 0;
|
||||
static u32 cluster_cache_end_index = 0;
|
||||
static emmc_part_t *system_part = NULL;
|
||||
static bis_cache_t *bis_cache = (bis_cache_t *)NX_BIS_CACHE_ADDR;
|
||||
static u32 *cluster_lookup_buf = NULL;
|
||||
static u32 *cluster_lookup = NULL;
|
||||
static bool lock_cluster_cache = false;
|
||||
|
||||
static void _gf256_mul_x_le(void *block)
|
||||
{
|
||||
u32 *pdata = (u32 *)block;
|
||||
u32 carry = 0;
|
||||
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
{
|
||||
u32 b = pdata[i];
|
||||
pdata[i] = (b << 1) | carry;
|
||||
carry = b >> 31;
|
||||
}
|
||||
|
||||
if (carry)
|
||||
pdata[0x0] ^= 0x87;
|
||||
}
|
||||
|
||||
static int _nx_aes_xts_crypt_sec(u32 tweak_ks, u32 crypt_ks, u32 enc, u8 *tweak, bool regen_tweak, u32 tweak_exp, u32 sec, void *dst, const void *src, u32 sec_size)
|
||||
{
|
||||
u32 *pdst = (u32 *)dst;
|
||||
u32 *psrc = (u32 *)src;
|
||||
u32 *ptweak = (u32 *)tweak;
|
||||
|
||||
if (regen_tweak)
|
||||
{
|
||||
for (int i = 0xF; i >= 0; i--)
|
||||
{
|
||||
tweak[i] = sec & 0xFF;
|
||||
sec >>= 8;
|
||||
}
|
||||
if (!se_aes_crypt_block_ecb(tweak_ks, 1, tweak, tweak))
|
||||
return 0;
|
||||
}
|
||||
|
||||
// tweak_exp allows us to use a saved tweak to reduce _gf256_mul_x_le calls.
|
||||
for (u32 i = 0; i < (tweak_exp << 5); i++)
|
||||
_gf256_mul_x_le(tweak);
|
||||
|
||||
u8 orig_tweak[0x10] __attribute__((aligned(4)));
|
||||
memcpy(orig_tweak, tweak, 0x10);
|
||||
|
||||
// We are assuming a 0x10-aligned sector size in this implementation.
|
||||
for (u32 i = 0; i < (sec_size >> 4); i++)
|
||||
{
|
||||
for (u32 j = 0; j < 4; j++)
|
||||
pdst[j] = psrc[j] ^ ptweak[j];
|
||||
|
||||
_gf256_mul_x_le(tweak);
|
||||
psrc += 4;
|
||||
pdst += 4;
|
||||
}
|
||||
|
||||
if (!se_aes_crypt_ecb(crypt_ks, enc, dst, sec_size, dst, sec_size))
|
||||
return 0;
|
||||
|
||||
pdst = (u32 *)dst;
|
||||
ptweak = (u32 *)orig_tweak;
|
||||
for (u32 i = 0; i < (sec_size >> 4); i++)
|
||||
{
|
||||
for (u32 j = 0; j < 4; j++)
|
||||
pdst[j] = pdst[j] ^ ptweak[j];
|
||||
|
||||
_gf256_mul_x_le(orig_tweak);
|
||||
pdst += 4;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nx_emmc_bis_write_block(u32 sector, u32 count, void *buff, bool force_flush)
|
||||
{
|
||||
if (!system_part)
|
||||
return 3; // Not ready.
|
||||
|
||||
u8 tweak[0x10] __attribute__((aligned(4)));
|
||||
u32 cluster = sector / SECTORS_PER_CLUSTER;
|
||||
u32 aligned_sector = cluster * SECTORS_PER_CLUSTER;
|
||||
u32 sector_index_in_cluster = sector % SECTORS_PER_CLUSTER;
|
||||
u32 cluster_lookup_index = cluster_lookup[cluster];
|
||||
bool is_cached = cluster_lookup_index != CLUSTER_LOOKUP_EMPTY_ENTRY;
|
||||
|
||||
// Write to cached cluster.
|
||||
if (is_cached)
|
||||
{
|
||||
if (buff)
|
||||
memcpy(bis_cache->cluster_cache[cluster_lookup_index].cluster + sector_index_in_cluster * NX_EMMC_BLOCKSIZE, buff, count * NX_EMMC_BLOCKSIZE);
|
||||
else
|
||||
buff = bis_cache->cluster_cache[cluster_lookup_index].cluster;
|
||||
bis_cache->cluster_cache[cluster_lookup_index].visit_count++;
|
||||
if (bis_cache->cluster_cache[cluster_lookup_index].dirty == 0)
|
||||
dirty_cluster_count++;
|
||||
bis_cache->cluster_cache[cluster_lookup_index].dirty = 1;
|
||||
if (!force_flush)
|
||||
return 0; // Success.
|
||||
|
||||
// Reset args to trigger a full cluster flush to emmc.
|
||||
sector_index_in_cluster = 0;
|
||||
sector = aligned_sector;
|
||||
count = SECTORS_PER_CLUSTER;
|
||||
}
|
||||
|
||||
// Encrypt and write.
|
||||
if (!_nx_aes_xts_crypt_sec(ks_tweak, ks_crypt, 1, tweak, true, sector_index_in_cluster, cluster, bis_cache->emmc_buffer, buff, count * NX_EMMC_BLOCKSIZE) ||
|
||||
!nx_emmc_part_write(&emmc_storage, system_part, sector, count, bis_cache->emmc_buffer)
|
||||
)
|
||||
return 1; // R/W error.
|
||||
|
||||
// Mark cache entry not dirty if write succeeds.
|
||||
if (is_cached)
|
||||
{
|
||||
bis_cache->cluster_cache[cluster_lookup_index].dirty = 0;
|
||||
dirty_cluster_count--;
|
||||
}
|
||||
|
||||
return 0; // Success.
|
||||
}
|
||||
|
||||
static void _nx_emmc_bis_flush_cluster(cluster_cache_t *cache_entry)
|
||||
{
|
||||
nx_emmc_bis_write_block(cache_entry->cluster_num * SECTORS_PER_CLUSTER, SECTORS_PER_CLUSTER, NULL, true);
|
||||
}
|
||||
|
||||
static int nx_emmc_bis_read_block(u32 sector, u32 count, void *buff)
|
||||
{
|
||||
if (!system_part)
|
||||
return 3; // Not ready.
|
||||
|
||||
static u32 prev_cluster = -1;
|
||||
static u32 prev_sector = 0;
|
||||
static u8 tweak[0x10] __attribute__((aligned(4)));
|
||||
u8 cache_tweak[0x10] __attribute__((aligned(4)));
|
||||
|
||||
u32 tweak_exp = 0;
|
||||
bool regen_tweak = true;
|
||||
|
||||
u32 cluster = sector / SECTORS_PER_CLUSTER;
|
||||
u32 aligned_sector = cluster * SECTORS_PER_CLUSTER;
|
||||
u32 sector_index_in_cluster = sector % SECTORS_PER_CLUSTER;
|
||||
u32 cluster_lookup_index = cluster_lookup[cluster];
|
||||
|
||||
// Read from cached cluster.
|
||||
if (cluster_lookup_index != CLUSTER_LOOKUP_EMPTY_ENTRY)
|
||||
{
|
||||
memcpy(buff, bis_cache->cluster_cache[cluster_lookup_index].cluster + sector_index_in_cluster * NX_EMMC_BLOCKSIZE, count * NX_EMMC_BLOCKSIZE);
|
||||
bis_cache->cluster_cache[cluster_lookup_index].visit_count++;
|
||||
prev_sector = sector + count - 1;
|
||||
prev_cluster = cluster;
|
||||
return 0; // Success.
|
||||
}
|
||||
|
||||
// Cache cluster.
|
||||
if (!lock_cluster_cache)
|
||||
{
|
||||
// Roll the cache index over and flush if full.
|
||||
if (cluster_cache_end_index >= MAX_CLUSTER_CACHE_ENTRIES)
|
||||
{
|
||||
cluster_cache_end_index = 0;
|
||||
cache_filled = 1;
|
||||
}
|
||||
// Check if cache entry was previously in use in case of cache loop.
|
||||
if (cache_filled == 1 && bis_cache->cluster_cache[cluster_cache_end_index].dirty == 1)
|
||||
_nx_emmc_bis_flush_cluster(&bis_cache->cluster_cache[cluster_cache_end_index]);
|
||||
|
||||
if (cache_filled == 1)
|
||||
cluster_lookup[bis_cache->cluster_cache[cluster_cache_end_index].cluster_num] = -1;
|
||||
|
||||
bis_cache->cluster_cache[cluster_cache_end_index].cluster_num = cluster;
|
||||
bis_cache->cluster_cache[cluster_cache_end_index].visit_count = 1;
|
||||
bis_cache->cluster_cache[cluster_cache_end_index].dirty = 0;
|
||||
cluster_lookup[cluster] = cluster_cache_end_index;
|
||||
|
||||
// Read and decrypt the whole cluster the sector resides in.
|
||||
if (!nx_emmc_part_read(&emmc_storage, system_part, aligned_sector, SECTORS_PER_CLUSTER, bis_cache->emmc_buffer) ||
|
||||
!_nx_aes_xts_crypt_sec(ks_tweak, ks_crypt, 0, cache_tweak, true, 0, cluster, bis_cache->emmc_buffer, bis_cache->emmc_buffer, XTS_CLUSTER_SIZE)
|
||||
)
|
||||
return 1; // R/W error.
|
||||
|
||||
// Copy to cluster cache.
|
||||
memcpy(bis_cache->cluster_cache[cluster_cache_end_index].cluster, bis_cache->emmc_buffer, XTS_CLUSTER_SIZE);
|
||||
memcpy(buff, bis_cache->emmc_buffer + sector_index_in_cluster * NX_EMMC_BLOCKSIZE, count * NX_EMMC_BLOCKSIZE);
|
||||
cluster_cache_end_index++;
|
||||
return 0; // Success.
|
||||
}
|
||||
|
||||
// If not reading from or writing to cache, do a regular read and decrypt.
|
||||
if (!nx_emmc_part_read(&emmc_storage, system_part, sector, count, bis_cache->emmc_buffer))
|
||||
return 1; // R/W error.
|
||||
|
||||
if (prev_cluster != cluster) // Sector in different cluster than last read.
|
||||
{
|
||||
prev_cluster = cluster;
|
||||
tweak_exp = sector_index_in_cluster;
|
||||
}
|
||||
else if (sector > prev_sector) // Sector in same cluster and past last sector.
|
||||
{
|
||||
// Calculates the new tweak using the saved one, reducing expensive _gf256_mul_x_le calls.
|
||||
tweak_exp = sector - prev_sector - 1;
|
||||
regen_tweak = false;
|
||||
}
|
||||
else // Sector in same cluster and before or same as last sector.
|
||||
tweak_exp = sector_index_in_cluster;
|
||||
|
||||
// Maximum one cluster (1 XTS crypto block 16KB).
|
||||
if (!_nx_aes_xts_crypt_sec(ks_tweak, ks_crypt, 0, tweak, regen_tweak, tweak_exp, prev_cluster, buff, bis_cache->emmc_buffer, count * NX_EMMC_BLOCKSIZE))
|
||||
return 1; // R/W error.
|
||||
prev_sector = sector + count - 1;
|
||||
|
||||
return 0; // Success.
|
||||
}
|
||||
|
||||
int nx_emmc_bis_read(u32 sector, u32 count, void *buff)
|
||||
{
|
||||
int res = 1;
|
||||
u8 *buf = (u8 *)buff;
|
||||
u32 curr_sct = sector;
|
||||
|
||||
while (count)
|
||||
{
|
||||
u32 sct_cnt = MIN(count, 0x20);
|
||||
res = nx_emmc_bis_read_block(curr_sct, sct_cnt, buf);
|
||||
if (res)
|
||||
return 1;
|
||||
|
||||
count -= sct_cnt;
|
||||
curr_sct += sct_cnt;
|
||||
buf += NX_EMMC_BLOCKSIZE * sct_cnt;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int nx_emmc_bis_write(u32 sector, u32 count, void *buff)
|
||||
{
|
||||
int res = 1;
|
||||
u8 *buf = (u8 *)buff;
|
||||
u32 curr_sct = sector;
|
||||
|
||||
while (count)
|
||||
{
|
||||
u32 sct_cnt = MIN(count, 0x20);
|
||||
res = nx_emmc_bis_write_block(curr_sct, sct_cnt, buf, false);
|
||||
if (res)
|
||||
return 1;
|
||||
|
||||
count -= sct_cnt;
|
||||
curr_sct += sct_cnt;
|
||||
buf += NX_EMMC_BLOCKSIZE * sct_cnt;
|
||||
}
|
||||
|
||||
nx_emmc_bis_finalize();
|
||||
return res;
|
||||
}
|
||||
|
||||
void nx_emmc_bis_cluster_cache_init()
|
||||
{
|
||||
u32 cluster_lookup_size = (system_part->lba_end - system_part->lba_start + 1) / SECTORS_PER_CLUSTER * sizeof(*cluster_lookup);
|
||||
|
||||
if (cluster_lookup_buf)
|
||||
free(cluster_lookup_buf);
|
||||
|
||||
// Check if carveout protected, in case of old hwinit (pre 4.0.0) chainload.
|
||||
*(vu32 *)NX_BIS_LOOKUP_ADDR = 0;
|
||||
if (*(vu32 *)NX_BIS_LOOKUP_ADDR != 0)
|
||||
{
|
||||
cluster_lookup_buf = (u32 *)malloc(cluster_lookup_size + 0x2000);
|
||||
cluster_lookup = (u32 *)ALIGN((u32)cluster_lookup_buf, 0x1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
cluster_lookup_buf = NULL;
|
||||
cluster_lookup = (u32 *)NX_BIS_LOOKUP_ADDR;
|
||||
}
|
||||
|
||||
// Clear cluster lookup table and reset end index.
|
||||
memset(cluster_lookup, -1, cluster_lookup_size);
|
||||
cluster_cache_end_index = 0;
|
||||
lock_cluster_cache = false;
|
||||
|
||||
dirty_cluster_count = 0;
|
||||
cache_filled = 0;
|
||||
}
|
||||
|
||||
void nx_emmc_bis_init(emmc_part_t *part)
|
||||
{
|
||||
system_part = part;
|
||||
|
||||
nx_emmc_bis_cluster_cache_init();
|
||||
|
||||
switch (part->index)
|
||||
{
|
||||
case 0: // PRODINFO.
|
||||
case 1: // PRODINFOF.
|
||||
ks_crypt = 0;
|
||||
ks_tweak = 1;
|
||||
break;
|
||||
case 8: // SAFE.
|
||||
ks_crypt = 2;
|
||||
ks_tweak = 3;
|
||||
break;
|
||||
case 9: // SYSTEM.
|
||||
case 10: // USER.
|
||||
ks_crypt = 4;
|
||||
ks_tweak = 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nx_emmc_bis_finalize()
|
||||
{
|
||||
if (dirty_cluster_count == 0)
|
||||
return;
|
||||
|
||||
u32 limit = cache_filled == 1 ? MAX_CLUSTER_CACHE_ENTRIES : cluster_cache_end_index;
|
||||
u32 clusters_to_flush = dirty_cluster_count;
|
||||
for (u32 i = 0; i < limit && clusters_to_flush; i++)
|
||||
{
|
||||
if (bis_cache->cluster_cache[i].dirty) {
|
||||
_nx_emmc_bis_flush_cluster(&bis_cache->cluster_cache[i]);
|
||||
clusters_to_flush--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set cluster cache lock according to arg.
|
||||
void nx_emmc_bis_cache_lock(bool lock)
|
||||
{
|
||||
lock_cluster_cache = lock;
|
||||
}
|
||||
240
TegraExplorer/source/storage/nx_emmc_bis.h
Normal file
240
TegraExplorer/source/storage/nx_emmc_bis.h
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (c) 2019 shchmue
|
||||
* Copyright (c) 2019 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NX_EMMC_BIS_H
|
||||
#define NX_EMMC_BIS_H
|
||||
|
||||
#include "../storage/nx_emmc.h"
|
||||
#include <storage/sdmmc.h>
|
||||
|
||||
typedef struct _nx_emmc_cal0_spk_t
|
||||
{
|
||||
u16 unk0;
|
||||
u16 unk1;
|
||||
u16 eq_bw_lop;
|
||||
u16 eq_gn_lop;
|
||||
u16 eq_fc_bp1;
|
||||
u16 eq_bw_bp1;
|
||||
u16 eq_gn_bp1;
|
||||
u16 eq_fc_bp2;
|
||||
u16 eq_bw_bp2;
|
||||
u16 eq_gn_bp2;
|
||||
u16 eq_fc_bp3;
|
||||
u16 eq_bw_bp3;
|
||||
u16 eq_gn_bp3;
|
||||
u16 eq_fc_bp4;
|
||||
u16 eq_bw_bp4;
|
||||
u16 eq_gn_bp4;
|
||||
u16 eq_fc_hip1;
|
||||
u16 eq_gn_hip1;
|
||||
u16 eq_fc_hip2;
|
||||
u16 eq_bw_hip2;
|
||||
u16 eq_gn_hip2;
|
||||
u16 eq_pre_vol;
|
||||
u16 eq_pst_vol;
|
||||
u16 eq_ctrl2;
|
||||
u16 eq_ctrl1;
|
||||
u16 drc_agc_2;
|
||||
u16 drc_agc_3;
|
||||
u16 drc_agc_1;
|
||||
u16 spk_vol;
|
||||
u16 hp_vol;
|
||||
u16 dac1_min_vol_spk;
|
||||
u16 dac1_max_vol_spk;
|
||||
u16 dac1_min_vol_hp;
|
||||
u16 dac1_max_vol_hp;
|
||||
u16 in1_in2;
|
||||
u16 adc_vol_min;
|
||||
u16 adc_vol_max;
|
||||
u8 unk4[16];
|
||||
} __attribute__((packed)) nx_emmc_cal0_spk_t;
|
||||
|
||||
typedef struct _nx_emmc_cal0_t
|
||||
{
|
||||
u32 magic; // 'CAL0'.
|
||||
u32 version;
|
||||
u32 body_size;
|
||||
u16 model;
|
||||
u16 update_cnt;
|
||||
u8 pad_crc16_0[0x10];
|
||||
u8 body_sha256[0x20];
|
||||
char cfg_id1[0x1E];
|
||||
u8 crc16_pad1[2];
|
||||
u8 rsvd0[0x20];
|
||||
u32 wlan_cc_num;
|
||||
u32 wlan_cc_last;
|
||||
char wlan_cc[128][3];
|
||||
u8 crc16_pad2[8];
|
||||
u8 wlan_mac[6];
|
||||
u8 crc16_pad3[2];
|
||||
u8 rsvd1[8];
|
||||
u8 bd_mac[6];
|
||||
u8 crc16_pad4[2];
|
||||
u8 rsvd2[8];
|
||||
u8 acc_offset[6];
|
||||
u8 crc16_pad5[2];
|
||||
u8 acc_scale[6];
|
||||
u8 crc16_pad6[2];
|
||||
u8 gyro_offset[6];
|
||||
u8 crc16_pad7[2];
|
||||
u8 gyro_scale[6];
|
||||
u8 crc16_pad8[2];
|
||||
char serial_number[0x18];
|
||||
u8 crc16_pad9[8];
|
||||
|
||||
u8 ecc_p256_device_key[0x30];
|
||||
u8 crc16_pad10[0x10];
|
||||
u8 ecc_p256_device_cert[0x180];
|
||||
u8 crc16_pad11[0x10];
|
||||
u8 ecc_p233_device_key[0x30];
|
||||
u8 crc16_pad12[0x10];
|
||||
u8 ecc_p33_device_cert[0x180];
|
||||
u8 crc16_pad13[0x10];
|
||||
u8 ecc_p256_ticket_key[0x30];
|
||||
u8 crc16_pad14[0x10];
|
||||
u8 ecc_p256_ticket_cert[0x180];
|
||||
u8 crc16_pad15[0x10];
|
||||
u8 ecc_p233_ticket_key[0x30];
|
||||
u8 crc16_pad16[0x10];
|
||||
u8 ecc_p33_ticket_cert[0x180];
|
||||
u8 crc16_pad17[0x10];
|
||||
u8 ssl_key[0x110];
|
||||
u8 crc16_pad18[0x10];
|
||||
u32 ssl_cert_size;
|
||||
u8 crc16_pad19[0xC];
|
||||
u8 ssl_cert[0x800];
|
||||
u8 ssl_sha256[0x20];
|
||||
u8 random_number[0x1000];
|
||||
u8 random_number_sha256[0x20];
|
||||
u8 gc_key[0x110];
|
||||
u8 crc16_pad20[0x10];
|
||||
u8 gc_cert[0x400];
|
||||
u8 gc_cert_sha256[0x20];
|
||||
u8 rsa2048_eticket_key[0x220];
|
||||
u8 crc16_pad21[0x10];
|
||||
u8 rsa2048_eticket_cert[0x240];
|
||||
u8 crc16_pad22[0x10];
|
||||
|
||||
char battery_lot[0x1E];
|
||||
u8 crc16_pad23[2];
|
||||
nx_emmc_cal0_spk_t spk_cal;
|
||||
u8 spk_cal_rsvd[0x800 - sizeof(nx_emmc_cal0_spk_t)];
|
||||
u8 crc16_pad24[0x10];
|
||||
u32 region_code;
|
||||
u8 crc16_pad25[0xC];
|
||||
|
||||
u8 amiibo_key[0x50];
|
||||
u8 crc16_pad26[0x10];
|
||||
u8 amiibo_ecqv_cert[0x14];
|
||||
u8 crc16_pad27[0xC];
|
||||
u8 amiibo_ecqdsa_cert[0x70];
|
||||
u8 crc16_pad28[0x10];
|
||||
u8 amiibo_ecqv_bls_key[0x40];
|
||||
u8 crc16_pad29[0x10];
|
||||
u8 amiibo_ecqv_bls_cert[0x20];
|
||||
u8 crc16_pad30[0x10];
|
||||
u8 amiibo_ecqv_bls_root_cert[0x90];
|
||||
u8 crc16_pad31[0x10];
|
||||
|
||||
u32 product_model; // 1: Nx, 2: Copper, 4: Hoag.
|
||||
u8 crc16_pad32[0xC];
|
||||
u8 home_menu_scheme_main_color[6];
|
||||
u8 crc16_pad33[0xA];
|
||||
u32 lcd_bl_brightness_mapping[3]; // Floats. Normally 100%, 0% and 2%.
|
||||
u8 crc16_pad34[0x4];
|
||||
|
||||
u8 ext_ecc_b233_device_key[0x50];
|
||||
u8 crc16_pad35[0x10];
|
||||
u8 ext_ecc_p256_eticket_key[0x50];
|
||||
u8 crc16_pad36[0x10];
|
||||
u8 ext_ecc_b233_eticket_key[0x50];
|
||||
u8 crc16_pad37[0x10];
|
||||
u8 ext_ecc_rsa2048_eticket_key_iv[0x10];
|
||||
u8 ext_ecc_rsa2048_eticket_key[0x230];
|
||||
u32 ext_ecc_rsa2048_eticket_key_ver;
|
||||
u8 crc16_pad38[0xC];
|
||||
u8 ext_ssl_key[0x130];
|
||||
u8 crc16_pad39[0x10];
|
||||
u8 ext_gc_key[0x130];
|
||||
u8 crc16_pad40[0x10];
|
||||
|
||||
u32 lcd_vendor;
|
||||
u8 crc16_pad41[0xC];
|
||||
|
||||
// 5.0.0 and up.
|
||||
u8 ext_rsa2048_device_key[0x240];
|
||||
u8 crc16_pad42[0x10];
|
||||
u8 rsa2048_device_cert[0x240];
|
||||
u8 crc16_pad43[0x10];
|
||||
|
||||
u8 usbc_pwr_src_circuit_ver;
|
||||
u8 crc16_pad44[0xF];
|
||||
|
||||
// 9.0.0 and up.
|
||||
u32 home_menu_scheme_sub_color;
|
||||
u8 crc16_pad45[0xC];
|
||||
u32 home_menu_scheme_bezel_color;
|
||||
u8 crc16_pad46[0xC];
|
||||
u32 home_menu_scheme_main_color1;
|
||||
u8 crc16_pad47[0xC];
|
||||
u32 home_menu_scheme_main_color2;
|
||||
u8 crc16_pad48[0xC];
|
||||
u32 home_menu_scheme_main_color3;
|
||||
u8 crc16_pad49[0xC];
|
||||
|
||||
u8 analog_stick_type_l;
|
||||
u8 crc16_pad50[0xF];
|
||||
u8 analog_stick_param_l[0x12];
|
||||
u8 crc16_pad51[0xE];
|
||||
u8 analog_stick_cal_l[0x9];
|
||||
u8 crc16_pad52[0x7];
|
||||
u8 analog_stick_type_r;
|
||||
u8 crc16_pad53[0xF];
|
||||
u8 analog_stick_param_r[0x12];
|
||||
u8 crc16_pad54[0xE];
|
||||
u8 analog_stick_cal_r[0x9];
|
||||
u8 crc16_pad55[0x7];
|
||||
u8 console_6axis_sensor_type;
|
||||
u8 crc16_pad56[0xF];
|
||||
u8 console_6axis_sensor_hor_off[0x6];
|
||||
u8 crc16_pad57[0xA];
|
||||
|
||||
// 6.0.0 and up.
|
||||
u8 battery_ver;
|
||||
u8 crc16_pad58[0x1F];
|
||||
|
||||
// 9.0.0 and up.
|
||||
u32 home_menu_scheme_model;
|
||||
u8 crc16_pad59[0xC];
|
||||
|
||||
// 10.0.0 and up.
|
||||
u8 console_6axis_sensor_mount_type;
|
||||
} __attribute__((packed)) nx_emmc_cal0_t;
|
||||
|
||||
#define MAGIC_CAL0 0x304C4143
|
||||
#define NX_EMMC_CALIBRATION_OFFSET 0x4400
|
||||
#define NX_EMMC_CALIBRATION_SIZE 0x8000
|
||||
#define XTS_CLUSTER_SIZE 0x4000
|
||||
|
||||
int nx_emmc_bis_read(u32 sector, u32 count, void *buff);
|
||||
int nx_emmc_bis_write(u32 sector, u32 count, void *buff);
|
||||
void nx_emmc_bis_cluster_cache_init();
|
||||
void nx_emmc_bis_init(emmc_part_t *part);
|
||||
void nx_emmc_bis_finalize();
|
||||
void nx_emmc_bis_cache_lock(bool lock);
|
||||
|
||||
#endif
|
||||
228
TegraExplorer/source/storage/nx_sd.c
Normal file
228
TegraExplorer/source/storage/nx_sd.c
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2019 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <storage/nx_sd.h>
|
||||
#include <storage/sdmmc.h>
|
||||
#include <storage/sdmmc_driver.h>
|
||||
#include <gfx_utils.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <mem/heap.h>
|
||||
|
||||
bool sd_mounted = false;
|
||||
static u16 sd_errors[3] = { 0 }; // Init and Read/Write errors.
|
||||
static u32 sd_mode = SD_UHS_SDR82;
|
||||
|
||||
sdmmc_t sd_sdmmc;
|
||||
sdmmc_storage_t sd_storage;
|
||||
FATFS sd_fs;
|
||||
|
||||
void sd_error_count_increment(u8 type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SD_ERROR_INIT_FAIL:
|
||||
sd_errors[0]++;
|
||||
break;
|
||||
case SD_ERROR_RW_FAIL:
|
||||
sd_errors[1]++;
|
||||
break;
|
||||
case SD_ERROR_RW_RETRY:
|
||||
sd_errors[2]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u16 *sd_get_error_count()
|
||||
{
|
||||
return sd_errors;
|
||||
}
|
||||
|
||||
bool sd_get_card_removed()
|
||||
{
|
||||
if (!sdmmc_get_sd_inserted())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 sd_get_mode()
|
||||
{
|
||||
return sd_mode;
|
||||
}
|
||||
|
||||
int sd_init_retry(bool power_cycle)
|
||||
{
|
||||
u32 bus_width = SDMMC_BUS_WIDTH_4;
|
||||
u32 type = SDHCI_TIMING_UHS_SDR82;
|
||||
|
||||
// Power cycle SD card.
|
||||
if (power_cycle)
|
||||
{
|
||||
sd_mode--;
|
||||
sdmmc_storage_end(&sd_storage);
|
||||
}
|
||||
|
||||
// Get init parameters.
|
||||
switch (sd_mode)
|
||||
{
|
||||
case SD_INIT_FAIL: // Reset to max.
|
||||
return 0;
|
||||
case SD_1BIT_HS25:
|
||||
bus_width = SDMMC_BUS_WIDTH_1;
|
||||
type = SDHCI_TIMING_SD_HS25;
|
||||
break;
|
||||
case SD_4BIT_HS25:
|
||||
type = SDHCI_TIMING_SD_HS25;
|
||||
break;
|
||||
case SD_UHS_SDR82:
|
||||
type = SDHCI_TIMING_UHS_SDR82;
|
||||
break;
|
||||
default:
|
||||
sd_mode = SD_UHS_SDR82;
|
||||
}
|
||||
|
||||
return sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, bus_width, type);
|
||||
}
|
||||
|
||||
bool sd_initialize(bool power_cycle)
|
||||
{
|
||||
if (power_cycle)
|
||||
sdmmc_storage_end(&sd_storage);
|
||||
|
||||
int res = !sd_init_retry(false);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!res)
|
||||
return true;
|
||||
else if (!sdmmc_get_sd_inserted()) // SD Card is not inserted.
|
||||
{
|
||||
sd_mode = SD_UHS_SDR82;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sd_errors[SD_ERROR_INIT_FAIL]++;
|
||||
|
||||
if (sd_mode == SD_INIT_FAIL)
|
||||
break;
|
||||
else
|
||||
res = !sd_init_retry(true);
|
||||
}
|
||||
}
|
||||
|
||||
sdmmc_storage_end(&sd_storage);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_sd_inited = false;
|
||||
|
||||
bool sd_mount()
|
||||
{
|
||||
if (sd_mounted)
|
||||
return true;
|
||||
|
||||
int res = !sd_initialize(false);
|
||||
is_sd_inited = !res;
|
||||
|
||||
if (res)
|
||||
{
|
||||
gfx_con.mute = false;
|
||||
EPRINTF("Failed to init SD card.");
|
||||
if (!sdmmc_get_sd_inserted())
|
||||
EPRINTF("Make sure that it is inserted.");
|
||||
else
|
||||
EPRINTF("SD Card Reader is not properly seated!");
|
||||
}
|
||||
else
|
||||
{
|
||||
res = f_mount(&sd_fs, "", 1);
|
||||
if (res == FR_OK)
|
||||
{
|
||||
sd_mounted = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
gfx_con.mute = false;
|
||||
EPRINTFARGS("Failed to mount SD card (FatFS Error %d).\nMake sure that a FAT partition exists..", res);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _sd_deinit()
|
||||
{
|
||||
if (sd_mode == SD_INIT_FAIL)
|
||||
sd_mode = SD_UHS_SDR82;
|
||||
|
||||
if (sd_mounted)
|
||||
{
|
||||
f_mount(NULL, "", 1);
|
||||
sdmmc_storage_end(&sd_storage);
|
||||
sd_mounted = false;
|
||||
is_sd_inited = false;
|
||||
}
|
||||
}
|
||||
|
||||
void sd_unmount() { _sd_deinit(); }
|
||||
void sd_end() { _sd_deinit(); }
|
||||
|
||||
void *sd_file_read(const char *path, u32 *fsize)
|
||||
{
|
||||
FIL fp;
|
||||
if (f_open(&fp, path, FA_READ) != FR_OK)
|
||||
return NULL;
|
||||
|
||||
u32 size = f_size(&fp);
|
||||
if (fsize)
|
||||
*fsize = size;
|
||||
|
||||
char *buf = malloc(size + 1);
|
||||
buf[size] = '\0';
|
||||
|
||||
if (f_read(&fp, buf, size, NULL) != FR_OK)
|
||||
{
|
||||
free(buf);
|
||||
f_close(&fp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f_close(&fp);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
int sd_save_to_file(void *buf, u32 size, const char *filename)
|
||||
{
|
||||
FIL fp;
|
||||
u32 res = 0;
|
||||
res = f_open(&fp, filename, FA_CREATE_ALWAYS | FA_WRITE);
|
||||
if (res)
|
||||
{
|
||||
EPRINTFARGS("Error (%d) creating file\n%s.\n", res, filename);
|
||||
return res;
|
||||
}
|
||||
|
||||
f_write(&fp, buf, size, NULL);
|
||||
f_close(&fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
270
TegraExplorer/source/tegraexplorer/mainmenu.c
Normal file
270
TegraExplorer/source/tegraexplorer/mainmenu.c
Normal file
@@ -0,0 +1,270 @@
|
||||
#include "mainmenu.h"
|
||||
#include "../gfx/gfx.h"
|
||||
#include "../gfx/gfxutils.h"
|
||||
#include "../gfx/menu.h"
|
||||
#include "tools.h"
|
||||
#include "../hid/hid.h"
|
||||
#include "../fs/menus/explorer.h"
|
||||
#include <utils/btn.h>
|
||||
#include <storage/nx_sd.h>
|
||||
#include "tconf.h"
|
||||
#include "../keys/keys.h"
|
||||
#include "../storage/mountmanager.h"
|
||||
#include "../storage/gptmenu.h"
|
||||
#include "../storage/emummc.h"
|
||||
#include <utils/util.h>
|
||||
#include "../fs/fsutils.h"
|
||||
#include <soc/fuse.h>
|
||||
#include "../utils/utils.h"
|
||||
#include "../config.h"
|
||||
#include "../fs/readers/folderReader.h"
|
||||
#include <string.h>
|
||||
#include <mem/heap.h>
|
||||
#include "../fs/menus/filemenu.h"
|
||||
|
||||
#define INCLUDE_BUILTIN_SCRIPTS 1
|
||||
//#define SCRIPT_ONLY 1
|
||||
|
||||
#ifdef INCLUDE_BUILTIN_SCRIPTS
|
||||
#include "../../build/TegraExplorer/script/builtin.h"
|
||||
#endif
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
enum {
|
||||
#ifndef SCRIPT_ONLY
|
||||
MainExplore = 0,
|
||||
MainBrowseSd,
|
||||
MainMountSd,
|
||||
MainBrowseEmmc,
|
||||
MainBrowseEmummc,
|
||||
MainTools,
|
||||
MainPartitionSd,
|
||||
MainViewKeys,
|
||||
MainViewCredits,
|
||||
MainExit,
|
||||
#else
|
||||
MainExit = 0,
|
||||
#endif
|
||||
MainPowerOff,
|
||||
MainRebootRCM,
|
||||
MainRebootNormal,
|
||||
MainRebootHekate,
|
||||
MainRebootAMS,
|
||||
MainScripts,
|
||||
};
|
||||
|
||||
MenuEntry_t mainMenuEntries[] = {
|
||||
#ifndef SCRIPT_ONLY
|
||||
[MainExplore] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "-- Explore --"},
|
||||
[MainBrowseSd] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Browse SD"},
|
||||
[MainMountSd] = {.optionUnion = COLORTORGB(COLOR_YELLOW)}, // To mount/unmount the SD
|
||||
[MainBrowseEmmc] = {.optionUnion = COLORTORGB(COLOR_BLUE), .name = "Browse EMMC"},
|
||||
[MainBrowseEmummc] = {.optionUnion = COLORTORGB(COLOR_BLUE), .name = "Browse EMUMMC"},
|
||||
[MainTools] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Tools --"},
|
||||
[MainPartitionSd] = {.optionUnion = COLORTORGB(COLOR_ORANGE), .name = "Partition the sd"},
|
||||
[MainViewKeys] = {.optionUnion = COLORTORGB(COLOR_YELLOW), .name = "View dumped keys"},
|
||||
[MainViewCredits] = {.optionUnion = COLORTORGB(COLOR_YELLOW), .name = "Credits"},
|
||||
[MainExit] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Exit --"},
|
||||
#else
|
||||
[MainExit] = {.optionUnion = COLORTORGB(COLOR_WHITE), .name = "\n-- Exit --"},
|
||||
#endif
|
||||
[MainPowerOff] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Power off"},
|
||||
[MainRebootRCM] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to RCM"},
|
||||
[MainRebootNormal] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot normally"},
|
||||
[MainRebootHekate] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to bootloader/update.bin"},
|
||||
[MainRebootAMS] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to atmosphere/reboot_payload.bin"},
|
||||
[MainScripts] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Scripts --"}
|
||||
};
|
||||
|
||||
void HandleSD(){
|
||||
gfx_clearscreen();
|
||||
TConf.curExplorerLoc = LOC_SD;
|
||||
if (!sd_mount() || sd_get_card_removed()){
|
||||
gfx_printf("Sd is not mounted!");
|
||||
hidWait();
|
||||
}
|
||||
else
|
||||
FileExplorer("sd:/");
|
||||
}
|
||||
|
||||
void HandleEMMC(){
|
||||
GptMenu(MMC_CONN_EMMC);
|
||||
}
|
||||
|
||||
void HandleEMUMMC(){
|
||||
GptMenu(MMC_CONN_EMUMMC);
|
||||
}
|
||||
|
||||
void ViewKeys(){
|
||||
gfx_clearscreen();
|
||||
for (int i = 0; i < 3; i++){
|
||||
gfx_printf("\nBis key 0%d: ", i);
|
||||
PrintKey(dumpedKeys.bis_key[i], AES_128_KEY_SIZE * 2);
|
||||
}
|
||||
|
||||
gfx_printf("\nMaster key 0: ");
|
||||
PrintKey(dumpedKeys.master_key, AES_128_KEY_SIZE);
|
||||
gfx_printf("\nHeader key: ");
|
||||
PrintKey(dumpedKeys.header_key, AES_128_KEY_SIZE * 2);
|
||||
gfx_printf("\nSave mac key: ");
|
||||
PrintKey(dumpedKeys.save_mac_key, AES_128_KEY_SIZE);
|
||||
|
||||
u8 fuseCount = 0;
|
||||
for (u32 i = 0; i < 32; i++){
|
||||
if ((fuse_read_odm(7) >> i) & 1)
|
||||
fuseCount++;
|
||||
}
|
||||
|
||||
gfx_printf("\n\nPkg1 ID: '%s'\nFuse count: %d", TConf.pkg1ID, fuseCount);
|
||||
|
||||
hidWait();
|
||||
}
|
||||
|
||||
void ViewCredits(){
|
||||
gfx_clearscreen();
|
||||
gfx_printf("\nTegraexplorer v%d.%d.%d\nBy SuchMemeManySkill\n\nBased on Lockpick_RCM & Hekate, from shchmue & CTCaer\n\n\n", LP_VER_MJ, LP_VER_MN, LP_VER_BF);
|
||||
|
||||
if (hidRead()->r)
|
||||
gfx_printf("%k\"I'm not even sure if it works\" - meme", COLOR_ORANGE);
|
||||
|
||||
hidWait();
|
||||
}
|
||||
|
||||
extern bool sd_mounted;
|
||||
extern bool is_sd_inited;
|
||||
extern int launch_payload(char *path);
|
||||
|
||||
void RebootToAMS(){
|
||||
launch_payload("sd:/atmosphere/reboot_payload.bin");
|
||||
}
|
||||
|
||||
void RebootToHekate(){
|
||||
launch_payload("sd:/bootloader/update.bin");
|
||||
}
|
||||
|
||||
void MountOrUnmountSD(){
|
||||
gfx_clearscreen();
|
||||
if (sd_mounted)
|
||||
sd_unmount();
|
||||
else if (!sd_mount())
|
||||
hidWait();
|
||||
}
|
||||
|
||||
menuPaths mainMenuPaths[] = {
|
||||
#ifndef SCRIPT_ONLY
|
||||
[MainBrowseSd] = HandleSD,
|
||||
[MainMountSd] = MountOrUnmountSD,
|
||||
[MainBrowseEmmc] = HandleEMMC,
|
||||
[MainBrowseEmummc] = HandleEMUMMC,
|
||||
[MainPartitionSd] = FormatSD,
|
||||
[MainViewKeys] = ViewKeys,
|
||||
[MainViewCredits] = ViewCredits,
|
||||
#endif
|
||||
[MainRebootAMS] = RebootToAMS,
|
||||
[MainRebootHekate] = RebootToHekate,
|
||||
[MainRebootRCM] = reboot_rcm,
|
||||
[MainPowerOff] = power_off,
|
||||
[MainRebootNormal] = reboot_normal,
|
||||
};
|
||||
|
||||
void EnterMainMenu(){
|
||||
int res = 0;
|
||||
while (1){
|
||||
if (sd_get_card_removed())
|
||||
sd_unmount();
|
||||
|
||||
#ifndef SCRIPT_ONLY
|
||||
// -- Explore --
|
||||
mainMenuEntries[MainBrowseSd].hide = !sd_mounted;
|
||||
mainMenuEntries[MainMountSd].name = (sd_mounted) ? "Unmount SD" : "Mount SD";
|
||||
mainMenuEntries[MainBrowseEmummc].hide = (!emu_cfg.enabled || !sd_mounted);
|
||||
|
||||
// -- Tools --
|
||||
mainMenuEntries[MainPartitionSd].hide = (!is_sd_inited || sd_get_card_removed());
|
||||
mainMenuEntries[MainViewKeys].hide = !TConf.keysDumped;
|
||||
|
||||
// -- Exit --
|
||||
mainMenuEntries[MainRebootAMS].hide = (!sd_mounted || !FileExists("sd:/atmosphere/reboot_payload.bin"));
|
||||
mainMenuEntries[MainRebootHekate].hide = (!sd_mounted || !FileExists("sd:/bootloader/update.bin"));
|
||||
mainMenuEntries[MainRebootRCM].hide = h_cfg.t210b01;
|
||||
#endif
|
||||
// -- Scripts --
|
||||
#ifndef INCLUDE_BUILTIN_SCRIPTS
|
||||
mainMenuEntries[MainScripts].hide = (!sd_mounted || !FileExists("sd:/tegraexplorer/scripts"));
|
||||
#else
|
||||
mainMenuEntries[MainScripts].hide = ((!sd_mounted || !FileExists("sd:/tegraexplorer/scripts")) && !EMBEDDED_SCRIPTS_LEN);
|
||||
#endif
|
||||
|
||||
Vector_t ent = newVec(sizeof(MenuEntry_t), ARRAY_SIZE(mainMenuEntries));
|
||||
ent.count = ARRAY_SIZE(mainMenuEntries);
|
||||
memcpy(ent.data, mainMenuEntries, sizeof(MenuEntry_t) * ARRAY_SIZE(mainMenuEntries));
|
||||
Vector_t scriptFiles = {0};
|
||||
u8 hasScripts = 0;
|
||||
|
||||
#ifdef INCLUDE_BUILTIN_SCRIPTS
|
||||
for (int i = 0; i < EMBEDDED_SCRIPTS_LEN; i++){
|
||||
MenuEntry_t m = {.name = embedded_scripts_g[i].name, .optionUnion = COLORTORGB(COLOR_BLUE), .icon = 128};
|
||||
vecAdd(&ent, m);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sd_mounted && FileExists("sd:/tegraexplorer/scripts")){
|
||||
scriptFiles = ReadFolder("sd:/tegraexplorer/scripts", &res);
|
||||
if (!res){
|
||||
if (!scriptFiles.count){
|
||||
FREE(scriptFiles.data);
|
||||
mainMenuEntries[MainScripts].hide = 1;
|
||||
}
|
||||
else {
|
||||
hasScripts = 1;
|
||||
vecForEach(FSEntry_t*, scriptFile, (&scriptFiles)){
|
||||
if (!scriptFile->isDir && StrEndsWith(scriptFile->name, ".te")){
|
||||
MenuEntry_t a = MakeMenuOutFSEntry(*scriptFile);
|
||||
vecAdd(&ent, a);
|
||||
}
|
||||
}
|
||||
|
||||
if (ent.count == ARRAY_SIZE(mainMenuEntries)){
|
||||
clearFileVector(&scriptFiles);
|
||||
hasScripts = 0;
|
||||
mainMenuEntries[MainScripts].hide = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gfx_clearscreen();
|
||||
gfx_putc('\n');
|
||||
|
||||
res = newMenu(&ent, res, 79, 30, (ent.count == ARRAY_SIZE(mainMenuEntries)) ? ALWAYSREDRAW : ALWAYSREDRAW | ENABLEPAGECOUNT, ent.count - ARRAY_SIZE(mainMenuEntries));
|
||||
if (res < MainScripts && mainMenuPaths[res] != NULL)
|
||||
mainMenuPaths[res]();
|
||||
#ifndef INCLUDE_BUILTIN_SCRIPTS
|
||||
else if (hasScripts){
|
||||
#else
|
||||
else {
|
||||
if (res - ARRAY_SIZE(mainMenuEntries) < EMBEDDED_SCRIPTS_LEN){
|
||||
char *script = embedded_scripts_g[res - ARRAY_SIZE(mainMenuEntries)].script;
|
||||
RunScriptString(script, strlen(script));
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
vecDefArray(MenuEntry_t*, entArray, ent);
|
||||
MenuEntry_t entry = entArray[res];
|
||||
FSEntry_t fsEntry = {.name = entry.name, .sizeUnion = entry.sizeUnion};
|
||||
RunScript("sd:/tegraexplorer/scripts", fsEntry);
|
||||
#ifdef INCLUDE_BUILTIN_SCRIPTS
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (hasScripts){
|
||||
clearFileVector(&scriptFiles);
|
||||
}
|
||||
|
||||
free(ent.data);
|
||||
}
|
||||
}
|
||||
|
||||
3
TegraExplorer/source/tegraexplorer/mainmenu.h
Normal file
3
TegraExplorer/source/tegraexplorer/mainmenu.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void EnterMainMenu();
|
||||
17
TegraExplorer/source/tegraexplorer/tconf.c
Normal file
17
TegraExplorer/source/tegraexplorer/tconf.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "tconf.h"
|
||||
#include <mem/heap.h>
|
||||
#include "../utils/utils.h"
|
||||
TConf_t TConf = {0};
|
||||
|
||||
void ResetCopyParams(){
|
||||
TConf.heldExplorerCopyLoc = LOC_None;
|
||||
FREE(TConf.srcCopy);
|
||||
TConf.explorerCopyMode = CMODE_None;
|
||||
}
|
||||
|
||||
void SetCopyParams(const char *path, u8 mode){
|
||||
ResetCopyParams();
|
||||
TConf.heldExplorerCopyLoc = TConf.curExplorerLoc;
|
||||
TConf.explorerCopyMode = mode;
|
||||
TConf.srcCopy = CpyStr(path);
|
||||
}
|
||||
42
TegraExplorer/source/tegraexplorer/tconf.h
Normal file
42
TegraExplorer/source/tegraexplorer/tconf.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
#include "../keys/keys.h"
|
||||
|
||||
enum {
|
||||
LOC_None = 0,
|
||||
LOC_SD,
|
||||
LOC_EMMC,
|
||||
LOC_EMUMMC
|
||||
};
|
||||
|
||||
enum {
|
||||
CMODE_None = 0,
|
||||
CMODE_Copy,
|
||||
CMODE_Move,
|
||||
CMODE_CopyFolder,
|
||||
CMODE_MoveFolder
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
u32 FSBuffSize;
|
||||
char *srcCopy;
|
||||
union {
|
||||
struct {
|
||||
u16 minervaEnabled:1;
|
||||
u16 keysDumped:1;
|
||||
u16 curExplorerLoc:2;
|
||||
u16 heldExplorerCopyLoc:2;
|
||||
u16 explorerCopyMode:4;
|
||||
u16 currentMMCConnected:2;
|
||||
u16 connectedMMCMounted:1;
|
||||
};
|
||||
u16 optionUnion;
|
||||
};
|
||||
const char *pkg1ID;
|
||||
char *scriptCWD;
|
||||
} TConf_t;
|
||||
|
||||
extern TConf_t TConf;
|
||||
|
||||
void ResetCopyParams();
|
||||
void SetCopyParams(const char *path, u8 mode);
|
||||
164
TegraExplorer/source/tegraexplorer/tools.c
Normal file
164
TegraExplorer/source/tegraexplorer/tools.c
Normal file
@@ -0,0 +1,164 @@
|
||||
#include "tools.h"
|
||||
#include "../gfx/gfx.h"
|
||||
#include "../gfx/gfxutils.h"
|
||||
#include "../gfx/menu.h"
|
||||
#include "../hid/hid.h"
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include "../keys/keys.h"
|
||||
#include "../keys/nca.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include "../fs/fsutils.h"
|
||||
#include <utils/util.h>
|
||||
#include "../storage/mountmanager.h"
|
||||
#include "../err.h"
|
||||
#include <utils/sprintf.h>
|
||||
#include <mem/heap.h>
|
||||
#include "../tegraexplorer/tconf.h"
|
||||
#include "../fs/readers/folderReader.h"
|
||||
#include <string.h>
|
||||
#include "../fs/fscopy.h"
|
||||
#include "../utils/utils.h"
|
||||
#include <display/di.h>
|
||||
|
||||
extern sdmmc_storage_t sd_storage;
|
||||
extern bool is_sd_inited;
|
||||
|
||||
MenuEntry_t FatAndEmu[] = {
|
||||
{.optionUnion = COLORTORGB(COLOR_ORANGE), .name = "Back to main menu"},
|
||||
{.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Fat32 + EmuMMC"},
|
||||
{.optionUnion = COLORTORGB(COLOR_BLUE), .name = "Only Fat32"}
|
||||
};
|
||||
|
||||
void FormatSD(){
|
||||
gfx_clearscreen();
|
||||
disconnectMMC();
|
||||
DWORD plist[] = {0,0,0,0};
|
||||
bool emummc = 0;
|
||||
int res;
|
||||
|
||||
if (!is_sd_inited || sd_get_card_removed())
|
||||
return;
|
||||
|
||||
gfx_printf("\nDo you want to partition for an emummc?\n");
|
||||
res = MakeHorizontalMenu(FatAndEmu, ARR_LEN(FatAndEmu), 3, COLOR_DEFAULT, 0);
|
||||
|
||||
if (!res)
|
||||
return;
|
||||
|
||||
emummc = !(res - 1);
|
||||
|
||||
SETCOLOR(COLOR_RED, COLOR_DEFAULT);
|
||||
|
||||
plist[0] = sd_storage.csd.capacity;
|
||||
if (emummc){
|
||||
if (plist[0] < 83886080){
|
||||
gfx_printf("\n\nYou seem to be running this on a 32GB or smaller SD\nNot enough free space for emummc!");
|
||||
hidWait();
|
||||
return;
|
||||
}
|
||||
plist[0] -= 61145088;
|
||||
u32 allignedSectors = plist[0] - plist[0] % 2048;
|
||||
plist[1] = 61145088 + plist[0] % 2048;
|
||||
plist[0] = allignedSectors;
|
||||
}
|
||||
|
||||
gfx_printf("\n\nAre you sure you want to format your sd?\nThis will delete everything on your SD card!\nThis action is irreversible!\n\n");
|
||||
WaitFor(1500);
|
||||
|
||||
gfx_printf("%kAre you sure? ", COLOR_WHITE);
|
||||
if (!MakeYesNoHorzMenu(3, COLOR_DEFAULT)){
|
||||
return;
|
||||
}
|
||||
|
||||
RESETCOLOR;
|
||||
|
||||
gfx_printf("\n\nStarting Partitioning & Formatting\n");
|
||||
|
||||
for (int i = 0; i < 2; i++){
|
||||
gfx_printf("Part %d: %dKiB\n", i + 1, plist[i] / 2);
|
||||
}
|
||||
|
||||
u8 *work = malloc(TConf.FSBuffSize);
|
||||
res = f_fdisk_mod(0, plist, work);
|
||||
|
||||
if (!res){
|
||||
res = f_mkfs("sd:", FM_FAT32, 32768, work, TConf.FSBuffSize);
|
||||
}
|
||||
|
||||
sd_unmount();
|
||||
|
||||
if (res){
|
||||
DrawError(newErrCode(res));
|
||||
gfx_clearscreen();
|
||||
gfx_printf("Something went wrong\nPress any key to exit");
|
||||
}
|
||||
else {
|
||||
sd_mount();
|
||||
gfx_printf("\nDone!\nPress any key to exit");
|
||||
}
|
||||
|
||||
free(work);
|
||||
hidWait();
|
||||
}
|
||||
|
||||
extern bool sd_mounted;
|
||||
|
||||
void TakeScreenshot(){
|
||||
static u32 timer = 0;
|
||||
|
||||
if (!TConf.minervaEnabled || !sd_mounted)
|
||||
return;
|
||||
|
||||
if (timer + 3 < get_tmr_s())
|
||||
timer = get_tmr_s();
|
||||
else
|
||||
return;
|
||||
|
||||
char *name, *path;
|
||||
const char basepath[] = "sd:/tegraexplorer/screenshots";
|
||||
name = malloc(40);
|
||||
s_printf(name, "Screenshot_%08X.bmp", get_tmr_us());
|
||||
|
||||
f_mkdir("sd:/tegraexplorer");
|
||||
f_mkdir(basepath);
|
||||
path = CombinePaths(basepath, name);
|
||||
free(name);
|
||||
|
||||
const u32 file_size = 0x384000 + 0x36;
|
||||
u8 *bitmap = malloc(file_size);
|
||||
u32 *fb = malloc(0x384000);
|
||||
u32 *fb_ptr = gfx_ctxt.fb;
|
||||
|
||||
for (int x = 1279; x >= 0; x--)
|
||||
{
|
||||
for (int y = 719; y >= 0; y--)
|
||||
fb[y * 1280 + x] = *fb_ptr++;
|
||||
}
|
||||
|
||||
memcpy(bitmap + 0x36, fb, 0x384000);
|
||||
bmp_t *bmp = (bmp_t *)bitmap;
|
||||
|
||||
bmp->magic = 0x4D42;
|
||||
bmp->size = file_size;
|
||||
bmp->rsvd = 0;
|
||||
bmp->data_off = 0x36;
|
||||
bmp->hdr_size = 40;
|
||||
bmp->width = 1280;
|
||||
bmp->height = 720;
|
||||
bmp->planes = 1;
|
||||
bmp->pxl_bits = 32;
|
||||
bmp->comp = 0;
|
||||
bmp->img_size = 0x384000;
|
||||
bmp->res_h = 2834;
|
||||
bmp->res_v = 2834;
|
||||
bmp->rsvd2 = 0;
|
||||
|
||||
sd_save_to_file(bitmap, file_size, path);
|
||||
free(bitmap);
|
||||
free(fb);
|
||||
free(path);
|
||||
|
||||
display_backlight_brightness(255, 1000);
|
||||
msleep(100);
|
||||
display_backlight_brightness(100, 1000);
|
||||
}
|
||||
24
TegraExplorer/source/tegraexplorer/tools.h
Normal file
24
TegraExplorer/source/tegraexplorer/tools.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
|
||||
typedef struct _bmp_t
|
||||
{
|
||||
u16 magic;
|
||||
u32 size;
|
||||
u32 rsvd;
|
||||
u32 data_off;
|
||||
u32 hdr_size;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u16 planes;
|
||||
u16 pxl_bits;
|
||||
u32 comp;
|
||||
u32 img_size;
|
||||
u32 res_h;
|
||||
u32 res_v;
|
||||
u64 rsvd2;
|
||||
} __attribute__((packed)) bmp_t;
|
||||
|
||||
void DumpSysFw();
|
||||
void FormatSD();
|
||||
void TakeScreenshot();
|
||||
188
TegraExplorer/source/utils/utils.c
Normal file
188
TegraExplorer/source/utils/utils.c
Normal file
@@ -0,0 +1,188 @@
|
||||
#include "utils.h"
|
||||
#include <string.h>
|
||||
#include <utils/types.h>
|
||||
#include <mem/heap.h>
|
||||
#include <utils/util.h>
|
||||
#include "vector.h"
|
||||
#include "../gfx/gfxutils.h"
|
||||
#include "../gfx/gfx.h"
|
||||
#include "../gfx/menu.h"
|
||||
#include "../hid/hid.h"
|
||||
#include "../fs/fsutils.h"
|
||||
#include "../config.h"
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
extern int launch_payload(char *path);
|
||||
|
||||
void ALWAYS_INLINE power_off(){
|
||||
power_set_state(POWER_OFF_RESET);
|
||||
}
|
||||
|
||||
void ALWAYS_INLINE reboot_rcm(){
|
||||
power_set_state(REBOOT_RCM);
|
||||
}
|
||||
|
||||
void ALWAYS_INLINE reboot_normal(){
|
||||
power_set_state((h_cfg.t210b01) ? REBOOT_BYPASS_FUSES : POWER_OFF_REBOOT);
|
||||
}
|
||||
|
||||
void RebootToPayloadOrRcm(){
|
||||
if (FileExists("sd:/atmosphere/reboot_payload.bin"))
|
||||
launch_payload("sd:/atmosphere/reboot_payload.bin");
|
||||
reboot_rcm();
|
||||
}
|
||||
|
||||
char *CpyStr(const char* in){
|
||||
int len = strlen(in);
|
||||
char *out = malloc(len + 1);
|
||||
out[len] = 0;
|
||||
memcpy(out, in, len);
|
||||
return out;
|
||||
}
|
||||
|
||||
void MaskIn(char *mod, u32 bitstream, char mask){
|
||||
u32 len = strlen(mod);
|
||||
for (int i = 0; i < len; i++){
|
||||
if (!(bitstream & 1))
|
||||
*mod = mask;
|
||||
|
||||
bitstream >>= 1;
|
||||
mod++;
|
||||
}
|
||||
}
|
||||
|
||||
// non-zero is yes, zero is no
|
||||
bool StrEndsWith(char *begin, char *end){
|
||||
begin = strrchr(begin, *end);
|
||||
if (begin != NULL)
|
||||
return !strcmp(begin, end);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WaitFor(u32 ms){
|
||||
u32 a = get_tmr_ms();
|
||||
while (a + ms > get_tmr_ms());
|
||||
}
|
||||
|
||||
char *lines[] = {
|
||||
"1234567890*", // 0 - 10
|
||||
"qwertyuiop~", // 11 - 21
|
||||
"asdfghjkl.+", // 22 - 32
|
||||
"^zxcvbnm_<>" // 33 - 43
|
||||
};
|
||||
|
||||
char *ShowKeyboard(const char *toEdit, u8 alwaysRet){
|
||||
char *ret = CpyStr(toEdit);
|
||||
int pos = 0;
|
||||
int posOnWord = 0;
|
||||
bool shift = 0;
|
||||
|
||||
gfx_printf("* = exit | ~ = backspace | ^(left) = shift | + = add char\n\n");
|
||||
|
||||
u32 x, y;
|
||||
gfx_con_getpos(&x, &y);
|
||||
|
||||
while (1){
|
||||
gfx_con_setpos(x, y);
|
||||
|
||||
for (int i = 0; i < strlen(ret); i++){
|
||||
(i == posOnWord) ? SETCOLOR(COLOR_WHITE, COLOR_VIOLET) : SETCOLOR(COLOR_WHITE, COLOR_DEFAULT);
|
||||
gfx_putc(ret[i]);
|
||||
}
|
||||
|
||||
RESETCOLOR;
|
||||
gfx_putc(' ');
|
||||
|
||||
for (int a = 0; a < 4; a++){
|
||||
for (int b = 0; b < 11; b++){
|
||||
(pos == ((b % 11) + (a * 11))) ? SETCOLOR(COLOR_DEFAULT, COLOR_WHITE) : SETCOLOR(COLOR_WHITE, COLOR_DEFAULT);
|
||||
gfx_con_setpos(x + 16 + (b * 2 * 16), y + a * 16 * 2 + 32);
|
||||
if (shift && lines[a][b] >= 'a' && lines[a][b] <= 'z')
|
||||
gfx_putc(lines[a][b] & ~BIT(5));
|
||||
else
|
||||
gfx_putc(lines[a][b]);
|
||||
}
|
||||
}
|
||||
|
||||
Input_t *input = hidWait();
|
||||
if (input->buttons & (JoyA | JoyLB | JoyRB)){
|
||||
if (pos == 42 || input->l){
|
||||
if (posOnWord > 0)
|
||||
posOnWord--;
|
||||
}
|
||||
else if (pos == 43 || input->r){
|
||||
if (strlen(ret) - 1 > posOnWord)
|
||||
posOnWord++;
|
||||
}
|
||||
else if (pos == 10){
|
||||
break;
|
||||
}
|
||||
else if (pos == 21){
|
||||
u32 wordLen = strlen(ret);
|
||||
if (!wordLen)
|
||||
continue;
|
||||
|
||||
for (int i = posOnWord; i < wordLen - 1; i++){
|
||||
ret[i] = ret[i + 1];
|
||||
}
|
||||
ret[wordLen - 1] = 0;
|
||||
if (posOnWord > wordLen - 2)
|
||||
posOnWord--;
|
||||
}
|
||||
else if (pos == 32){
|
||||
u32 wordLen = strlen(ret);
|
||||
if (wordLen >= 79)
|
||||
continue;
|
||||
|
||||
char *copy = calloc(wordLen + 2, 1);
|
||||
memcpy(copy, ret, wordLen);
|
||||
copy[wordLen] = 'a';
|
||||
free(ret);
|
||||
ret = copy;
|
||||
}
|
||||
else if (pos == 33){
|
||||
shift = !shift;
|
||||
}
|
||||
else {
|
||||
char toPut = lines[pos / 11][pos % 11];
|
||||
if (shift)
|
||||
toPut &= ~BIT(5);
|
||||
ret[posOnWord] = toPut;
|
||||
|
||||
if (strlen(ret) - 1 > posOnWord)
|
||||
posOnWord++;
|
||||
}
|
||||
}
|
||||
int val = (input->up || input->down) ? 11 : 1;
|
||||
|
||||
if (input->volm) {
|
||||
if (pos > 0)
|
||||
pos -= 1;
|
||||
}
|
||||
else if (input->volp) {
|
||||
if (pos < 43)
|
||||
pos += 1;
|
||||
}
|
||||
else if (input->buttons & (JoyLLeft | JoyLUp)){
|
||||
if (pos > -1 + val)
|
||||
pos -= val;
|
||||
}
|
||||
else if (input->buttons & (JoyLRight | JoyLDown)){
|
||||
if (pos < 44 - val)
|
||||
pos += val;
|
||||
}
|
||||
|
||||
if (input->b){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(ret, toEdit) && !alwaysRet){
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
15
TegraExplorer/source/utils/utils.h
Normal file
15
TegraExplorer/source/utils/utils.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <utils/types.h>
|
||||
|
||||
char *CpyStr(const char* in);
|
||||
void MaskIn(char *mod, u32 bitstream, char mask);
|
||||
bool StrEndsWith(char *begin, char *end);
|
||||
void WaitFor(u32 ms);
|
||||
void RebootToPayloadOrRcm();
|
||||
|
||||
#define FREE(x) free(x); x = NULL;
|
||||
char *ShowKeyboard(const char *toEdit, u8 alwaysRet);
|
||||
|
||||
void power_off();
|
||||
void reboot_rcm();
|
||||
void reboot_normal();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user