Compare commits

...

39 Commits

Author SHA1 Message Date
yellows8
2c53af56cc
Release 3.5.1 2022-06-14 18:20:43 -04:00
yellows8
7bdfce172c
Fix temperature display on [14.0.0+]. 2022-03-24 20:53:19 -04:00
imxyd
34f7f16cb1 Update language.c
Improve Chinese language
2022-01-22 19:34:54 +01:00
yellows8
73d7108a62
Release 3.5.0 2021-10-29 19:47:15 -04:00
yellows8
422039f727
Use physfs instead of minizip for asset loading, etc. 2021-10-22 13:18:30 -04:00
yellows8
3f73855170
Added theme support for dirs and .zip via physfs. Closes #121. 2021-10-21 15:26:30 -04:00
qazrfv1234
6e654ec2af Update language.c (typo fixed) 2021-08-27 20:06:28 +02:00
Dave Murphy
5c85dd784b 3.4.1 release 2021-07-16 14:53:06 +01:00
Dave Murphy
8c87b1c46f ensure build picks up version from Makefile 2021-07-16 14:52:34 +01:00
fincs
47fd18cabb
Update psm charger type 2021-07-15 21:59:49 +02:00
Chronoss
c1640f4b54
Update language.c (#130) 2021-06-09 08:21:37 +01:00
HamletDuFromage
aef37e10f7
stat(path) before trying to launch a nro (#129) 2021-06-09 08:16:37 +01:00
Mehdi Khelfi
95411fe5e9
Added some spanish and french strings (#132) 2021-04-18 14:51:03 +02:00
yellows8
65f23b0bde
Release 3.4.0 2020-12-19 12:11:00 -05:00
fincs
20c1f00972
Update for libnx hid-refactor 2020-12-02 15:01:40 +01:00
fincs
fcbc56acc4
Improve performance by using deko3d to do the framebuffer conversion on the GPU 2020-11-11 17:00:25 +01:00
fincs
244d058f1b
Minor fixes 2020-11-11 12:40:41 +01:00
Mehdi Khelfi
df09e9ed1b Added French strings
Added 2 missing french translations in language.c
2020-10-31 18:52:55 +01:00
Chris Bradel
45efcfcb98
added momentum to touch drags (#124)
* added momentum to touch drags
2020-06-26 11:35:11 -04:00
yellows8
16958ac4ba
Fixed warnings, menuTimer multi-definition, and updated for latest libnx. 2020-05-05 18:47:40 -04:00
HookedBehemoth
c69f4c56c5 update for libnx pl init change 2020-04-15 18:18:22 +02:00
yellows8
3e451a9c8b
Added resources/eth_none_icon.png which was missing. 2020-04-10 00:54:10 -04:00
yellows8
090054fc86
Release v3.3.0 2020-04-06 10:37:55 -04:00
yellows8
e2a16a83e0
Netloader now uses SO_REUSEADDR, this fixes netloader activation temporarily failing after a netloader error previously occurred. 2020-04-06 10:36:47 -04:00
yellows8
ba330cfd84
Actually display errors from netloader activation. 2020-04-06 10:35:43 -04:00
yellows8
f4067d64f3
Added app_args config setting for fileassoc. 2020-04-06 10:34:44 -04:00
yellows8
88fe495bd8
Don't delete the fileassoc entry when loading the icon failed. 2020-04-04 12:55:55 -04:00
fincs
7f1a9f4f29
U64_MAX -> UINT64_MAX 2020-03-16 16:23:36 +01:00
yellows8
ec24d595d6
Updated the default highlightGradientEdgeColor. 2020-01-08 17:03:49 -05:00
Ivan Mazzoli
5dfb9ec328 Updated italian translation and added new ones 2020-01-07 16:13:39 +01:00
yellows8
a4ad7b01b7
The icon sizes for theme-layout menuListIcon/menuActiveEntryIcon can now be controlled with the size field, with the internal imageSize field being used when size is invalid. When entering Theme Menu, the initially selected menu-entry is now the currently configured theme. Minor improvements. 2020-01-04 18:08:54 -05:00
yellows8
6ee5d36084
Removed hbmenuLogoImage from theme_t and just determine which AssetId to use for logo during rendering, imageSize from the asset is now used instead of from the layoutobj. Removed drawImageFromLayout since it's no longer used, and removed imageSize from the layout objects which don't need it anymore. 2020-01-03 17:49:08 -05:00
yellows8
981749cfb7
Added highlightGradientEdgeColor for theme config. 2020-01-03 11:47:49 -05:00
yellows8
9eda227d33
Added logoColor to the theme config. 2020-01-02 22:48:48 -05:00
yellows8
d11585589e
Added support for using themes with extension '.romfs', which contains '/theme.cfg', optionally '/icon.jpg' for the menu-entry in theme-menu, and any assets. Added 'assets' group to theme config, this is only used with .romfs themes. Added 'backgroundImage' to layout theme config, which requires assets.background_image to be setup. Various improvements. 2020-01-02 11:08:17 -05:00
yellows8
42a4ad9787
Added support for using fileassoc with netloader, an error will now be thrown if the filename/file-extension used with netloader is not recognized. Various improvements. 2019-12-30 11:15:38 -05:00
yellows8
b40d558458
Fixed a crash in touch handling for star-button when the current directory is empty (no menu entries). Implemented layouts which can optionally be loaded from theme config, closes #83. 2019-12-26 01:40:54 -05:00
yellows8
387850d301
Added APP_AUTHOR to Makefile.nx. 2019-12-14 12:44:43 -05:00
yellows8
38b48a8609
Don't use hidGetHandheldMode() to determine whether to use handheld or non-handheld. Just accept input from controllers 0-7 and handheld regardless, as long as the controller is connected. Closes #108. 2019-12-11 20:13:18 -05:00
30 changed files with 1705 additions and 536 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.*/
*~
*.exe
*.o

View File

@ -1,4 +1,4 @@
export APP_VERSION := 3.2.0
export APP_VERSION := 3.5.1
ifeq ($(RELEASE),)
export APP_VERSION := $(APP_VERSION)-$(shell git describe --dirty --always)

View File

@ -46,6 +46,8 @@ ROMFS := romfs
DIST_PATH := $(TARGET)_v$(APP_VERSION)
APP_AUTHOR := switchbrew
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
@ -61,7 +63,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lminizip `freetype-config --libs` -lconfig -lturbojpeg
LIBS := -ldeko3d -lphysfs `freetype-config --libs` -lconfig -lturbojpeg -lpng
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
@ -210,6 +212,7 @@ $(OUTPUT).nso : $(OUTPUT).elf
endif
menu.o : $(TOPDIR)/Makefile
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)

View File

@ -14,7 +14,7 @@ test : pc_main/main.cpp pc_main/pc_launch.c pc_main/pc_power.c pc_main/pc_netsta
common/menu-entry.c common/menu-list.c common/message-box.c common/text.c \
common/ui.c common/assets.c common/math.c common/theme.c \
common/netloader.c
gcc -Wall -O2 -g -DVERSION=\"v$(APP_VERSION)\" $(EXTRA_CFLAGS) `pkg-config freetype2 --cflags` $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lpthread `pkg-config freetype2 --libs` -lm -lminizip -lz -lconfig -lturbojpeg $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@
gcc -Wall -O2 -g -DVERSION=\"v$(APP_VERSION)\" $(EXTRA_CFLAGS) `pkg-config freetype2 --cflags` $^ -lsfml-graphics -lsfml-window -lsfml-system -lstdc++ -lpthread `pkg-config freetype2 --libs` -lm -lphysfs -lz -lconfig -lturbojpeg -lpng $(EXTRA_LDFLAGS) -I. -iquote $(DEVKITPRO)/libnx/include -Ibuild_pc -g -o $@
clean:
rm -rf build_pc/ test test.*

View File

@ -13,13 +13,13 @@ The following [pacman packages](https://devkitpro.org/wiki/devkitPro_pacman) are
- `switch-freetype`
- `switch-libconfig`
- `switch-libjpeg-turbo`
- `switch-zlib`
- `switch-physfs`
The following libraries are required to build for PC:
- `libfreetype`
- `libconfig`
- `libjpeg-turbo`
- `libminizip`
- `libphysfs`
Building for Switch/PC requires `zip`.

View File

@ -1,32 +1,28 @@
#include "common.h"
#include <minizip/unzip.h>
#include <physfs.h>
#include <png.h>
typedef struct {
u8 *buffer;
size_t size;
const char *filename;
} assetsDataEntry;
#define GENASSET(x) {.filename = x}
#define GENASSET(_p, _mode, _w, _h) {{.path = _p, .imageMode = _mode, .imageSize = {_w, _h}}, {}}
static bool g_assetsInitialized = 0;
assetsDataEntry g_assetsDataList[AssetId_Max] = {
GENASSET("battery_icon.bin"),
GENASSET("charging_icon.bin"),
GENASSET("folder_icon.bin"),
GENASSET("invalid_icon.bin"),
GENASSET("hbmenu_logo_dark.bin"),
GENASSET("hbmenu_logo_light.bin"),
GENASSET("theme_icon_dark.bin"),
GENASSET("theme_icon_light.bin"),
GENASSET("airplane_icon.bin"),
GENASSET("wifi_none_icon.bin"),
GENASSET("wifi1_icon.bin"),
GENASSET("wifi2_icon.bin"),
GENASSET("wifi3_icon.bin"),
GENASSET("eth_icon.bin"),
GENASSET("eth_none_icon.bin"),
assetsDataEntry g_assetsDataList[AssetId_Max][2] = {
GENASSET("battery_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("charging_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("folder_icon.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("invalid_icon.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("hbmenu_logo_dark.bin", IMAGE_MODE_RGBA32, 140, 60),
GENASSET("hbmenu_logo_light.bin", IMAGE_MODE_RGBA32, 140, 60),
GENASSET("theme_icon_dark.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("theme_icon_light.bin", IMAGE_MODE_RGB24, 256, 256),
GENASSET("airplane_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi1_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi2_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("wifi3_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("eth_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("eth_none_icon.bin", IMAGE_MODE_RGBA32, 24, 24),
GENASSET("", IMAGE_MODE_RGB24, 1280, 720),
};
static void assetsClearEntry(assetsDataEntry *entry) {
@ -34,61 +30,27 @@ static void assetsClearEntry(assetsDataEntry *entry) {
entry->size = 0;
entry->buffer = NULL;
memset(entry, 0, sizeof(*entry));
}
static int assetsLoadFile(unzFile zipf, assetsDataEntry *entry) {
int ret;
int filesize=0;
unz_file_info file_info;
u8* buffer = NULL;
static void assetsSetPixelSize(assetsDataEntry *entry) {
switch (entry->imageMode) {
case IMAGE_MODE_RGB24:
entry->pixSize = 3;
break;
ret = unzLocateFile(zipf, entry->filename, 0);
if (ret==UNZ_OK) ret = unzOpenCurrentFile(zipf);
if (ret==UNZ_OK) {
ret = unzGetCurrentFileInfo(zipf, &file_info, NULL, 0, NULL, 0, NULL, 0);
filesize = file_info.uncompressed_size;
if (filesize == 0) ret = -10;
if (ret==UNZ_OK) {
buffer = (u8*)malloc(filesize);
if (buffer) {
memset(buffer, 0, filesize);
} else {
ret = -11;
case IMAGE_MODE_RGBA32:
entry->pixSize = 4;
break;
}
}
if (ret==UNZ_OK) {
ret = unzReadCurrentFile(zipf, buffer, filesize);
if(ret < filesize) {
ret = -12;
} else {
ret = UNZ_OK;
}
}
if (ret!=UNZ_OK && buffer!=NULL) free(buffer);
unzCloseCurrentFile(zipf);
}
if (ret==UNZ_OK) {
entry->buffer = buffer;
entry->size = filesize;
}
return ret;
}
Result assetsInit(void) {
int ret=0;
bool ret=false;
int i, stopi;
unzFile zipf;
assetsDataEntry *entry = NULL;
char tmp_path[PATH_MAX+1];
char tmp_path[PATH_MAX];
if (g_assetsInitialized) return 0;
@ -105,65 +67,211 @@ Result assetsInit(void) {
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/romfs/assets.zip", menuGetRootBasePath());
#endif
zipf = unzOpen(tmp_path);
if(zipf==NULL) {
#ifdef __SWITCH__
romfsExit();
#endif
return 0x80;
}
if (PHYSFS_mount(tmp_path, "", 0)) {
ret=true;
for (i=0; i<AssetId_Max; i++) {
stopi = i;
entry = &g_assetsDataList[i];
ret = assetsLoadFile(zipf, entry);
if (ret!=UNZ_OK) break;
entry = &g_assetsDataList[i][0];
if (entry->path[0]) {
ret = assetsLoadData(i, NULL, NULL);
if (!ret) break;
}
}
if (ret!=UNZ_OK) {
if (!ret) {
for (i=0; i<stopi; i++) {
assetsClearEntry(&g_assetsDataList[i]);
assetsClearEntry(&g_assetsDataList[i][0]);
}
}
if (ret==UNZ_OK) g_assetsInitialized = 1;
if (ret) g_assetsInitialized = 1;
unzClose(zipf);
PHYSFS_unmount(tmp_path);
}
#ifdef __SWITCH__
romfsExit();
return ret ? 0 : MAKERESULT(Module_Libnx, LibnxError_IoError);
#else
return ret ? 0 : 1;
#endif
}
void assetsExit(void) {
if (!g_assetsInitialized) return;
g_assetsInitialized = 0;
for (int i=0; i<AssetId_Max; i++) {
assetsClearEntry(&g_assetsDataList[i][0]);
}
assetsClearTheme();
}
void assetsClearTheme(void) {
for (int i=0; i<AssetId_Max; i++) {
assetsClearEntry(&g_assetsDataList[i][1]);
}
}
bool assetsLoadJpgFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height) {
int w,h,samp;
tjhandle _jpegDecompressor = tjInitDecompress();
if (_jpegDecompressor == NULL) {
return false;
}
if (tjDecompressHeader2(_jpegDecompressor, indata, indata_size, &w, &h, &samp) == -1) {
tjDestroy(_jpegDecompressor);
return false;
}
if (w != width || h != height ) {
tjDestroy(_jpegDecompressor);
return false;
}
if (tjDecompress2(_jpegDecompressor, indata, indata_size, outdata, w, 0, h, imageMode == IMAGE_MODE_RGB24 ? TJPF_RGB : TJPF_RGBA, TJFLAG_ACCURATEDCT) == -1) {
tjDestroy(_jpegDecompressor);
return false;
}
tjDestroy(_jpegDecompressor);
return true;
}
bool assetsLoadPngFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height) {
png_image image;
bool ret=true;
memset(&image, 0, sizeof(image));
image.version = PNG_IMAGE_VERSION;
if (png_image_begin_read_from_memory(&image, indata, indata_size) != 0) {
if (image.width != width || image.height != height)
ret = false;
if (ret) image.format = imageMode == IMAGE_MODE_RGB24 ? PNG_FORMAT_RGB : PNG_FORMAT_RGBA;
if (ret && png_image_finish_read(&image, NULL, outdata, 0, NULL) == 0)
ret = false;
}
else
ret = false;
png_image_free(&image);
return ret;
}
void assetsExit(void) {
int i;
bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term) {
bool ret=true;
*data_buf = NULL;
if (filesize) *filesize = 0;
if (!g_assetsInitialized) return;
g_assetsInitialized = 0;
PHYSFS_Stat tmpstat={0};
if (!(PHYSFS_stat(path, &tmpstat) && tmpstat.filesize!=-1)) ret = false;
for (i=0; i<AssetId_Max; i++) {
assetsClearEntry(&g_assetsDataList[i]);
if (ret) {
size_t bufsize = tmpstat.filesize;
if (nul_term) bufsize++;
*data_buf = (u8*)malloc(bufsize);
if (*data_buf) memset(*data_buf, 0, bufsize);
else ret = false;
}
if (ret) {
PHYSFS_File *f = PHYSFS_openRead(path);
if (f==NULL) ret = false;
else {
ret = PHYSFS_readBytes(f, *data_buf, tmpstat.filesize) == tmpstat.filesize;
PHYSFS_close(f);
}
}
if (ret) {
if (filesize) *filesize = tmpstat.filesize;
}
else {
free(*data_buf);
*data_buf = NULL;
}
return ret;
}
void assetsGetData(AssetId id, u8 **buffer, size_t *size) {
if (buffer) *buffer = NULL;
if (size) *size = 0;
bool assetsLoadData(AssetId id, const char *path, int *imageSize) {
if (id < 0 || id >= AssetId_Max) return false;
assetsDataEntry *entry = &g_assetsDataList[id][path ? 1 : 0];
if (entry->initialized) return false;
if (path) memset(entry, 0, sizeof(*entry));
if (imageSize) {
entry->imageSize[0] = imageSize[0];
entry->imageSize[1] = imageSize[1];
}
if (path) entry->imageMode = g_assetsDataList[id][0].imageMode;
assetsSetPixelSize(entry);
entry->size = entry->imageSize[0] * entry->imageSize[1] * entry->pixSize;
if (path) strncpy(entry->path, path, sizeof(entry->path)-1);
const char* ext = getExtension(entry->path);
bool ret=true;
size_t filesize=0;
if (ext==NULL) ret = false;
u8 *data_buf = NULL;
if (ret) {
entry->buffer = (u8*)malloc(entry->size);
if (entry->buffer) memset(entry->buffer, 0, entry->size);
else ret = false;
}
if (ret) ret = assetsPhysfsReadFile(entry->path, &data_buf, &filesize, false);
if (ret) {
if (strcasecmp(ext, ".bin")==0) {
if (filesize != entry->size) ret = false;
if (ret) memcpy(entry->buffer, data_buf, entry->size);
}
else if (strcasecmp(ext, ".jpg")==0 || strcasecmp(ext, ".jpeg")==0)
ret = assetsLoadJpgFromMemory(data_buf, filesize, entry->buffer, entry->imageMode, entry->imageSize[0], entry->imageSize[1]);
else if (strcasecmp(ext, ".png")==0)
ret = assetsLoadPngFromMemory(data_buf, filesize, entry->buffer, entry->imageMode, entry->imageSize[0], entry->imageSize[1]);
else
ret = false; // File extension not recognized.
}
if (ret) entry->initialized = true;
else assetsClearEntry(entry);
free(data_buf);
return ret;
}
void assetsGetData(AssetId id, assetsDataEntry **out) {
if (out) *out = NULL;
if (id < 0 || id >= AssetId_Max) return;
assetsDataEntry *entry = &g_assetsDataList[id];
if (buffer) *buffer = entry->buffer;
if (size) *size = entry->size;
u32 pos = g_assetsDataList[id][1].initialized ? 1 : 0;
assetsDataEntry *entry = &g_assetsDataList[id][pos];
if (entry->initialized) *out = entry;
}
u8 *assetsGetDataBuffer(AssetId id) {
u8 *buffer = NULL;
assetsDataEntry *entry = NULL;
assetsGetData(id, &buffer, NULL);
return buffer;
assetsGetData(id, &entry);
return entry ? entry->buffer : NULL;
}

View File

@ -17,12 +17,28 @@ typedef enum {
AssetId_wifi3_icon,
AssetId_eth_icon,
AssetId_eth_none_icon,
AssetId_background_image,
AssetId_Max
AssetId_Max,
} AssetId;
typedef struct {
bool initialized;
u8 *buffer;
size_t size;
ImageMode imageMode;
size_t pixSize;
size_t imageSize[2];
char path[PATH_MAX];
} assetsDataEntry;
Result assetsInit(void);
void assetsExit(void);
void assetsGetData(AssetId id, u8 **buffer, size_t *size);
void assetsClearTheme(void);
bool assetsPhysfsReadFile(const char *path, u8 **data_buf, size_t *filesize, bool nul_term);
bool assetsLoadData(AssetId id, const char *path, int *imageSize);
void assetsGetData(AssetId id, assetsDataEntry **out);
u8 *assetsGetDataBuffer(AssetId id);
bool assetsLoadJpgFromMemory(u8 *indata, size_t indata_size, u8 *outdata, ImageMode imageMode, size_t width, size_t height);

View File

@ -21,6 +21,7 @@
#include <stdint.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
@ -47,6 +48,49 @@ typedef union {
};
} color_t;
typedef enum
{
ThemeLayoutId_Logo,
ThemeLayoutId_HbmenuVersion,
ThemeLayoutId_LoaderInfo,
ThemeLayoutId_AttentionText,
ThemeLayoutId_LogInfo,
ThemeLayoutId_InfoMsg,
ThemeLayoutId_MenuPath,
ThemeLayoutId_MenuTypeMsg,
ThemeLayoutId_MsgBoxSeparator,
ThemeLayoutId_MsgBoxBottomText,
ThemeLayoutId_BackgroundImage,
ThemeLayoutId_BackWave,
ThemeLayoutId_MiddleWave,
ThemeLayoutId_FrontWave,
ThemeLayoutId_ButtonA,
ThemeLayoutId_ButtonAText,
ThemeLayoutId_ButtonB,
ThemeLayoutId_ButtonBText,
ThemeLayoutId_ButtonY,
ThemeLayoutId_ButtonYText,
ThemeLayoutId_ButtonM,
ThemeLayoutId_ButtonMText,
ThemeLayoutId_ButtonX,
ThemeLayoutId_ButtonXText,
ThemeLayoutId_NetworkIcon,
ThemeLayoutId_BatteryCharge,
ThemeLayoutId_BatteryIcon,
ThemeLayoutId_ChargingIcon,
ThemeLayoutId_Status,
ThemeLayoutId_Temperature,
ThemeLayoutId_MenuList,
ThemeLayoutId_MenuListTiles,
ThemeLayoutId_MenuListIcon,
ThemeLayoutId_MenuListName,
ThemeLayoutId_MenuActiveEntryIcon,
ThemeLayoutId_MenuActiveEntryName,
ThemeLayoutId_MenuActiveEntryAuthor,
ThemeLayoutId_MenuActiveEntryVersion,
ThemeLayoutId_Total,
} ThemeLayoutId;
// when building for pc we need to include these separately
#ifndef __SWITCH__
#include "switch/nro.h"
@ -163,6 +207,8 @@ static inline color_t FetchPixelColor(uint32_t x, uint32_t y)
void DrawPixel(uint32_t x, uint32_t y, color_t clr);
void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text);
void DrawTextFromLayout(ThemeLayoutId id, color_t clr, const char* text);
void DrawTextFromLayoutRelative(ThemeLayoutId id, int base_x, int base_y, int *inPos, int *outPos, color_t clr, const char* text, const char align);
void DrawTextTruncate(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text);
void GetTextDimensions(u32 font, const char* text, uint32_t* width_out, uint32_t* height_out);
uint32_t GetTextXCoordinate(u32 font, uint32_t rX, const char* text, const char align);

View File

@ -9,6 +9,10 @@
#define FONT_FACES_MAX 2
#endif
#ifdef __SWITCH__
static bool s_plinited;
#endif
static FT_Error s_font_libret=1, s_font_facesret[FONT_FACES_MAX];
static FT_Library s_font_library;
@ -276,6 +280,38 @@ void DrawText(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text)
DrawText_(font, x, y, clr, text, 0, NULL);
}
void DrawTextFromLayout(ThemeLayoutId id, color_t clr, const char* text)
{
ThemeLayoutObject *obj = &themeCurrent.layoutObjects[id];
if (!obj->visible) return;
DrawText(obj->font, obj->posStart[0], obj->posStart[1], clr, text);
}
void DrawTextFromLayoutRelative(ThemeLayoutId id, int base_x, int base_y, int *inPos, int *outPos, color_t clr, const char* text, const char align)
{
ThemeLayoutObject *obj = &themeCurrent.layoutObjects[id];
base_x = obj->posType ? base_x + inPos[0] : inPos[0];
base_y = obj->posType ? base_y + inPos[1] : inPos[1];
base_x = GetTextXCoordinate(obj->font, base_x, text, align);
if (outPos) {
outPos[0] = base_x;
outPos[1] = base_y;
}
obj->posFinal[0] = base_x;
obj->posFinal[1] = base_y;
if (!obj->visible) return;
GetTextDimensions(obj->font, text, &obj->textSize[0], &obj->textSize[1]);
DrawText(obj->font, base_x, base_y, clr, text);
}
void DrawTextTruncate(u32 font, uint32_t x, uint32_t y, color_t clr, const char* text, uint32_t max_width, const char* end_text)
{
DrawText_(font, x, y, clr, text, max_width, end_text);
@ -332,7 +368,11 @@ bool fontInitialize(void)
PlFontData fonts[PlSharedFontType_Total];
Result rc=0;
rc = plInitialize(PlServiceType_User);
if (R_SUCCEEDED(rc)) {
s_plinited = true;
rc = plGetSharedFont(textGetLanguageCode(), fonts, FONT_FACES_MAX, &s_font_faces_total);
}
if (R_FAILED(rc)) return false;
for (i=0; i<s_font_faces_total; i++) {
@ -378,6 +418,10 @@ void fontExit()
if (s_font_facesret[i]==0) FT_Done_Face(s_font_faces[i]);
if (s_font_libret==0) FT_Done_FreeType(s_font_library);
#ifdef __SWITCH__
if (s_plinited) plExit();
#endif
}
/*Automatically gives you the desired x-coordinate

View File

@ -43,13 +43,15 @@ const char* const g_strings[StrId_Max][17] =
STR_KO("로딩중…"),
STR_RU("загрузка…"),
STR_ZH_HANS("加载中…"),
STR_ZH_HANT("載中…"),
STR_ZH_HANT("中…"),
},
[StrId_AppletMode] =
{
STR_EN("● Applet Mode ●"),
STR_ES("● Modo Applet ●"),
STR_FR("● Mode Applet ●"),
STR_ZH_HANS("● 小程序模式 ●"),
},
[StrId_Directory] =
@ -97,7 +99,7 @@ const char* const g_strings[StrId_Max][17] =
STR_KO("알 수 없는 개발자"),
STR_RU("неизвестный автор"),
STR_ZH_HANS("未知作者"),
STR_ZH_HANT("作者不詳"),
STR_ZH_HANT("作者未知"),
},
[StrId_IOError] =
@ -106,14 +108,14 @@ const char* const g_strings[StrId_Max][17] =
STR_ES("Error de E/S"),
STR_DE("I/O-Fehler"),
STR_FR("Erreur d'E/S"),
STR_IT("Errore di I/O"),
STR_IT("Errore I/O"),
STR_JP("入出力エラー"),
STR_PT("Erro de E/S"),
STR_NL("I/O Fout"),
STR_KO("입출력 오류"),
STR_RU("I/O-ошибка"),
STR_ZH_HANS("读写出错"),
STR_ZH_HANT("讀寫錯誤"),
STR_ZH_HANT("取存錯誤"),
},
[StrId_CouldNotOpenFile] =
@ -129,7 +131,14 @@ const char* const g_strings[StrId_Max][17] =
STR_KO("파일을 열 수 없습니다:\n%s"),
STR_RU("Не могу открыть файл:\n%s"),
STR_ZH_HANS("无法打开文件:\n%s"),
STR_ZH_HANT("開啓檔案失敗:\n%s"),
STR_ZH_HANT("無法開啟檔案:\n%s"),
},
[StrId_NroNotFound] =
{
STR_EN("Could not find executable: %s"),
STR_FR("Impossible trouver l'exécutable : %s"),
STR_ZH_HANS("找不到可执行文件"),
},
[StrId_NoAppsFound_Title] =
@ -145,7 +154,7 @@ const char* const g_strings[StrId_Max][17] =
STR_KO("애플리케이션을 찾을 수 없습니다"),
STR_RU("приложение не найдено"),
STR_ZH_HANS("找不到可执行的自制程序"),
STR_ZH_HANT("未能找到可執行的自製程式"),
STR_ZH_HANT("沒有可執行的自製程式"),
},
[StrId_NoAppsFound_Msg] =
@ -206,14 +215,14 @@ const char* const g_strings[StrId_Max][17] =
"названием switch и она содержит приложения."
),
STR_ZH_HANS(
"内存卡找不到任何可执行的应用程序\n"
"请在内存卡的根目录建立「switch」子目录\n"
"存放自制应用软件至该目录"
"找不到任何自制程序(nro)\n"
"在SD卡根目录建立“switch”文件夹\n"
"将自制程序(nro)放在其中"
),
STR_ZH_HANT(
"記憶體找不到任何可執行的應用程式。\n"
"請在記憶體建立「switch」資料夾\n"
"然後儲存自製軟體到此處"
"記憶卡內沒有可供執行的應用程式。\n"
"請在根目錄下建立「switch」資料夾\n"
"並將自製軟體複製到switch資料夾內"
),
},
@ -223,16 +232,22 @@ const char* const g_strings[StrId_Max][17] =
STR_ES("La última aplicación devolvió un error:"),
STR_DE("Die letzte Anwendung erzeugte einen Fehler:"),
STR_FR("La dernière application a retourné une erreur:"),
STR_IT("L'ultima applicazione ha restituito un errore:"),
STR_JP("直前に実行したアプリでエラーが発生しました:"),
STR_KO("최근 애플리케이션에서 오류가 발생했습니다:"),
STR_ZH_HANT("程式執行時發生錯誤:"),
STR_ZH_HANS("程序运行后出现错误:"),
STR_ZH_HANT("程式執行後出現錯誤:"),
},
[StrId_AppLaunchError] =
{
STR_EN("Failed to launch the application:"),
STR_DE("Konnte die Anwendung nicht starten:"),
STR_FR("Erreur au lancement de l'application"),
STR_FR("Erreur au lancement de l'application:"),
STR_IT("Errore nell'avvio dell'applicazione:"),
STR_ES("No se ha podido iniciar la aplicación:"),
STR_ZH_HANS("运行程序时发生错误:"),
STR_ZH_HANT("執行程式時發生錯誤:"),
},
[StrId_AppInfo_Author] =
@ -263,8 +278,8 @@ const char* const g_strings[StrId_Max][17] =
STR_NL("Versie"),
STR_KO("버전"),
STR_RU("Версия"),
STR_ZH_HANS(""),
STR_ZH_HANT(""),
STR_ZH_HANS(""),
STR_ZH_HANT(""),
},
[StrId_Actions_Launch] =
@ -272,8 +287,8 @@ const char* const g_strings[StrId_Max][17] =
STR_EN("Launch"),
STR_ES("Lanzamiento"),
STR_DE("Starten"),
STR_FR("Lancement"),
STR_IT("Lanciare"),
STR_FR("Lancer"),
STR_IT("Avvia"),
STR_JP("起動"),
STR_PT("Lançamento"),
STR_NL("Lancering"),
@ -289,7 +304,7 @@ const char* const g_strings[StrId_Max][17] =
STR_ES("Abrir"),
STR_DE("Öffnen"),
STR_FR("Ouvrir"),
STR_IT("Aprire"),
STR_IT("Apri"),
STR_JP("開く"),
STR_PT("Abrir"),
STR_NL("Open"),
@ -311,8 +326,8 @@ const char* const g_strings[StrId_Max][17] =
STR_NL("Terug"),
STR_KO("뒤로 가기"),
STR_RU("возвращаться"),
STR_ZH_HANS(""),
STR_ZH_HANT(""),
STR_ZH_HANS(""),
STR_ZH_HANT(""),
},
[StrId_MsgBox_OK] =
@ -320,9 +335,11 @@ const char* const g_strings[StrId_Max][17] =
STR_EN("OK"),
STR_DE("OK"),
STR_FR("OK"),
STR_IT("OK"),
STR_ES("Aceptar"),
STR_JP("了解"),
STR_KO("확인"),
STR_ZH_HANS("确认"),
STR_ZH_HANT("確認"),
},
@ -332,43 +349,55 @@ const char* const g_strings[StrId_Max][17] =
STR_FR("Appliquer"),
STR_DE("Anwenden"),
STR_ES("Aplicar"),
STR_IT("Applica"),
STR_JP("適用"),
STR_KO("적용"),
STR_ZH_HANT("应用"),
STR_ZH_HANS("应用"),
STR_ZH_HANT("套用"),
},
[StrId_Actions_Star] =
{
STR_EN("Star"),
STR_ES("Agregar a favoritos"),
STR_IT("Aggiungi ai preferiti"),
STR_FR("Ajouter aux favoris"),
STR_ZH_HANS("收藏"),
},
[StrId_Actions_Unstar] =
{
STR_EN("Unstar"),
STR_ES("Borrar de favoritos"),
STR_IT("Rimuovi dai preferiti"),
STR_FR("Retirer des favoris"),
STR_ZH_HANS("取消收藏"),
},
[StrId_ThemeMenu] =
{
STR_EN("Theme Menu"),
STR_FR("Menu Thème"),
STR_FR("Menu thèmes"),
STR_DE("Theme Menü"),
STR_ES("Menú temático"),
STR_IT("Tema Menu"),
STR_JP("テーマメニュー"),
STR_KO("테마 메뉴"),
STR_ZH_HANT("主题菜单"),
STR_ZH_HANS("主题菜单"),
STR_ZH_HANT("主題選單"),
},
[StrId_ThemeNotApplied] =
{
STR_EN("Theme cannot be applied because an error occurred."),
STR_EN("Das Theme konnte nicht geladen werden, da ein Fehler aufgetreten ist."),
STR_DE("Das Theme konnte nicht geladen werden, da ein Fehler aufgetreten ist."),
STR_FR("Le thème ne peut pas être appliqué car une erreur est survenue."),
STR_ES("El tema no se pudo aplicar porque se ha producido un error."),
STR_IT("Il tema non è stato applicato a causa di un errore."),
STR_JP("エラーが発生したため、テーマを適用できませんでした。"),
STR_KO("오류가 발생 했기 때문에 테마를 적용할 수 없습니다."),
STR_ZH_HANT("由于发生错误, 无法应用主题。"),
STR_ZH_HANS("由于发生错误, 无法应用主题。"),
STR_ZH_HANT("出現錯誤,無法套用主題。"),
},
[StrId_DefaultThemeName] =
@ -376,6 +405,10 @@ const char* const g_strings[StrId_Max][17] =
STR_EN("Default Theme"),
STR_FR("Thème par défaut"),
STR_DE("Standard Theme"),
STR_IT("Tema di default"),
STR_ES("Tema por defecto"),
STR_ZH_HANS("默认主题"),
STR_ZH_HANT("預設主題"),
},
/*[StrId_Reboot] =
@ -554,7 +587,7 @@ const char* const g_strings[StrId_Max][17] =
STR_DE("Fehler beim Lesen der Titel-Metadaten.\n%08lX%08lX@%d"),
STR_FR(
"Erreur lors de la lecture des métadonnées\n"
"de titre.\n%08lX%08lX@%d"
"du titre.\n%08lX%08lX@%d"
),
STR_IT("Errore nella lettura dei metadata dei titoli.\n%08lX%08lX@%d"),
STR_JP("タイトルメタデータを読み取ることができませんでした。\n%08lX%08lX@%d"),
@ -563,7 +596,7 @@ const char* const g_strings[StrId_Max][17] =
STR_KO("타이틀 메타데이터를 읽는데 실패하였습니다.\n%08lX%08lX@%d"),
STR_RU("Ошибка чтения метаданных заголовка\n.%08lX%08lX@%d"),
STR_ZH_HANS("读取软件相关信息时发生错误:\n%08lX%08lX@%d"),
STR_ZH_HANT("讀取軟體相關數據時發生錯誤:\n%08lX%08lX@%d"),
STR_ZH_HANT("讀取軟體相關資訊時發生錯誤:\n%08lX%08lX@%d"),
},
[StrId_NoTitlesFound] =
@ -705,7 +738,7 @@ const char* const g_strings[StrId_Max][17] =
),
STR_ZH_HANT(
"您所利用漏洞開啓的「自製軟體啓動器」\n"
"無法在當前選中的軟體啓動自製軟\n"
"無法在當前選中的軟體啓動自製軟\n"
"請利用其它漏洞來啓動「自製軟體啓動器」。"
),
},
@ -767,7 +800,7 @@ const char* const g_strings[StrId_Max][17] =
STR_EN("NetLoader"),
STR_ES("Cargador de programas"),
STR_DE("Netzwerk-Loader"),
STR_FR("Chargeur de programme"),
STR_FR("NetLoader"),
STR_IT("Caricamento programmi"),
STR_JP("ネットローダ"),
STR_PT("Carregador de programas"),
@ -783,7 +816,7 @@ const char* const g_strings[StrId_Max][17] =
STR_EN("The NetLoader is currently unavailable."),
STR_ES("El cargador de programas no está disponible."),
STR_DE("Der Netzwerk-Loader ist zur Zeit nicht verfügbar."),
STR_FR("Le chargeur de programme nxlink est indisponible."),
STR_FR("Le programme nxlink est indisponible."),
STR_IT("Il caricamento programmi nxlink non è disponibile."),
STR_JP("nxlinkネットローダは現在利用できません。"),
STR_PT("O carregador de programas está de momento indisponível."),
@ -816,10 +849,11 @@ const char* const g_strings[StrId_Max][17] =
STR_DE("Offline, warte auf Netzwerk…"),
STR_FR("Hors-ligne, en attente d'une connection..."),
STR_IT("Disconnesso, in attesa della connessione…"),
STR_ES("Desconectado, esperando a la red..."),
STR_JP("オフラインです。ネットワーク接続を待っています…"),
STR_KO("연결 끊김, 네트워크 기다리는 중…"),
STR_ZH_HANS("无法连接网络,等待网络连接…"),
STR_ZH_HANT("當前離線,等待網路連線…"),
STR_ZH_HANT("目前已離線,等待網路連線…"),
},
[StrId_NetLoaderActive] =
@ -926,4 +960,3 @@ const char* const g_strings[StrId_Max][17] =
),
},
};

View File

@ -11,6 +11,7 @@ typedef enum
StrId_DefaultPublisher,
StrId_IOError,
StrId_CouldNotOpenFile,
StrId_NroNotFound,
StrId_NoAppsFound_Title,
StrId_NoAppsFound_Msg,

View File

@ -1,4 +1,5 @@
#include "common.h"
#include <physfs.h>
void menuEntryInit(menuEntry_s* me, MenuEntryType type) {
memset(me, 0, sizeof(*me));
@ -80,9 +81,13 @@ static bool menuEntryLoadEmbeddedIcon(menuEntry_s* me) {
return ok;
}
static bool menuEntryLoadExternalIcon(menuEntry_s* me, const char* path) {
static bool menuEntryLoadExternalIcon(menuEntry_s* me, const char* path, bool data_source) {
struct stat st;
if (data_source) {
return assetsPhysfsReadFile(path, &me->icon, &me->icon_size, false);
}
if(stat(path, &st)==-1) return false;
FILE* f = fopen(path, "rb");
@ -106,12 +111,15 @@ static bool menuEntryImportIconGfx(menuEntry_s* me, uint8_t* icon_gfx, uint8_t*
if (icon_gfx == NULL || icon_gfx_small == NULL) return false;
tmpsize = 256*256*3;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
tmpsize = layoutobj->size[0]*layoutobj->size[1]*3;
me->icon_gfx = (uint8_t*)malloc(tmpsize);
if (me->icon_gfx) memcpy(me->icon_gfx, icon_gfx, tmpsize);
if (me->icon_gfx) {
tmpsize = 140*140*3;
tmpsize = layoutobj2->size[0]*layoutobj2->size[1]*3;
me->icon_gfx_small = (uint8_t*)malloc(tmpsize);
if (me->icon_gfx_small) memcpy(me->icon_gfx_small, icon_gfx_small, tmpsize);
@ -202,18 +210,18 @@ static bool menuEntryLoadExternalNacp(menuEntry_s* me, const char* path) {
} while (lastc);
}*/
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists) {
int i=0, tmplen;
menu_s *menu_fileassoc = menuFileassocGetCurrent();
menuEntry_s* fileassoc_me = NULL;
char *strptr = NULL;
static char tempbuf[PATH_MAX+1];
char tempbuf[PATH_MAX+16];
//bool isOldAppFolder = false;
if (!fsobjExists(me->path)) return false;
if (check_exists && !fsobjExists(me->path)) return false;
tempbuf[PATH_MAX] = 0;
strcpy(me->name, name);
strncpy(me->name, name, sizeof(me->name)-1);
if (me->type == ENTRY_TYPE_FOLDER)
{
@ -285,9 +293,8 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
if (me->type == ENTRY_TYPE_FILE)
{
//strcpy(me->name, name);//This is already done before both if statements
strcpy(me->author, textGetString(StrId_DefaultPublisher));
strcpy(me->version, "1.0.0");
strncpy(me->author, textGetString(StrId_DefaultPublisher), sizeof(me->author)-1);
strncpy(me->version, "1.0.0", sizeof(me->version)-1);
//shortcut_s sc;
@ -324,7 +331,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
char* ext = getExtension(tempbuf);
strcpy(ext, ".jpg");
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf);
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) break;
if (isOldAppFolder)
@ -332,7 +339,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
char* slash = getSlash(tempbuf);
strcpy(slash, "/icon.jpg");
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf);
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) break;
}*/
@ -405,7 +412,44 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
*author = textGetString(StrId_DefaultPublisher),
*version = "1.0.0";
if (config_read_file(&cfg, me->path)) {
const char* cfg_path = me->path;
const char* theme_archive_path = NULL;
const char* ext = getExtension(me->path);
bool good_cfg = false;
bool is_archive = false;
#ifdef __SWITCH__
bool is_romfs = false;
if (strcasecmp(ext, ".romfs")==0) {
if (R_FAILED(romfsMountFromFsdev(me->path, 0, "themetmp")))
return false;
is_romfs = true;
cfg_path = "themetmp:/theme.cfg";
theme_archive_path = "themetmp:/";
}
#endif
if (strcasecmp(ext, ".romfs")!=0 && strcasecmp(ext, ".cfg")!=0) {
theme_archive_path = me->path;
}
if (theme_archive_path) {
if (!PHYSFS_mount(theme_archive_path, "themetmp", 0)) cfg_path = NULL;
else {
is_archive = true;
cfg_path = "themetmp/theme.cfg";
}
}
if (cfg_path) {
if (!is_archive) good_cfg = config_read_file(&cfg, cfg_path);
else {
u8 *cfg_buf = NULL;
good_cfg = assetsPhysfsReadFile(cfg_path, &cfg_buf, NULL, true);
if (good_cfg) good_cfg = config_read_string(&cfg, (char*)cfg_buf);
free(cfg_buf);
}
}
if (good_cfg) {
themeInfo = config_lookup(&cfg, "themeInfo");
if (themeInfo != NULL) {
if(config_setting_lookup_string(themeInfo, "name", &name))
@ -418,6 +462,20 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
strncpy(me->author, author, sizeof(me->author)-1);
strncpy(me->version, version, sizeof(me->version)-1);
config_destroy(&cfg);
if (good_cfg && is_archive) {
bool iconLoaded = false;
iconLoaded = menuEntryLoadExternalIcon(me, "themetmp/icon.jpg", true);
if (iconLoaded) menuEntryParseIcon(me);
}
if (is_archive) PHYSFS_unmount(theme_archive_path);
#ifdef __SWITCH__
if (is_romfs) romfsUnmount("themetmp");
#endif
}
if (me->type == ENTRY_TYPE_FILE_OTHER)
@ -449,7 +507,7 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
bool iconLoaded = false;
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf);
iconLoaded = menuEntryLoadExternalIcon(me, tempbuf, false);
if (iconLoaded) menuEntryParseIcon(me);
@ -478,9 +536,28 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
// Initialize the argument data
argData_s* ad = &me->args;
argData_s* ad_assoc = &fileassoc_me->args;
char *arg_src = (char*)&ad_assoc->buf[1];
bool ftoken_found=0;
ad->dst = (char*)&ad->buf[1];
launchAddArg(ad, fileassoc_me->path);
launchAddArg(ad, me->path);
for (u32 argi=0; argi<ad_assoc->buf[0]; argi++, arg_src+= strlen(arg_src)+1) {
if (argi) {
strptr = strchr(arg_src, '%');
if (strptr && strptr[0] && strptr[1] && (strptr == arg_src || strptr[-1] != '\\')) {
if (strptr[1] == 'f') {
memset(tempbuf, 0, sizeof(tempbuf));
snprintf(tempbuf, sizeof(tempbuf)-1, "%.*s%s%s", (int)((uintptr_t)strptr-(uintptr_t)arg_src), arg_src, me->path, &strptr[2]);
launchAddArg(ad, tempbuf);
ftoken_found = 1;
continue;
}
}
}
launchAddArg(ad, arg_src);
}
if (!ftoken_found) launchAddArg(ad, me->path);
strncpy(me->path, fileassoc_me->path, sizeof(me->path));
me->path[sizeof(me->path)-1] = 0;
@ -501,12 +578,12 @@ bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut) {
}
void menuEntryFileassocLoad(const char* filepath) {
bool success=0, success2=0;
bool success=0, iconLoaded=0;
menuEntry_s* me = NULL;
config_setting_t *fileassoc = NULL, *targets = NULL, *target = NULL;
config_setting_t *fileassoc = NULL, *targets = NULL, *target = NULL, *app_args = NULL, *target_args = NULL;
config_t cfg = {0};
int targets_len=0, i;
int targets_len=0, args_len=0, i;
const char *strptr = NULL;
char app_path[PATH_MAX+8];
@ -515,8 +592,8 @@ void menuEntryFileassocLoad(const char* filepath) {
char target_file_extension[PATH_MAX+1];
char target_filename[PATH_MAX+1];
char app_author[ENTRY_AUTHORLENGTH+1];
char app_version[ENTRY_VERLENGTH+1];
char app_author[ENTRY_AUTHORLENGTH+2];
char app_version[ENTRY_VERLENGTH+2];
uint8_t *app_icon_gfx = NULL;
uint8_t *app_icon_gfx_small = NULL;
@ -538,7 +615,8 @@ void menuEntryFileassocLoad(const char* filepath) {
snprintf(app_path, sizeof(app_path)-1, "%s%s", menuGetRootBasePath(), strptr);
if (config_setting_lookup_string(fileassoc, "icon_path", &strptr))
snprintf(main_icon_path, sizeof(main_icon_path)-1, "%s%s", menuGetRootBasePath(), strptr);
targets = config_setting_get_member(fileassoc, "targets");
app_args = config_setting_lookup(fileassoc, "app_args");
targets = config_setting_lookup(fileassoc, "targets");
if (app_path[0] && targets) {
targets_len = config_setting_length(targets);
@ -554,7 +632,7 @@ void menuEntryFileassocLoad(const char* filepath) {
strptr = getSlash(app_path);
if(strptr[0] == '/') strptr++;
if (menuEntryLoad(me, strptr, 0)) {
if (menuEntryLoad(me, strptr, 0, true)) {
strncpy(app_author, me->author, sizeof(app_author));
app_author[sizeof(app_author)-1] = 0;
strncpy(app_version, me->version, sizeof(app_version));
@ -584,12 +662,13 @@ void menuEntryFileassocLoad(const char* filepath) {
strncpy(target_file_extension, strptr, sizeof(target_file_extension)-1);
if (config_setting_lookup_string(target, "filename", &strptr))
strncpy(target_filename, strptr, sizeof(target_filename)-1);
target_args = config_setting_lookup(target, "app_args");
//string_is_set for target_file_extension and target_filename must differ: only 1 can be set, not both set or both not set.
if ((target_file_extension[0]!=0) == (target_filename[0]!=0)) continue;
me = menuCreateEntry(ENTRY_TYPE_FILEASSOC);
success2 = 0;
iconLoaded = 0;
if (me) {
strncpy(me->path, app_path, sizeof(me->path));
@ -608,22 +687,32 @@ void menuEntryFileassocLoad(const char* filepath) {
}
me->fileassoc_str[sizeof(me->fileassoc_str)-1] = 0;
if (target_icon_path[0]) success2 = menuEntryLoadExternalIcon(me, target_icon_path);
if (!success2 && main_icon_path[0]) success2 = menuEntryLoadExternalIcon(me, main_icon_path);
if (target_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, target_icon_path, false);
if (!iconLoaded && main_icon_path[0]) iconLoaded = menuEntryLoadExternalIcon(me, main_icon_path, false);
if (success2) {
if (iconLoaded) {
menuEntryParseIcon(me);
} else {
success2 = menuEntryImportIconGfx(me, app_icon_gfx, app_icon_gfx_small);
iconLoaded = menuEntryImportIconGfx(me, app_icon_gfx, app_icon_gfx_small);
}
argData_s* ad = &me->args;
ad->dst = (char*)&ad->buf[1];
launchAddArg(ad, me->path);
config_setting_t *config_args = target_args ? target_args : app_args;
if (config_args) {
args_len = config_setting_length(config_args);
for (int argi=0; argi<args_len; argi++) {
strptr = config_setting_get_string_elem(config_args, argi);
if (strptr==NULL) continue;
launchAddArg(ad, strptr);
}
}
}
if (me) {
if (success2)
menuFileassocAddEntry(me);
else
menuDeleteEntry(me, 0);
}
if (me) menuFileassocAddEntry(me);
}
}
}
@ -642,65 +731,33 @@ void menuEntryFileassocLoad(const char* filepath) {
void menuEntryParseIcon(menuEntry_s* me) {
if (me->icon_size==0 || me->icon==NULL) return;
int w,h,samp;
size_t imagesize = 256*256*3;
me->icon_gfx = (uint8_t*)malloc(imagesize);
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
if (me->icon_gfx == NULL) {
me->icon_size = 0;
free(me->icon);
me->icon = NULL;
return;
}
size_t imagesize = layoutobj->imageSize[0]*layoutobj->imageSize[1]*3;
bool ret=true;
uint8_t *tmp_gfx = (uint8_t*)malloc(imagesize);
tjhandle _jpegDecompressor = tjInitDecompress();
if (tmp_gfx == NULL) ret = false;
if (_jpegDecompressor == NULL) {
free(me->icon_gfx);
me->icon_gfx = NULL;
if (ret) ret = assetsLoadJpgFromMemory(me->icon, me->icon_size, tmp_gfx, IMAGE_MODE_RGB24, layoutobj->imageSize[0], layoutobj->imageSize[1]);
me->icon_size = 0;
free(me->icon);
me->icon = NULL;
return;
}
if (ret) me->icon_gfx = downscaleImg(tmp_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], IMAGE_MODE_RGB24);
if (tjDecompressHeader2(_jpegDecompressor, me->icon, me->icon_size, &w, &h, &samp) == -1) {
free(me->icon_gfx);
me->icon_gfx = NULL;
me->icon_size = 0;
free(me->icon);
me->icon = NULL;
tjDestroy(_jpegDecompressor);
return;
}
if (w != 256 || h != 256 ) return;
if (tjDecompress2(_jpegDecompressor, me->icon, me->icon_size, me->icon_gfx, w, 0, h, TJPF_RGB, TJFLAG_ACCURATEDCT) == -1) {
free(me->icon_gfx);
me->icon_gfx = NULL;
me->icon_size = 0;
free(me->icon);
me->icon = NULL;
tjDestroy(_jpegDecompressor);
return;
}
if (ret && me->icon_gfx==NULL) ret = false;
me->icon_size = 0;
free(me->icon);
me->icon = NULL;
tjDestroy(_jpegDecompressor);
if (ret) me->icon_gfx_small = downscaleImg(tmp_gfx, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], IMAGE_MODE_RGB24);
me->icon_gfx_small = downscaleImg(me->icon_gfx, 256, 256, 140, 140, IMAGE_MODE_RGB24);
if (me->icon_gfx_small == NULL) {
if (!ret || me->icon_gfx_small == NULL) {
free(me->icon_gfx);
me->icon_gfx = NULL;
}
free(tmp_gfx);
}
uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode) {
@ -720,6 +777,11 @@ uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int des
return NULL;
}
if (srcWidth == destWidth && srcHeight == destHeight) {
memcpy(out, image, destWidth*destHeight*(mode==IMAGE_MODE_RGBA32 ? 4 : 3));
return out;
}
int tmpx, tmpy;
int pos;
float sourceX, sourceY;

View File

@ -35,6 +35,7 @@ static void _menuAddEntry(menu_s *m, menuEntry_s* me) {
m->lastEntry = me;
}
m->xPos = 0;
m->slideSpeed = 0;
m->nEntries ++;
}
@ -59,6 +60,7 @@ static void menuAddEntryToFront(menuEntry_s* me) {
m->lastEntry = me;
}
m->xPos = 0;
m->slideSpeed = 0;
m->nEntries ++;
}
@ -207,7 +209,7 @@ int menuScan(const char* target) {
strncpy(me->path, tmp_path, sizeof(me->path)-1);
me->path[sizeof(me->path)-1] = 0;
if (menuEntryLoad(me, dp->d_name, shortcut))
if (menuEntryLoad(me, dp->d_name, shortcut, true))
menuAddEntry(me);
else
menuDeleteEntry(me, 0);
@ -244,8 +246,23 @@ int themeMenuScan(const char* target) {
memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/%s", s_menu[!s_curMenu].dirname, dp->d_name);
bool entrytype=0;
#ifdef _DIRENT_HAVE_D_TYPE
if (dp->d_type == DT_UNKNOWN)
continue;
entrytype = dp->d_type != DT_REG;
#else
struct stat tmpstat;
if(stat(tmp_path, &tmpstat)==-1)
continue;
entrytype = (tmpstat.st_mode & S_IFMT) != S_IFREG;
#endif
const char* ext = getExtension(dp->d_name);
if (strcasecmp(ext, ".cfg")==0)
if (entrytype || strcasecmp(ext, ".cfg")==0 || strcasecmp(ext, ".romfs")==0 || strcasecmp(ext, ".zip")==0)
me = menuCreateEntry(ENTRY_TYPE_THEME);
if (!me)
@ -253,7 +270,7 @@ int themeMenuScan(const char* target) {
strncpy(me->path, tmp_path, sizeof(me->path)-1);
me->path[sizeof(me->path)-1] = 0;
if (menuEntryLoad(me, dp->d_name, shortcut))
if (menuEntryLoad(me, dp->d_name, shortcut, true))
menuAddEntry(me);
else
menuDeleteEntry(me, 0);
@ -265,7 +282,7 @@ int themeMenuScan(const char* target) {
menuEntry_s* me = menuCreateEntry(ENTRY_TYPE_THEME);
if(me) {
if(menuEntryLoad(me, textGetString(StrId_DefaultThemeName), false))//Create Default theme Menu Entry
if(menuEntryLoad(me, textGetString(StrId_DefaultThemeName), false, false))//Create Default theme Menu Entry
menuAddEntryToFront(me);
else
menuDeleteEntry(me, 0);

View File

@ -6,10 +6,19 @@
#include "switch/runtime/nxlink.h"
#endif
double menuTimer;
char rootPathBase[PATH_MAX];
char rootPath[PATH_MAX+8];
uint8_t *folder_icon_large, *folder_icon_small;
uint8_t *invalid_icon_large, *invalid_icon_small;
uint8_t *theme_icon_large, *theme_icon_small;
void computeFrontGradient(color_t baseColor, int height);
void menuLoadFileassoc(void);
char *menuGetRootPath(void) {
return rootPath;
}
@ -113,12 +122,63 @@ void menuHandleXButton(void) {
}
}
void menuStartupCommon(void) {
free(folder_icon_large);
free(folder_icon_small);
free(invalid_icon_large);
free(invalid_icon_small);
free(theme_icon_large);
free(theme_icon_small);
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
ThemeLayoutObject *layoutobj2 = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
assetsDataEntry *data = NULL;
assetsGetData(AssetId_folder_icon, &data);
folder_icon_large = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], data->imageMode);
folder_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], data->imageMode);
assetsGetData(AssetId_invalid_icon, &data);
invalid_icon_large = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], data->imageMode);
invalid_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], data->imageMode);
if(themeGlobalPreset == THEME_PRESET_DARK)
assetsGetData(AssetId_theme_icon_dark, &data);
else
assetsGetData(AssetId_theme_icon_light, &data);
theme_icon_large = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj->size[0], layoutobj->size[1], data->imageMode);
theme_icon_small = downscaleImg(data->buffer, layoutobj->imageSize[0], layoutobj->imageSize[1], layoutobj2->size[0], layoutobj2->size[1], data->imageMode);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave];
computeFrontGradient(themeCurrent.frontWaveColor, layoutobj->size[1]);
}
void menuThemeSelectCurrentEntry(void) {
menu_s* menu = menuGetCurrent();
char themePath[PATH_MAX] = {0};
GetThemePathFromConfig(themePath, PATH_MAX);
if (themePath[0]==0) menu->curEntry = 0;
else {
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; me != NULL; i ++, me = me->next) {
if (strcmp(me->path, themePath)==0) {
menu->curEntry = i;
break;
}
}
}
}
void launchApplyThemeTask(menuEntry_s* arg) {
const char* themePath = arg->path;
menu_s* menu = menuGetCurrent();
SetThemePathToConfig(themePath);
themeStartup(themeGlobalPreset);
computeFrontGradient(themeCurrent.frontWaveColor, 280);
menuStartupCommon();
menuLoadFileassoc();
if (hbmenu_state == HBMENU_THEME_MENU) { // Normally this should never be used outside of theme-menu.
themeMenuScan(menu->dirname);
menuThemeSelectCurrentEntry();
} else menuScan(menu->dirname);
}
bool menuIsNetloaderActive(void) {
@ -165,16 +225,13 @@ static void drawIcon(int x, int y, int width, int height, const uint8_t *image,
}
}
uint8_t *folder_icon_small;
uint8_t *invalid_icon_small;
uint8_t *theme_icon_small;
static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
int x, y;
int start_y = 720 - 100 - 145;//*(n % 2);
int end_y = start_y + 140 + 32;
int start_y = layoutobj->posStart[1];//*(n % 2);
int end_y = start_y + layoutobj->size[1];
int start_x = off_x;//(n / 2);
int end_x = start_x + 140;
int end_x = start_x + layoutobj->size[0];
int j;
const uint8_t *smallimg = NULL;
@ -195,7 +252,7 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
if (is_active) {
highlight_multiplier = fmax(0.0, fabs(fmod(menuTimer, 1.0) - 0.5) / 0.5);
border_color = MakeColor(themeCurrent.highlightColor.r + (255 - themeCurrent.highlightColor.r) * highlight_multiplier, themeCurrent.highlightColor.g + (255 - themeCurrent.highlightColor.g) * highlight_multiplier, themeCurrent.highlightColor.b + (255 - themeCurrent.highlightColor.b) * highlight_multiplier, 255);
border_color = MakeColor(themeCurrent.highlightColor.r + (themeCurrent.highlightGradientEdgeColor.r - themeCurrent.highlightColor.r) * highlight_multiplier, themeCurrent.highlightColor.g + (themeCurrent.highlightGradientEdgeColor.g - themeCurrent.highlightColor.g) * highlight_multiplier, themeCurrent.highlightColor.b + (themeCurrent.highlightGradientEdgeColor.b - themeCurrent.highlightColor.b) * highlight_multiplier, 255);
border_start_x = start_x-6;
border_end_x = end_x+6;
border_start_y = start_y-5;
@ -273,29 +330,29 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
}
else if (me->type == ENTRY_TYPE_FOLDER) {
smallimg = folder_icon_small;
largeimg = assetsGetDataBuffer(AssetId_folder_icon);
largeimg = folder_icon_large;
}
else if (me->type == ENTRY_TYPE_THEME){
smallimg = theme_icon_small;
if(themeGlobalPreset == THEME_PRESET_DARK)
largeimg = assetsGetDataBuffer(AssetId_theme_icon_dark);
else largeimg = assetsGetDataBuffer(AssetId_theme_icon_light);
largeimg = theme_icon_large;
}
else {
smallimg = invalid_icon_small;
largeimg = assetsGetDataBuffer(AssetId_invalid_icon);
largeimg = invalid_icon_large;
}
if (smallimg) {
drawImage(start_x, start_y + 32, 140, 140, smallimg, IMAGE_MODE_RGB24);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
drawImage(start_x + layoutobj->posStart[0], start_y + layoutobj->posStart[1], layoutobj->size[0], layoutobj->size[1], smallimg, IMAGE_MODE_RGB24);
}
if (is_active && largeimg) {
drawImage(117, 100+10, 256, 256, largeimg, IMAGE_MODE_RGB24);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
if (is_active && largeimg && layoutobj->visible) {
drawImage(layoutobj->posStart[0], layoutobj->posStart[1], layoutobj->size[0], layoutobj->size[1], largeimg, IMAGE_MODE_RGB24);
shadow_start_y = 100+10+256;
border_start_x = 117;
border_end_x = 117+256;
shadow_start_y = layoutobj->posStart[1]+layoutobj->size[1];
border_start_x = layoutobj->posStart[0];
border_end_x = layoutobj->posStart[0]+layoutobj->size[0];
for (shadow_y=shadow_start_y; shadow_y <shadow_start_y+shadow_size; shadow_y++) {
for (x=border_start_x; x<border_end_x; x++) {
@ -317,21 +374,23 @@ static void drawEntry(menuEntry_s* me, int off_x, int is_active) {
memset(tmpstr, 0, sizeof(tmpstr));
snprintf(tmpstr, sizeof(tmpstr)-1, "%s%s", strptr, me->name);
DrawTextTruncate(interuiregular14, start_x + 4, start_y + 4 + 18, themeCurrent.borderTextColor, tmpstr, 140 - 32, "...");
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListName];
DrawTextTruncate(layoutobj->font, start_x + layoutobj->posStart[0], start_y + layoutobj->posStart[1], themeCurrent.borderTextColor, tmpstr, layoutobj->size[0], "...");
if (is_active) {
start_x = 1280 - 790;
start_y = 135+10;
DrawTextTruncate(interuimedium30, start_x, start_y + 39, themeCurrent.textColor, tmpstr, 1280 - start_x - 120 ,"...");
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryName];
if (layoutobj->visible) DrawTextTruncate(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr, layoutobj->size[0], "...");
if (me->type != ENTRY_TYPE_FOLDER) {
memset(tmpstr, 0, sizeof(tmpstr));
snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Author), me->author);
DrawText(interuiregular14, start_x, start_y + 28 + 30 + 18, themeCurrent.textColor, tmpstr);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryAuthor];
if (layoutobj->visible) DrawText(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr);
memset(tmpstr, 0, sizeof(tmpstr));
snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Version), me->version);
DrawText(interuiregular14, start_x, start_y + 28 + 30 + 18 + 6 + 18, themeCurrent.textColor, tmpstr);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryVersion];
if (layoutobj->visible) DrawText(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, tmpstr);
}
}
}
@ -344,6 +403,8 @@ void computeFrontGradient(color_t baseColor, int height) {
float dark_mult, dark_sub = 75;
color_t color;
if (height < 0 || height > 720) return;
for (y=0; y<720; y++) {
alpha = y - (720 - height);
@ -392,22 +453,20 @@ void menuStartupPath(void) {
}
}
void menuStartup(void) {
void menuLoadFileassoc(void) {
char tmp_path[PATH_MAX+28];
memset(tmp_path, 0, sizeof(tmp_path)-1);
snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/fileassoc", rootPathBase);
menuFileassocScan(tmp_path);
}
void menuStartup(void) {
menuLoadFileassoc();
menuScan(rootPath);
folder_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_folder_icon), 256, 256, 140, 140, IMAGE_MODE_RGB24);
invalid_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_invalid_icon), 256, 256, 140, 140, IMAGE_MODE_RGB24);
if(themeGlobalPreset == THEME_PRESET_DARK)
theme_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_theme_icon_dark), 256, 256, 140, 140, IMAGE_MODE_RGB24);
else
theme_icon_small = downscaleImg(assetsGetDataBuffer(AssetId_theme_icon_light), 256, 256, 140, 140, IMAGE_MODE_RGB24);
computeFrontGradient(themeCurrent.frontWaveColor, 280);
//menuCreateMsgBox(780, 300, "This is a test");
menuStartupCommon();
}
void themeMenuStartup(void) {
@ -418,6 +477,7 @@ void themeMenuStartup(void) {
snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s%s%s%s%s%s", rootPathBase, DIRECTORY_SEPARATOR, "config", DIRECTORY_SEPARATOR, "nx-hbmenu" , DIRECTORY_SEPARATOR, "themes");
themeMenuScan(tmp_path);
menuThemeSelectCurrentEntry();
}
color_t waveBlendAdd(color_t a, color_t b, float alpha) {
@ -429,6 +489,7 @@ void drawWave(int id, float timer, color_t color, int height, float phase, float
float wave_top_y, alpha, one_minus_alpha;
color_t existing_color, new_color;
if (height < 0 || height > 720) return;
height = 720 - height;
for (x=0; x<1280; x++) {
@ -478,17 +539,30 @@ void drawCharge() {
sprintf(chargeString, "%d%%", batteryCharge);
int tmpX = GetTextXCoordinate(interuiregular14, 1180 - 10, chargeString, 'r');
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BatteryCharge];
DrawText(interuiregular14, tmpX - 24 - 8, 0 + 47 + 10 + 21 + 4, themeCurrent.textColor, chargeString);
drawIcon(1180 - 8 - 24 - 8, 0 + 47 + 10 + 6, 24, 24, assetsGetDataBuffer(AssetId_battery_icon), themeCurrent.textColor);
if (isCharging)
drawIcon(1180 - 20, 0 + 47 + 10 + 6, 24, 24, assetsGetDataBuffer(AssetId_charging_icon), themeCurrent.textColor);
if (layoutobj->visible) {
int tmpX = GetTextXCoordinate(layoutobj->font, layoutobj->posStart[0], chargeString, 'r');
DrawText(layoutobj->font, tmpX, layoutobj->posStart[1], themeCurrent.textColor, chargeString);
}
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BatteryIcon];
assetsDataEntry *data = NULL;
assetsGetData(AssetId_battery_icon, &data);
if (layoutobj->visible) drawIcon(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.textColor);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ChargingIcon];
assetsGetData(AssetId_charging_icon, &data);
if (isCharging && layoutobj->visible)
drawIcon(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.textColor);
}
}
void drawNetwork(int tmpX, AssetId id) {
drawIcon(tmpX, 0 + 47 + 10 + 3, 24, 24, assetsGetDataBuffer(id), themeCurrent.textColor);
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_NetworkIcon];
assetsDataEntry *data = NULL;
assetsGetData(id, &data);
if (layoutobj->visible) drawIcon(layoutobj->posType ? tmpX + layoutobj->posStart[0] : layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.textColor);
}
u32 drawStatus() {
@ -508,31 +582,31 @@ u32 drawStatus() {
snprintf(tmpstr, sizeof(tmpstr)-1, "%02d:%02d:%02d", hours, minutes, seconds);
u32 tmpX = GetTextXCoordinate(interuimedium20, 1180, tmpstr, 'r');
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_Status];
DrawText(interuimedium20, tmpX, 0 + 47 + 10, themeCurrent.textColor, tmpstr);
u32 tmpX = GetTextXCoordinate(layoutobj->font, layoutobj->posStart[0], tmpstr, 'r');
if (layoutobj->visible) DrawText(layoutobj->font, tmpX, layoutobj->posStart[1], themeCurrent.textColor, tmpstr);
drawCharge();
if (statusGet(&netstatusFlag, &id, &temperatureFlag, &temperature)) {
if (netstatusFlag) drawNetwork(tmpX, id);
if (temperatureFlag) {
snprintf(tmpstr, sizeof(tmpstr)-1, "%.1f°C", ((float)temperature) / 1000);
DrawText(interuiregular14, 1180 + 4, 0 + 47 + 10 + + 21 + 6, themeCurrent.textColor, tmpstr);
snprintf(tmpstr, sizeof(tmpstr)-1, "%d°C", temperature);
DrawTextFromLayout(ThemeLayoutId_Temperature, themeCurrent.textColor, tmpstr);
}
}
return tmpX;
}
void drawButtons(menu_s* menu, bool emptyDir, int *x_image_out) {
int x_image = 1280 - 252 - 30 - 32;
int x_text = 1280 - 216 - 30 - 32;
void drawButtons(menu_s* menu, bool emptyDir, int *out_basePos) {
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA];
int basePos[2]={0};
if(emptyDir) {
x_image = 1280 - 126 - 30 - 32;
x_text = 1280 - 90 - 30 - 32;
}
basePos[0] = layoutobj->posStart[0];
basePos[1] = layoutobj->posStart[1];
#ifdef __SWITCH__
if (strcmp( menu->dirname, "sdmc:/") != 0)
@ -540,27 +614,26 @@ void drawButtons(menu_s* menu, bool emptyDir, int *x_image_out) {
if (strcmp( menu->dirname, "/") != 0)
#endif
{
//drawImage(x_image, 720 - 48, 32, 32, themeCurrent.buttonBImage, IMAGE_MODE_RGBA32);
DrawText(fontscale7, x_image, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonBText);//Display the 'B' button from SharedFont.
DrawText(interuiregular18, x_text, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Back));
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonBText];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonBText, basePos[0], basePos[1], !emptyDir ? layoutobj->posStart : layoutobj->posEnd, basePos, themeCurrent.textColor, textGetString(StrId_Actions_Back), 'l');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonB];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonB, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonBText, 'l');
}
if(hbmenu_state == HBMENU_DEFAULT)
{
x_text = GetTextXCoordinate(interuiregular18, x_image - 32, textGetString(StrId_NetLoader), 'r');
x_image = x_text - 36;
*x_image_out = x_image - 40;
DrawText(fontscale7, x_image, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonYText);
DrawText(interuiregular18, x_text, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_NetLoader));
x_text = GetTextXCoordinate(interuiregular18, x_image - 32, textGetString(StrId_ThemeMenu), 'r');
x_image = x_text - 36;
*x_image_out = x_image - 40;
DrawText(fontscale7, x_image, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonMText);
DrawText(interuiregular18, x_text, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_ThemeMenu));
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonYText];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonYText, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, textGetString(StrId_NetLoader), 'r');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonY];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonY, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonYText, 'l');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonMText];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonMText, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, textGetString(StrId_ThemeMenu), 'r');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonM];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonM, basePos[0], basePos[1], layoutobj->posStart, basePos, themeCurrent.textColor, themeCurrent.buttonMText, 'l');
}
out_basePos[0] = basePos[0];
out_basePos[1] = basePos[1];
}
void menuUpdateNetloader(netloaderState *netloader_state) {
@ -595,44 +668,77 @@ void menuLoop(void) {
menuEntry_s* me;
menu_s* menu = NULL;
int i;
int x, y;
int menupath_x_endpos = 918 + 40;
int x, y, endy = 720;
int curPos[2]={0};
netloaderState netloader_state;
ThemeLayoutObject *layoutobj = NULL;
for (y=0; y<450; y++) {
for (i=0; i<3; i++) {
if (i==2) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackWave];
if (i==1) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave];
if (i==0) layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave];
if (layoutobj->visible && layoutobj->size[1] >= 0 && layoutobj->size[1] <= 720-10) {
endy = 720 - layoutobj->size[1] + 10;
break;
}
}
for (y=0; y<endy; y++) {
for (x=0; x<1280; x+=4) {// don't draw bottom pixels as they are covered by the waves
Draw4PixelsRaw(x, y, themeCurrent.backgroundColor);
}
}
drawWave(0, menuTimer, themeCurrent.backWaveColor, 295, 0.0, 3.0);
drawWave(1, menuTimer, themeCurrent.middleWaveColor, 290, 2.0, 3.5);
drawWave(2, menuTimer, themeCurrent.frontWaveColor, 280, 4.0, -2.5);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackgroundImage];
assetsDataEntry *data = NULL;
assetsGetData(AssetId_background_image, &data);
if (layoutobj->visible && data) drawImage(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], endy < data->imageSize[1] ? endy : data->imageSize[1], data->buffer, data->imageMode);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_BackWave];
if (layoutobj->visible) drawWave(0, menuTimer, themeCurrent.backWaveColor, layoutobj->size[1], 0.0, 3.0);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave];
if (layoutobj->visible) drawWave(1, menuTimer, themeCurrent.middleWaveColor, layoutobj->size[1], 2.0, 3.5);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave];
if (layoutobj->visible) drawWave(2, menuTimer, themeCurrent.frontWaveColor, layoutobj->size[1], 4.0, -2.5);
menuTimer += 0.05;
drawImage(40, 20, 140, 60, themeCurrent.hbmenuLogoImage, IMAGE_MODE_RGBA32);
DrawText(interuiregular14, 184, 46 + 18, themeCurrent.textColor, VERSION);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_Logo];
if(themeGlobalPreset == THEME_PRESET_DARK)
assetsGetData(AssetId_hbmenu_logo_dark, &data);
else assetsGetData(AssetId_hbmenu_logo_light, &data);
if (layoutobj->visible) {
if (!themeCurrent.logoColor_set)
drawImage(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, data->imageMode);
else {
drawIcon(layoutobj->posStart[0], layoutobj->posStart[1], data->imageSize[0], data->imageSize[1], data->buffer, themeCurrent.logoColor);
}
}
DrawTextFromLayout(ThemeLayoutId_HbmenuVersion, themeCurrent.textColor, VERSION);
u32 statusXPos = drawStatus();
#ifdef __SWITCH__
AppletType at = appletGetAppletType();
if (at != AppletType_Application && at != AppletType_SystemApplication) {
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_AttentionText];
if (at != AppletType_Application && at != AppletType_SystemApplication && layoutobj->visible) {
const char* appletMode = textGetString(StrId_AppletMode);
u32 x_pos = GetTextXCoordinate(interuimedium30, statusXPos, appletMode, 'r');
DrawText(interuimedium30, x_pos - 32, 46 + 18, themeCurrent.attentionTextColor, appletMode);
u32 x_pos = GetTextXCoordinate(layoutobj->font, statusXPos, appletMode, 'r');
DrawText(layoutobj->font, layoutobj->posType ? x_pos + layoutobj->posStart[0] : layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.attentionTextColor, appletMode);
}
const char* loaderInfo = envGetLoaderInfo();
if (loaderInfo) {
u32 x_pos = 43;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_LoaderInfo];
if (loaderInfo && layoutobj->visible) {
u32 x_pos = layoutobj->posStart[0];
char* spacePos = strchr(loaderInfo, ' ');
if (spacePos) {
char tempbuf[64] = {0};
size_t tempsize = spacePos - loaderInfo + 1;
if (tempsize > sizeof(tempbuf)-1) tempsize = sizeof(tempbuf)-1;
memcpy(tempbuf, loaderInfo, tempsize);
x_pos = GetTextXCoordinate(interuiregular14, 184, tempbuf, 'r');
x_pos = GetTextXCoordinate(layoutobj->font, layoutobj->posEnd[0], tempbuf, 'r');
}
DrawText(interuiregular14, x_pos, 46 + 18 + 20, themeCurrent.textColor, loaderInfo);
DrawText(layoutobj->font, x_pos, layoutobj->posStart[1], themeCurrent.textColor, loaderInfo);
}
#endif
@ -642,7 +748,7 @@ void menuLoop(void) {
char tmpstr[64];
snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_frame);
DrawText(interuiregular14, 180 + 256, 46 + 16 + 18, themeCurrent.textColor, tmpstr);
DrawTextFromLayout(ThemeLayoutId_LogInfo, themeCurrent.textColor, tmpstr);
#endif
memset(&netloader_state, 0, sizeof(netloader_state));
@ -659,8 +765,11 @@ void menuLoop(void) {
menuCloseMsgBox();
menuMsgBoxSetNetloaderState(0, NULL, 0, 0);
}
if (netloader_state.errormsg[0]) menuCreateMsgBox(780,300, netloader_state.errormsg);
if (netloader_state.errormsg[0]) {
menuCloseMsgBox();
menuCreateMsgBox(780,300, netloader_state.errormsg);
}
if(hbmenu_state == HBMENU_NETLOADER_ACTIVE) {
@ -680,16 +789,21 @@ void menuLoop(void) {
launchMenuEntryTask(netloader_state.me);
}
} else {
DrawText(interuiregular14, 64, 128 + 18, themeCurrent.textColor, textGetString(StrId_NoAppsFound_Msg));
DrawTextFromLayout(ThemeLayoutId_InfoMsg, themeCurrent.textColor, textGetString(StrId_NoAppsFound_Msg));
}
drawButtons(menu, true, &menupath_x_endpos);
drawButtons(menu, true, curPos);
}
else
{
static int v = 0;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
if (menu->nEntries > 7) {
int wanted_x = clamp(-menu->curEntry * (140 + 30), -(menu->nEntries - 7) * (140 + 30), 0);
// Gentle Realign only when not manually moving
if (menu->slideSpeed == 0) {
if (menu->nEntries > entries_count) {
int wanted_x = clamp(-menu->curEntry * layoutobj->posEnd[0], -(menu->nEntries - entries_count) * layoutobj->posEnd[0], 0);
menu->xPos += v;
v += (wanted_x - menu->xPos) / 3;
v /= 2;
@ -697,12 +811,31 @@ void menuLoop(void) {
else {
menu->xPos = v = 0;
}
}
else {
menu->xPos += menu->slideSpeed;
if (abs(menu->slideSpeed) > 2) {
// Slow down way faster when outside the normal bounds
if (menu->xPos > 0 || menu->xPos < -(menu->nEntries) * layoutobj->posEnd[0]) {
menu->slideSpeed *= .5f;
}
else {
menu->slideSpeed *= .9f;
}
}
else {
menu->slideSpeed = 0;
}
menu->curEntry = clamp(roundf(-((float) menu->xPos / layoutobj->posEnd[0])), 0, menu->nEntries);
}
menuEntry_s *active_entry = NULL;
// Draw menu entries
for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) {
int entry_start_x = 29 + i * (140 + 30);
int entry_start_x = layoutobj->posStart[0] + i * layoutobj->posEnd[0];
int entry_draw_x = entry_start_x + menu->xPos;
int screen_width = 1280;
@ -714,54 +847,61 @@ void menuLoop(void) {
if (is_active)
active_entry = me;
if (!is_active && entry_draw_x < -(29 + 140 + 30))
if (!is_active && entry_draw_x < -(layoutobj->posStart[0] + layoutobj->posEnd[0]))
continue;
drawEntry(me, entry_draw_x, is_active);
}
int getX = GetTextXCoordinate(interuiregular18, 1180, textGetString(StrId_ThemeMenu), 'r');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuTypeMsg];
int getX=0;
if (layoutobj->visible) {
getX = GetTextXCoordinate(layoutobj->font, layoutobj->posStart[0], textGetString(StrId_ThemeMenu), 'r');
if(hbmenu_state == HBMENU_THEME_MENU) {
DrawText(interuiregular18, getX, 30 + 26 + 32 + 20, themeCurrent.textColor, textGetString(StrId_ThemeMenu));
DrawText(layoutobj->font, getX, layoutobj->posStart[1], themeCurrent.textColor, textGetString(StrId_ThemeMenu));
} else {
//DrawText(interuiregular18, getX, 30 + 26 + 32 + 10, themeCurrent.textColor, textGetString(StrId_ThemeMenu));
//DrawText(fontscale7, getX - 40, 30 + 26 + 32 + 10, themeCurrent.textColor, themeCurrent.buttonMText);
}
}
if(active_entry != NULL) {
if (active_entry->type == ENTRY_TYPE_THEME) {
DrawText(fontscale7, 1280 - 126 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonAText);
DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Apply));
}
else if (active_entry->type != ENTRY_TYPE_FOLDER) {
DrawText(fontscale7, 1280 - 126 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonAText);//Display the 'A' button from SharedFont.
DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Launch));
}
else {
DrawText(fontscale7, 1280 - 126 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonAText);
DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Open));
}
const char *buttonstr = "";
if (active_entry->type == ENTRY_TYPE_THEME)
buttonstr = textGetString(StrId_Actions_Apply);
else if (active_entry->type != ENTRY_TYPE_FOLDER)
buttonstr = textGetString(StrId_Actions_Launch);
else
buttonstr = textGetString(StrId_Actions_Open);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonAText];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonAText, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, buttonstr, 'l');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonA, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, themeCurrent.buttonAText, 'l');
}
drawButtons(menu, false, &menupath_x_endpos);
drawButtons(menu, false, curPos);
if (active_entry && active_entry->type != ENTRY_TYPE_THEME) {
if (active_entry->starred) {
getX = GetTextXCoordinate(interuiregular18, menupath_x_endpos + 8, textGetString(StrId_Actions_Unstar), 'r');
DrawText(fontscale7, getX - 36, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonXText);
DrawText(interuiregular18, getX, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Unstar));
} else {
getX = GetTextXCoordinate(interuiregular18, menupath_x_endpos + 8, textGetString(StrId_Actions_Star), 'r');
DrawText(fontscale7, getX - 36, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonXText);
DrawText(interuiregular18, getX, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Star));
}
menupath_x_endpos = getX - 36 - 40;
const char *buttonstr = "";
if (active_entry->starred)
buttonstr = textGetString(StrId_Actions_Unstar);
else
buttonstr = textGetString(StrId_Actions_Star);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonXText];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonXText, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, buttonstr, 'r');
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_ButtonX];
DrawTextFromLayoutRelative(ThemeLayoutId_ButtonX, curPos[0], curPos[1], layoutobj->posStart, curPos, themeCurrent.textColor, themeCurrent.buttonXText, 'l');
}
}
DrawTextTruncate(interuiregular18, 40, 720 - 47 + 24, themeCurrent.textColor, menu->dirname, menupath_x_endpos - 40, "...");
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuPath];
if (layoutobj->visible) DrawTextTruncate(layoutobj->font, layoutobj->posStart[0], layoutobj->posStart[1], themeCurrent.textColor, menu->dirname, layoutobj->size[0], "...");
menuDrawMsgBox();
}

View File

@ -33,6 +33,7 @@ struct menu_s_tag
int nEntries;
int curEntry;
int xPos;
int slideSpeed;
char dirname[PATH_MAX+1];
};
@ -77,7 +78,7 @@ typedef enum
IMAGE_MODE_RGBA32
} ImageMode;
double menuTimer;
extern double menuTimer;
#ifdef __cplusplus
extern "C" {
@ -86,7 +87,7 @@ extern "C" {
void menuEntryInit(menuEntry_s* me, MenuEntryType type);
void menuEntryFree(menuEntry_s* me, bool skip_icongfx);
bool fileExists(const char* path);
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut);
bool menuEntryLoad(menuEntry_s* me, const char* name, bool shortcut, bool check_exists);
void menuEntryParseIcon(menuEntry_s* me);
uint8_t *downscaleImg(const uint8_t *image, int srcWidth, int srcHeight, int destWidth, int destHeight, ImageMode mode);
void menuEntryParseNacp(menuEntry_s* me);

View File

@ -16,6 +16,7 @@ void drawMsgBoxBgToBuff(color_t *buff, int width, int height) {
float rad, alpha;
color_t base_color = themeCurrent.backgroundColor;
color_t color;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
for (y=0; y<height; y++) {
for (x=0; x<width; x++) {
@ -62,7 +63,7 @@ void drawMsgBoxBgToBuff(color_t *buff, int width, int height) {
else
color = base_color;
if (y == height - 80) {
if (y == height + layoutobj->posStart[1]) {
color = themeCurrent.separatorColor;
}
@ -76,6 +77,7 @@ void menuDrawMsgBox() {
if (!menuIsMsgBoxOpen())
return;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
int off;
int x, y;
int start_x = 1280 / 2 - currMsgBox.width / 2;
@ -85,7 +87,7 @@ void menuDrawMsgBox() {
color_t curr_color;
color_t border_color;
int sep_start_y = currMsgBox.height - 80;
int sep_start_y = currMsgBox.height + layoutobj->posStart[1];
int border_thickness = 6;
int shadow_start_y, shadow_y;
@ -121,7 +123,7 @@ void menuDrawMsgBox() {
curr_color = border_color;
}
}
else if (msgboxNetloaderProgressEnabled && y > currMsgBox.height - 80 && x < progress_width) {
else if (msgboxNetloaderProgressEnabled && y > sep_start_y && x < progress_width) {
curr_color = themeCurrent.progressBarColor;
}
@ -135,10 +137,11 @@ void menuDrawMsgBox() {
x = GetTextXCoordinate(interuiregular18, start_x + (currMsgBox.width / 2), textptr, 'c');
if (text_width < currMsgBox.width && text_height < sep_start_y) {
DrawText(interuiregular18, x, start_y + (currMsgBox.height - text_height - 80) / 2, themeCurrent.textColor, textptr);
DrawText(interuiregular18, x, start_y + (sep_start_y - text_height) / 2, themeCurrent.textColor, textptr);
}
y = start_y + 245 + 26;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxBottomText];
y = start_y + currMsgBox.height + layoutobj->posStart[1];
if (!msgboxNetloaderEnabled) {
x = GetTextXCoordinate(interuimedium20, start_x + (currMsgBox.width / 2), textGetString(StrId_MsgBox_OK), 'c');

View File

@ -368,7 +368,7 @@ static int decompress(int sock, FILE *fh, size_t filesize) {
int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
//---------------------------------------------------------------------------------
int len, namelen, filelen;
char filename[PATH_MAX+1];
char filepath[PATH_MAX+1];
len = recvall(sock, &namelen, 4, 0);
if (len != 4) {
@ -376,19 +376,19 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
return -1;
}
if (namelen >= sizeof(filename)-1) {
netloader_error("Filename length is too large",errno);
if (namelen >= sizeof(filepath)-1) {
netloader_error("File-path length is too large",errno);
return -1;
}
len = recvall(sock, filename, namelen, 0);
len = recvall(sock, filepath, namelen, 0);
if (len != namelen) {
netloader_error("Error getting filename", errno);
netloader_error("Error getting file-path", errno);
return -1;
}
filename[namelen] = 0;
filepath[namelen] = 0;
len = recvall(sock, &filelen, 4, 0);
@ -403,21 +403,34 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
int response = 0;
sanitisePath(filename);
sanitisePath(filepath);
snprintf(me->path, sizeof(me->path)-1, "%s%s%s", menuGetRootPath(), DIRECTORY_SEPARATOR, filename);
me->path[PATH_MAX] = 0;
snprintf(me->path, sizeof(me->path)-1, "%s%s%s", menuGetRootPath(), DIRECTORY_SEPARATOR, filepath);
// make sure it's terminated
me->path[PATH_MAX] = 0;
me->path[sizeof(me->path)-1] = 0;
strncpy(filepath, me->path, sizeof(filepath)-1); // menuEntryLoad() below will overwrite me->path, so copy me->path to filepath and use that instead.
filepath[sizeof(filepath)-1] = 0;
argData_s* ad = &me->args;
ad->dst = (char*)&ad->buf[1];
ad->nxlink_host = remote;
const char* ext = getExtension(me->path);
if (ext && strcasecmp(ext, ".nro")==0)
launchAddArg(ad, me->path);
else {
me->type = ENTRY_TYPE_FILE_OTHER; // Handle fileassoc when extension isn't .nro.
if (!menuEntryLoad(me, "", false, false)) {
response = -3;
errno = EINVAL;
netloader_error("File-extension/filename not recognized",0);
}
menuEntryFree(me, false); // We don't need any of the buffers which may have been allocated.
}
#ifndef _WIN32
int fd = open(me->path,O_CREAT|O_WRONLY, ACCESSPERMS);
if (response == 0) {
int fd = open(filepath,O_CREAT|O_WRONLY, ACCESSPERMS);
if (fd < 0) {
response = -1;
@ -429,16 +442,18 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
}
close(fd);
}
}
#endif
FILE *file = NULL;
if (response == 0) file = fopen(me->path,"wb");
if(NULL == file) {
if (response == 0) {
file = fopen(filepath,"wb");
if(file == NULL) {
perror("file");
response = -1;
}
}
send(sock,(char *)&response,sizeof(response),0);
@ -449,12 +464,14 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
netloader_error("Failed to allocate memory",ENOMEM);
response = -1;
}
else
else {
memset(writebuffer, 0, FILE_BUFFER_SIZE);
setvbuf(file,writebuffer,_IOFBF, FILE_BUFFER_SIZE);
}
}
if (response == 0 ) {
//printf("transferring %s\n%d bytes.\n", filename, filelen);
//printf("transferring %s\n%d bytes.\n", filepath, filelen);
if (decompress(sock,file,filelen)==Z_OK) {
int netloaded_cmdlen = 0;
@ -499,13 +516,14 @@ int loadnro(menuEntry_s *me, int sock, struct in_addr remote) {
} else {
response = -1;
}
}
if (file) {
fflush(file);
fclose(file);
free(writebuffer);
if (response == -1) unlink(me->path);
}
if (response == -1) unlink(filepath);
free(writebuffer);
return response;
}
@ -549,7 +567,15 @@ int netloader_activate(void) {
return -1;
}
int rc = bind(netloader_listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
uint32_t tmpval=1;
int rc = setsockopt(netloader_listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&tmpval, sizeof(tmpval));
if(rc != 0)
{
netloader_socket_error("setsockopt");
return -1;
}
rc = bind(netloader_listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if(rc != 0)
{
netloader_socket_error("bind");

View File

@ -1,4 +1,5 @@
#include "theme.h"
#include <physfs.h>
theme_t themeCurrent;
ThemePreset themeGlobalPreset;
@ -10,6 +11,83 @@ bool colorFromSetting(config_setting_t *rgba, color_t *col) {
return true;
}
bool intElemFromSetting(config_setting_t *setting, int *out, size_t count) {
if (!setting || config_setting_length(setting) < count)
return false;
for (size_t i=0; i<count; i++) {
out[i] = config_setting_get_int_elem(setting, i);
}
return true;
}
bool layoutObjectFromSetting(config_setting_t *layout_setting, ThemeLayoutObject *obj, bool ignore_cfg_visible) {
int tmp=0;
ThemeLayoutObject tmpobj={0};
if (!layout_setting)
return false;
memcpy(tmpobj.posStart, obj->posStart, sizeof(obj->posStart));
memcpy(tmpobj.posEnd, obj->posEnd, sizeof(obj->posEnd));
memcpy(tmpobj.size, obj->size, sizeof(obj->size));
if (config_setting_lookup_bool(layout_setting, "visible", &tmp)==CONFIG_TRUE)
tmpobj.visible = tmp;
else
tmpobj.visible = obj->visible;
if (config_setting_lookup_bool(layout_setting, "posType", &tmp)==CONFIG_TRUE)
tmpobj.posType = tmp;
else
tmpobj.posType = obj->posType;
intElemFromSetting(config_setting_lookup(layout_setting, "posStart"), tmpobj.posStart, 2);
intElemFromSetting(config_setting_lookup(layout_setting, "posEnd"), tmpobj.posEnd, 2);
intElemFromSetting(config_setting_lookup(layout_setting, "size"), tmpobj.size, 2);
if (!tmpobj.posType && (tmpobj.posStart[0] < 0 || tmpobj.posStart[1] < 0 || tmpobj.posEnd[0] < 0 || tmpobj.posEnd[1] < 0))
return false;
if (tmpobj.size[0] < 0 || tmpobj.size[1] < 0)
return false;
obj->posStart[0] = tmpobj.posStart[0];
obj->posStart[1] = tmpobj.posStart[1];
obj->posEnd[0] = tmpobj.posEnd[0];
obj->posEnd[1] = tmpobj.posEnd[1];
if (!ignore_cfg_visible) obj->visible = tmpobj.visible;
obj->posType = tmpobj.posType;
obj->size[0] = tmpobj.size[0];
obj->size[1] = tmpobj.size[1];
return true;
}
bool assetObjectFromSetting(config_setting_t *asset_setting, AssetId id, ThemeLayoutObject *layoutobj) {
int imageSize[2]={0};
const char *path = NULL;
char tmp_path[PATH_MAX];
if (!asset_setting)
return false;
if (config_setting_lookup_string(asset_setting, "path", &path)==CONFIG_FALSE)
return false;
if (!intElemFromSetting(config_setting_lookup(asset_setting, "imageSize"), imageSize, 2))
return false;
if (imageSize[0] <= 0 || imageSize[1] <= 0 || imageSize[0] > 1280 || imageSize[1] > 720)
return false;
if (layoutobj && (imageSize[0] != layoutobj->imageSize[0] || imageSize[1] != layoutobj->imageSize[1]))
return false;
memset(tmp_path, 0, sizeof(tmp_path));
snprintf(tmp_path, sizeof(tmp_path)-1, "theme/%s", path);
return assetsLoadData(id, tmp_path, imageSize);
}
void themeStartup(ThemePreset preset) {
themeGlobalPreset = preset;
theme_t themeLight = (theme_t) {
@ -20,6 +98,7 @@ void themeStartup(ThemePreset preset) {
.backWaveColor = MakeColor(154, 171, 255, 255),
.backgroundColor = MakeColor(233, 236, 241, 255),
.highlightColor = MakeColor(91, 237, 224, 255),
.highlightGradientEdgeColor = MakeColor(91,176,224,255),
.separatorColor = MakeColor(219, 218, 219, 255),
.borderColor = MakeColor(255,255,255,255),
.borderTextColor = MakeColor(64,64,64,255),
@ -33,7 +112,6 @@ void themeStartup(ThemePreset preset) {
.buttonMText = "\uE0F0",
.labelStarOnText = "\u2605",
.labelStarOffText = "\u2606",
.hbmenuLogoImage = assetsGetDataBuffer(AssetId_hbmenu_logo_light),
};
theme_t themeDark = (theme_t) {
@ -44,6 +122,7 @@ void themeStartup(ThemePreset preset) {
.backWaveColor = MakeColor(73, 103, 169, 255),
.backgroundColor = MakeColor(45, 45, 50, 255),
.highlightColor = MakeColor(91, 237, 224, 255),
.highlightGradientEdgeColor = MakeColor(91,176,224,255),
.separatorColor = MakeColor(219, 218, 219, 255),
.borderColor = MakeColor(255,255,255,255),
.borderTextColor = MakeColor(64,64,64,255),
@ -57,7 +136,276 @@ void themeStartup(ThemePreset preset) {
.buttonMText = "\uE0B4",
.labelStarOnText = "\u2605",
.labelStarOffText = "\u2606",
.hbmenuLogoImage = assetsGetDataBuffer(AssetId_hbmenu_logo_dark),
};
theme_t themeCommon = {
.layoutObjects = {
[ThemeLayoutId_Logo] = {
.visible = true,
.posType = false,
.posStart = {40, 20},
},
[ThemeLayoutId_HbmenuVersion] = {
.visible = true,
.posType = false,
.posStart = {184, 46 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_LoaderInfo] = {
.visible = true,
.posType = true,
.posStart = {43, 46 + 18 + 20},
.posEnd = {184},
.font = interuiregular14,
},
[ThemeLayoutId_AttentionText] = {
.visible = true,
.posType = true,
.posStart = {-32, 46 + 18},
.font = interuimedium30,
},
[ThemeLayoutId_LogInfo] = {
.visible = true,
.posType = false,
.posStart = {180 + 256, 46 + 16 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_InfoMsg] = {
.visible = true,
.posType = false,
.posStart = {64, 128 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_MenuPath] = {
.visible = true,
.posType = false,
.posStart = {40, 720 - 47 + 24},
.size = {380},
.font = interuiregular18,
},
[ThemeLayoutId_MenuTypeMsg] = {
.visible = true,
.posType = false,
.posStart = {1180, 30 + 26 + 32 + 20},
.font = interuiregular18,
},
[ThemeLayoutId_MsgBoxSeparator] = {
.visible = true,
.posType = true,
.posStart = {0, -80},
},
[ThemeLayoutId_MsgBoxBottomText] = {
.visible = true,
.posType = true,
.posStart = {0, -29},
},
// ThemeLayoutId_BackgroundImage is not set with the defaults.
[ThemeLayoutId_BackWave] = {
.visible = true,
.posType = true,
.size = {0, 295},
},
[ThemeLayoutId_MiddleWave] = {
.visible = true,
.posType = true,
.size = {0, 290},
},
[ThemeLayoutId_FrontWave] = {
.visible = true,
.posType = true,
.size = {0, 280},
},
[ThemeLayoutId_ButtonA] = {
.visible = true,
.posType = false,
.posStart = {1280 - 126 - 30 - 32, 720 - 47 + 24},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonAText] = {
.visible = true,
.posType = false,
.posStart = {1280 - 90 - 30 - 32, 720 - 47 + 24},
.touchSize = {0, 25},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonB] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.posEnd = {0},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonBText] = {
.visible = true,
.posType = true,
.posStart = {-90, 0},
.touchSize = {0, 32},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonY] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.font = fontscale7,
},
[ThemeLayoutId_ButtonYText] = {
.visible = true,
.posType = true,
.posStart = {-32, 0},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonM] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.font = fontscale7,
},
[ThemeLayoutId_ButtonMText] = {
.visible = true,
.posType = true,
.posStart = {-32, 0},
.font = interuiregular18,
},
[ThemeLayoutId_ButtonX] = {
.visible = true,
.posType = true,
.posStart = {-36, 0},
.touchSize = {36, 25},
.font = fontscale7,
},
[ThemeLayoutId_ButtonXText] = {
.visible = true,
.posType = true,
.posStart = {-40 + 8, 0},
.touchSize = {0, 25},
.font = interuiregular18,
},
[ThemeLayoutId_NetworkIcon] = {
.visible = true,
.posType = true,
.posStart = {0, 0 + 47 + 10 + 3},
},
[ThemeLayoutId_BatteryCharge] = {
.visible = true,
.posType = false,
.posStart = {1180 - 10 - 24 - 8, 0 + 47 + 10 + 21 + 4},
.font = interuiregular14,
},
[ThemeLayoutId_BatteryIcon] = {
.visible = true,
.posType = false,
.posStart = {1180 - 8 - 24 - 8, 0 + 47 + 10 + 6},
},
[ThemeLayoutId_ChargingIcon] = {
.visible = true,
.posType = false,
.posStart = {1180 - 20, 0 + 47 + 10 + 6},
},
[ThemeLayoutId_Status] = {
.visible = true,
.posType = false,
.posStart = {1180, 0 + 47 + 10},
.font = interuimedium20,
},
[ThemeLayoutId_Temperature] = {
.visible = true,
.posType = false,
.posStart = {1180 + 4, 0 + 47 + 10 + + 21 + 6},
.font = interuiregular14,
},
[ThemeLayoutId_MenuList] = {
.visible = true,
.posType = false,
.posStart = {29, 720 - 100 - 145},
.posEnd = {140 + 30, 0},
.size = {140, 140 + 32},
},
[ThemeLayoutId_MenuListTiles] = {
.visible = true,
.posType = true,
.posEnd = {7, 0},
.size = {0, 0},
},
[ThemeLayoutId_MenuListIcon] = {
.visible = true,
.posType = true,
.posStart = {0, 32},
.size = {140, 140},
.imageSize = {256, 256},
},
[ThemeLayoutId_MenuListName] = {
.visible = true,
.posType = true,
.posStart = {4, 4 + 18},
.size = {140 - 32, 0},
.font = interuiregular14,
},
[ThemeLayoutId_MenuActiveEntryIcon] = {
.visible = true,
.posType = false,
.posStart = {117, 100+10},
.size = {256, 256},
.imageSize = {256, 256},
},
[ThemeLayoutId_MenuActiveEntryName] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 39},
.size = {790 - 120, 0},
.font = interuimedium30,
},
[ThemeLayoutId_MenuActiveEntryAuthor] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 28 + 30 + 18},
.font = interuiregular14,
},
[ThemeLayoutId_MenuActiveEntryVersion] = {
.visible = true,
.posType = false,
.posStart = {1280 - 790, 135+10 + 28 + 30 + 18 + 6 + 18},
.font = interuiregular14,
},
},
};
char themePath[PATH_MAX] = {0};
@ -66,14 +414,55 @@ void themeStartup(ThemePreset preset) {
theme_t *themeDefault;
config_t cfg = {0};
config_init(&cfg);
config_setting_t *theme = NULL;
color_t text, attentionText, frontWave, middleWave, backWave, background, highlight, separator, borderColor, borderTextColor, progressBarColor;
config_setting_t *theme = NULL, *layout = NULL, *assets = NULL;
color_t text, logoColor={0}, attentionText, frontWave, middleWave, backWave, background, highlight, highlightGradientEdgeColor, separator, borderColor, borderTextColor, progressBarColor;
int waveBlending;
const char *AText, *BText, *XText, *YText, *PText, *MText, *starOnText, *starOffText;
bool logoColor_set = false;
bool good_cfg = false;
#ifdef __SWITCH__
bool is_romfs = false;
#endif
bool is_archive = false;
const char* theme_archive_path = NULL;
if(themePath[0]!=0)
good_cfg = config_read_file(&cfg, themePath);
assetsClearTheme();
if(themePath[0]!=0) {
const char* cfg_path = themePath;
const char* ext = getExtension(themePath);
#ifdef __SWITCH__
if (strcasecmp(ext, ".romfs")==0) {
if (R_FAILED(romfsMountFromFsdev(themePath, 0, "theme")))
cfg_path = NULL;
else {
is_romfs = true;
cfg_path = "theme:/theme.cfg";
theme_archive_path = "theme:/";
}
}
#endif
if (strcasecmp(ext, ".romfs")!=0 && strcasecmp(ext, ".cfg")!=0) {
theme_archive_path = themePath;
}
if (theme_archive_path) {
if (!PHYSFS_mount(theme_archive_path, "theme", 0)) cfg_path = NULL;
else {
is_archive = true;
cfg_path = "theme/theme.cfg";
}
}
if (cfg_path) {
if (!is_archive) good_cfg = config_read_file(&cfg, cfg_path);
else {
u8 *cfg_buf = NULL;
good_cfg = assetsPhysfsReadFile(cfg_path, &cfg_buf, NULL, true);
if (good_cfg) good_cfg = config_read_string(&cfg, (char*)cfg_buf);
free(cfg_buf);
}
}
}
switch (preset) {
case THEME_PRESET_LIGHT:
@ -94,6 +483,8 @@ void themeStartup(ThemePreset preset) {
if (theme != NULL) {
if (!colorFromSetting(config_setting_lookup(theme, "textColor"), &text))
text = themeDefault->textColor;
if (colorFromSetting(config_setting_lookup(theme, "logoColor"), &logoColor))
logoColor_set = true;
if (!colorFromSetting(config_setting_lookup(theme, "attentionTextColor"), &attentionText))
attentionText = themeDefault->attentionTextColor;
if (!colorFromSetting(config_setting_lookup(theme, "frontWaveColor"), &frontWave))
@ -106,6 +497,8 @@ void themeStartup(ThemePreset preset) {
background = themeDefault->backgroundColor;
if (!colorFromSetting(config_setting_lookup(theme, "highlightColor"), &highlight))
highlight = themeDefault->highlightColor;
if (!colorFromSetting(config_setting_lookup(theme, "highlightGradientEdgeColor"), &highlightGradientEdgeColor))
highlightGradientEdgeColor = themeDefault->highlightGradientEdgeColor;
if (!colorFromSetting(config_setting_lookup(theme, "separatorColor"), &separator))
separator = themeDefault->separatorColor;
if (!colorFromSetting(config_setting_lookup(theme, "borderColor"), &borderColor))
@ -134,18 +527,20 @@ void themeStartup(ThemePreset preset) {
starOffText = themeDefault->labelStarOffText;
themeCurrent = (theme_t) {
.textColor = text,
.logoColor = logoColor,
.attentionTextColor = attentionText,
.frontWaveColor = frontWave,
.middleWaveColor = middleWave,
.backWaveColor = backWave,
.backgroundColor = background,
.highlightColor = highlight,
.highlightGradientEdgeColor = highlightGradientEdgeColor,
.separatorColor = separator,
.borderColor = borderColor,
.borderTextColor = borderTextColor,
.progressBarColor = progressBarColor,
.logoColor_set = logoColor_set,
.enableWaveBlending = waveBlending,
.hbmenuLogoImage = themeDefault->hbmenuLogoImage
};
strncpy(themeCurrent.buttonAText, AText, sizeof(themeCurrent.buttonAText));
themeCurrent.buttonAText[sizeof(themeCurrent.buttonAText)-1] = 0;
@ -166,10 +561,95 @@ void themeStartup(ThemePreset preset) {
} else {
themeCurrent = *themeDefault;
}
memcpy(themeCurrent.layoutObjects, themeCommon.layoutObjects, sizeof(themeCommon.layoutObjects));
layout = config_lookup(&cfg, "layout");
if (layout != NULL) {
layoutObjectFromSetting(config_setting_lookup(layout, "logo"), &themeCurrent.layoutObjects[ThemeLayoutId_Logo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "hbmenuVersion"), &themeCurrent.layoutObjects[ThemeLayoutId_HbmenuVersion], true);
layoutObjectFromSetting(config_setting_lookup(layout, "loaderInfo"), &themeCurrent.layoutObjects[ThemeLayoutId_LoaderInfo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "attentionText"), &themeCurrent.layoutObjects[ThemeLayoutId_AttentionText], true);
layoutObjectFromSetting(config_setting_lookup(layout, "logInfo"), &themeCurrent.layoutObjects[ThemeLayoutId_LogInfo], true);
layoutObjectFromSetting(config_setting_lookup(layout, "infoMsg"), &themeCurrent.layoutObjects[ThemeLayoutId_InfoMsg], true);
layoutObjectFromSetting(config_setting_lookup(layout, "menuPath"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuPath], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuTypeMsg"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuTypeMsg], false);
layoutObjectFromSetting(config_setting_lookup(layout, "msgBoxSeparator"), &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator], false);
layoutObjectFromSetting(config_setting_lookup(layout, "msgBoxBottomText"), &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxBottomText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "backgroundImage"), &themeCurrent.layoutObjects[ThemeLayoutId_BackgroundImage], false);
layoutObjectFromSetting(config_setting_lookup(layout, "backWave"), &themeCurrent.layoutObjects[ThemeLayoutId_BackWave], false);
layoutObjectFromSetting(config_setting_lookup(layout, "middleWave"), &themeCurrent.layoutObjects[ThemeLayoutId_MiddleWave], false);
layoutObjectFromSetting(config_setting_lookup(layout, "frontWave"), &themeCurrent.layoutObjects[ThemeLayoutId_FrontWave], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonA"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonA], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonAText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonAText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonB"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonB], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonBText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonBText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonY"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonY], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonYText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonYText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonM"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonM], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonMText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonMText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonX"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonX], false);
layoutObjectFromSetting(config_setting_lookup(layout, "buttonXText"), &themeCurrent.layoutObjects[ThemeLayoutId_ButtonXText], false);
layoutObjectFromSetting(config_setting_lookup(layout, "networkIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_NetworkIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "batteryCharge"), &themeCurrent.layoutObjects[ThemeLayoutId_BatteryCharge], false);
layoutObjectFromSetting(config_setting_lookup(layout, "batteryIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_BatteryIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "chargingIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_ChargingIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "status"), &themeCurrent.layoutObjects[ThemeLayoutId_Status], false);
layoutObjectFromSetting(config_setting_lookup(layout, "temperature"), &themeCurrent.layoutObjects[ThemeLayoutId_Temperature], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuList"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuList], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListTiles"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuListName"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuListName], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryIcon"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryName"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryName], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryAuthor"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryAuthor], false);
layoutObjectFromSetting(config_setting_lookup(layout, "menuActiveEntryVersion"), &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryVersion], false);
}
if (is_archive) assets = config_lookup(&cfg, "assets");
if (is_archive && assets) {
assetObjectFromSetting(config_setting_lookup(assets, "battery_icon"), AssetId_battery_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "charging_icon"), AssetId_charging_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "folder_icon"), AssetId_folder_icon, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "invalid_icon"), AssetId_invalid_icon, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "theme_icon_dark"), AssetId_theme_icon_dark, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "theme_icon_light"), AssetId_theme_icon_light, &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon]);
assetObjectFromSetting(config_setting_lookup(assets, "airplane_icon"), AssetId_airplane_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi_none_icon"), AssetId_wifi_none_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi1_icon"), AssetId_wifi1_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi2_icon"), AssetId_wifi2_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "wifi3_icon"), AssetId_wifi3_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "eth_icon"), AssetId_eth_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "eth_none_icon"), AssetId_eth_none_icon, NULL);
assetObjectFromSetting(config_setting_lookup(assets, "background_image"), AssetId_background_image, NULL);
}
} else {
themeCurrent = *themeDefault;
memcpy(themeCurrent.layoutObjects, themeCommon.layoutObjects, sizeof(themeCommon.layoutObjects));
}
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
if (layoutobj->posEnd[0] < 1) layoutobj->posEnd[0] = 1;
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListIcon];
if (layoutobj->size[0] <= 0 || layoutobj->size[1] <= 0 || layoutobj->size[0] > layoutobj->imageSize[0] || layoutobj->size[1] > layoutobj->imageSize[1]) {
layoutobj->size[0] = layoutobj->imageSize[0];
layoutobj->size[1] = layoutobj->imageSize[1];
}
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuActiveEntryIcon];
if (layoutobj->size[0] <= 0 || layoutobj->size[1] <= 0 || layoutobj->size[0] > layoutobj->imageSize[0] || layoutobj->size[1] > layoutobj->imageSize[1]) {
layoutobj->size[0] = layoutobj->imageSize[0];
layoutobj->size[1] = layoutobj->imageSize[1];
}
config_destroy(&cfg);
if (is_archive) PHYSFS_unmount(theme_archive_path);
#ifdef __SWITCH__
if (is_romfs) romfsUnmount("theme");
#endif
}
void GetThemePathFromConfig(char* themePath, size_t size) {

View File

@ -3,19 +3,42 @@
#include "common.h"
#include <libconfig.h>
typedef enum
{
THEME_PRESET_LIGHT,
THEME_PRESET_DARK,
} ThemePreset;
typedef struct
{
bool visible;
bool posType; // false = absolute, true = relative
int posStart[2];
int posEnd[2];
int size[2]; // width/height
int imageSize[2]; // width/height for the actual image data
int touchSize[2];
int posFinal[2];
uint32_t textSize[2];
u32 font;
} ThemeLayoutObject;
typedef struct
{
color_t textColor;
color_t logoColor;
color_t attentionTextColor;
color_t frontWaveColor;
color_t middleWaveColor;
color_t backWaveColor;
color_t backgroundColor;
color_t highlightColor;
color_t highlightGradientEdgeColor;
color_t separatorColor;
color_t borderColor;
color_t borderTextColor;
color_t progressBarColor;
bool logoColor_set;
bool enableWaveBlending;
char buttonAText[32];
char buttonBText[32];
@ -25,14 +48,9 @@ typedef struct
char buttonMText[32];
char labelStarOnText[32];
char labelStarOffText[32];
const uint8_t *hbmenuLogoImage;
} theme_t;
typedef enum
{
THEME_PRESET_LIGHT,
THEME_PRESET_DARK,
} ThemePreset;
ThemeLayoutObject layoutObjects[ThemeLayoutId_Total];
} theme_t;
bool colorFromSetting(config_setting_t *rgba, color_t *col);
void themeStartup(ThemePreset preset);

View File

@ -68,16 +68,27 @@ static void launchFile(const char* path, argData_s* args)
init_args(argBuf, sizeof(argBuf)-1, args->buf, sizeof(args->buf));
struct stat st;
if (stat(path, &st) == -1) {
memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, textGetString(StrId_NroNotFound), path);
menuCreateMsgBox(780, 300, msg);
menuScan(".");
}
else {
Result rc = envSetNextLoad(path, argBuf);
if(R_FAILED(rc)) {
memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, "%s\n0x%x", textGetString(StrId_AppLaunchError), rc);
snprintf(msg, sizeof(msg)-1, "%s\n2%03d-%04d", textGetString(StrId_AppLaunchError), R_MODULE(rc), R_DESCRIPTION(rc));
menuCreateMsgBox(780, 300, msg);
}
else {
uiExitLoop();
}
}
}
const loaderFuncs_s loader_builtin =

View File

@ -1,19 +1,22 @@
#include <switch.h>
#include <string.h>
#include <stdio.h>
#include <physfs.h>
#include "../common/common.h"
#include "nx_graphics.h"
#include "nx_touch.h"
// Define the desired framebuffer resolution (here we set it to 720p).
#define FB_WIDTH 1280
#define FB_HEIGHT 720
Framebuffer g_framebufObj;
uint8_t* g_framebuf;
u32 g_framebuf_width;
PadState g_pad;
PadRepeater g_pad_repeater;
bool menuUpdateErrorScreen(void);
#ifdef PERF_LOG
@ -39,35 +42,44 @@ int main(int argc, char **argv)
u64 start_tick=0;
#endif
padConfigureInput(8, HidNpadStyleSet_NpadStandard);
padInitializeAny(&g_pad);
padRepeaterInitialize(&g_pad_repeater, 20, 10);
hidSetNpadHandheldActivationMode(HidNpadHandheldActivationMode_Single);
touchInit();
memset(errormsg, 0, sizeof(errormsg));
appletLockExit();
appletSetScreenShotPermission(AppletScreenShotPermission_Enable);
ColorSetId theme;
ColorSetId theme = ColorSetId_Light;
rc = setsysInitialize();
if (R_FAILED(rc)) snprintf(errormsg, sizeof(errormsg)-1, "Error: setsysInitialize() failed: 0x%x.", rc);
if (R_SUCCEEDED(rc)) setsysGetColorSetId(&theme);
if (R_SUCCEEDED(rc)) {
rc = plInitialize();
if (R_FAILED(rc)) snprintf(errormsg, sizeof(errormsg)-1, "Error: plInitialize() failed: 0x%x.", rc);
setsysGetColorSetId(&theme);
setsysExit();
}
if (R_SUCCEEDED(rc)) {
rc = textInit();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: textInit() failed: 0x%x.", rc);
snprintf(errormsg, sizeof(errormsg)-1, "Error: textInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
if (R_SUCCEEDED(rc)) menuStartupPath();
if (R_SUCCEEDED(rc)) {
if (!PHYSFS_init(argv[0])) {
rc = 1;
snprintf(errormsg, sizeof(errormsg)-1, "Error: PHYSFS_init() failed: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
}
}
if (R_SUCCEEDED(rc)) {
rc = assetsInit();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: assetsInit() failed: 0x%x.", rc);
snprintf(errormsg, sizeof(errormsg)-1, "Error: assetsInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
@ -78,7 +90,7 @@ int main(int argc, char **argv)
if (R_SUCCEEDED(rc)) {
rc = netloaderInit();
if (R_FAILED(rc)) {
snprintf(errormsg, sizeof(errormsg)-1, "Error: netloaderInit() failed: 0x%x.", rc);
snprintf(errormsg, sizeof(errormsg)-1, "Error: netloaderInit() failed: 2%03d-%04d", R_MODULE(rc), R_DESCRIPTION(rc));
}
}
@ -115,7 +127,7 @@ int main(int argc, char **argv)
if (R_FAILED(lastret)) {
memset(msg, 0, sizeof(msg));
snprintf(msg, sizeof(msg)-1, "%s\n0x%x", textGetString(StrId_LastLoadResult), lastret);
snprintf(msg, sizeof(msg)-1, "%s\n2%03d-%04d", textGetString(StrId_LastLoadResult), R_MODULE(lastret), R_DESCRIPTION(lastret));
menuCreateMsgBox(780, 300, msg);
}
@ -124,8 +136,7 @@ int main(int argc, char **argv)
if (errormsg[0]) error_screen = 1;
if (!error_screen) {
framebufferCreate(&g_framebufObj, nwindowGetDefault(), FB_WIDTH, FB_HEIGHT, PIXEL_FORMAT_RGBA_8888, 2);
framebufferMakeLinear(&g_framebufObj);
graphicsInit(FB_WIDTH, FB_HEIGHT);
}
else {
consoleInit(NULL);
@ -135,16 +146,17 @@ int main(int argc, char **argv)
while (appletMainLoop())
{
//Scan all the inputs. This should be done once for each frame
hidScanInput();
// Scan the gamepad. This should be done once for each frame
padUpdate(&g_pad);
padRepeaterUpdate(&g_pad_repeater, padGetButtons(&g_pad) & (
HidNpadButton_AnyLeft | HidNpadButton_AnyUp | HidNpadButton_AnyRight | HidNpadButton_AnyDown
));
if (!error_screen) {
if (!uiUpdate()) break;
g_framebuf = framebufferBegin(&g_framebufObj, &g_framebuf_width);
g_framebuf = graphicsFrameBegin(&g_framebuf_width);
#ifdef PERF_LOG
start_tick = svcGetSystemTick();
start_tick = armGetSystemTick();
#endif
memset(g_framebuf, 237, g_framebuf_width * FB_HEIGHT);
menuLoop();
@ -154,10 +166,10 @@ int main(int argc, char **argv)
}
if (!error_screen) {
framebufferEnd(&g_framebufObj);
graphicsFrameEnd();
#ifdef PERF_LOG
g_tickdiff_frame = svcGetSystemTick() - start_tick;
g_tickdiff_frame = armGetSystemTick() - start_tick;
#endif
}
else {
@ -166,7 +178,7 @@ int main(int argc, char **argv)
}
if (!error_screen) {
framebufferClose(&g_framebufObj);
graphicsExit();
}
else {
consoleExit(NULL);
@ -185,8 +197,7 @@ int main(int argc, char **argv)
netloaderExit();
powerExit();
assetsExit();
plExit();
setsysExit();
PHYSFS_deinit();
appletUnlockExit();
@ -194,17 +205,9 @@ int main(int argc, char **argv)
}
u64 menuGetKeysDown(void) {
u64 down = 0;
if (hidGetHandheldMode())
down |= hidKeysDown(CONTROLLER_HANDHELD);
else {
for (u32 controller=0; controller<8; controller++) {
if (hidIsControllerConnected(controller)) down |= hidKeysDown(controller);
}
}
return down;
u64 keys = padGetButtonsDown(&g_pad);
keys |= padRepeaterGetButtons(&g_pad_repeater);
return keys;
}
//This is implemented here due to the hid code.
@ -212,29 +215,31 @@ bool menuUpdate(void) {
bool exitflag = 0;
menu_s* menu = menuGetCurrent();
u64 down = menuGetKeysDown();
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
handleTouch(menu);
if (down & KEY_Y)
if (down & HidNpadButton_Y)
{
launchMenuNetloaderTask();
}
else if (down & KEY_X)
else if (down & HidNpadButton_X)
{
menuHandleXButton();
}
else if (down & KEY_A)
else if (down & HidNpadButton_A)
{
menuHandleAButton();
}
else if (down & KEY_B)
else if (down & HidNpadButton_B)
{
launchMenuBackTask();
}
else if(down & KEY_MINUS){
else if(down & HidNpadButton_Minus){
themeMenuStartup();
}
else if (down & KEY_PLUS)
else if (down & HidNpadButton_Plus)
{
exitflag = 1;
}
@ -242,10 +247,10 @@ bool menuUpdate(void) {
{
int move = 0;
if (down & KEY_LEFT) move--;
if (down & KEY_RIGHT) move++;
if (down & KEY_DOWN) move-=7;
if (down & KEY_UP) move+=7;
if (down & HidNpadButton_AnyLeft) move--;
if (down & HidNpadButton_AnyRight) move++;
if (down & HidNpadButton_AnyDown) move-=entries_count;
if (down & HidNpadButton_AnyUp) move+=entries_count;
int newEntry = menu->curEntry + move;
if (newEntry < 0) newEntry = 0;
@ -260,7 +265,7 @@ bool menuUpdateErrorScreen(void) {
bool exitflag = 0;
u64 down = menuGetKeysDown();
if (down & KEY_PLUS)
if (down & HidNpadButton_Plus)
{
exitflag = 1;
}

View File

@ -72,7 +72,7 @@ static void audio_playback_thread(void* arg)
playing = 1;
}
if (R_SUCCEEDED(audoutWaitPlayFinish(&released_buffer, &released_count, U64_MAX)))
if (R_SUCCEEDED(audoutWaitPlayFinish(&released_buffer, &released_count, UINT64_MAX)))
playing = 0;
}
}

141
nx_main/nx_graphics.c Normal file
View File

@ -0,0 +1,141 @@
#include <switch.h>
#include <deko3d.h>
#include "nx_graphics.h"
#define FB_NUM 2
#define CMDMEMSIZE 0x1000
static u32 s_fbWidth, s_fbHeight;
static DkDevice s_device;
static DkMemBlock s_fbMemBlock, s_workMemBlock, s_cmdMemBlock;
static DkSwapchain s_swapchain;
static DkCmdBuf s_cmdBuf;
static DkCmdList s_cmdLists[FB_NUM];
static DkFence s_fence;
static DkQueue s_queue;
void graphicsInit(u32 width, u32 height)
{
DkImageLayoutMaker imgLayoutMaker;
DkMemBlockMaker memBlockMaker;
// Create the device, which is the root object
DkDeviceMaker deviceMaker;
dkDeviceMakerDefaults(&deviceMaker);
s_device = dkDeviceCreate(&deviceMaker);
// Calculate layout for the framebuffers
DkImageLayout fbLayout;
dkImageLayoutMakerDefaults(&imgLayoutMaker, s_device);
imgLayoutMaker.flags = DkImageFlags_UsagePresent;
imgLayoutMaker.format = DkImageFormat_RGBA8_Unorm;
imgLayoutMaker.dimensions[0] = s_fbWidth = width;
imgLayoutMaker.dimensions[1] = s_fbHeight = height;
dkImageLayoutInitialize(&fbLayout, &imgLayoutMaker);
// Retrieve necessary size and alignment for the framebuffers
uint32_t fbSize = dkImageLayoutGetSize(&fbLayout);
uint32_t fbAlign = dkImageLayoutGetAlignment(&fbLayout);
fbSize = (fbSize + fbAlign - 1) &~ (fbAlign - 1);
// Create a memory block that will host the framebuffers
dkMemBlockMakerDefaults(&memBlockMaker, s_device, FB_NUM*fbSize);
memBlockMaker.flags = DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image;
s_fbMemBlock = dkMemBlockCreate(&memBlockMaker);
// Initialize the framebuffers with the layout and backing memory we've just created
DkImage fbImages[FB_NUM];
DkImage const* swapchainImages[FB_NUM];
for (unsigned i = 0; i < FB_NUM; i ++)
{
swapchainImages[i] = &fbImages[i];
dkImageInitialize(&fbImages[i], &fbLayout, s_fbMemBlock, i*fbSize);
}
// Create a swapchain out of the framebuffers we've just initialized
DkSwapchainMaker swapchainMaker;
dkSwapchainMakerDefaults(&swapchainMaker, s_device, nwindowGetDefault(), swapchainImages, FB_NUM);
s_swapchain = dkSwapchainCreate(&swapchainMaker);
// Create a memory block for the linear framebuffer
dkMemBlockMakerDefaults(&memBlockMaker, s_device, width*height*4);
memBlockMaker.flags = DkMemBlockFlags_CpuCached | DkMemBlockFlags_GpuUncached;
s_workMemBlock = dkMemBlockCreate(&memBlockMaker);
// Create a memory block for the command lists
dkMemBlockMakerDefaults(&memBlockMaker, s_device, CMDMEMSIZE);
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
s_cmdMemBlock = dkMemBlockCreate(&memBlockMaker);
// Create a command buffer
DkCmdBufMaker cmdBufMaker;
dkCmdBufMakerDefaults(&cmdBufMaker, s_device);
s_cmdBuf = dkCmdBufCreate(&cmdBufMaker);
dkCmdBufAddMemory(s_cmdBuf, s_cmdMemBlock, 0, CMDMEMSIZE);
// Define source for linear framebuffer copies
const DkCopyBuf linearSrc = {
.addr = dkMemBlockGetGpuAddr(s_workMemBlock),
.rowLength = 0,
.imageHeight = 0,
};
// Define rectangle for the copies
const DkImageRect copyRect = {
.x = 0, .y = 0, .z = 0,
.width = width, .height = height, .depth = 1,
};
// Record command lists for the copies
for (unsigned i = 0; i < FB_NUM; i ++) {
DkImageView tiledDst;
dkImageViewDefaults(&tiledDst, &fbImages[i]);
dkCmdBufCopyBufferToImage(s_cmdBuf, &linearSrc, &tiledDst, &copyRect, 0);
dkCmdBufSignalFence(s_cmdBuf, &s_fence, false);
s_cmdLists[i] = dkCmdBufFinishList(s_cmdBuf);
}
// Create a queue, to which we will submit our command lists
DkQueueMaker queueMaker;
dkQueueMakerDefaults(&queueMaker, s_device);
queueMaker.flags = 0; // we will only use this queue for transferring
s_queue = dkQueueCreate(&queueMaker);
}
void graphicsExit(void)
{
// Make sure the queue is idle before destroying anything
dkQueueWaitIdle(s_queue);
// Destroy all the resources we've created
dkQueueDestroy(s_queue);
dkCmdBufDestroy(s_cmdBuf);
dkMemBlockDestroy(s_cmdMemBlock);
dkMemBlockDestroy(s_workMemBlock);
dkSwapchainDestroy(s_swapchain);
dkMemBlockDestroy(s_fbMemBlock);
dkDeviceDestroy(s_device);
}
void* graphicsFrameBegin(u32* out_stride)
{
// Ensure the GPU is not reading from the framebuffer
dkFenceWait(&s_fence, -1);
// Return information
if (out_stride) *out_stride = s_fbWidth*4;
return dkMemBlockGetCpuAddr(s_workMemBlock);
}
void graphicsFrameEnd(void)
{
// Flush the linear framebuffer
dkMemBlockFlushCpuCache(s_workMemBlock, 0, s_fbWidth*s_fbHeight*4);
// Present a frame
int slot = dkQueueAcquireImage(s_queue, s_swapchain);
dkQueueSubmitCommands(s_queue, s_cmdLists[slot]);
dkQueuePresentImage(s_queue, s_swapchain, slot);
}

9
nx_main/nx_graphics.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <switch.h>
void graphicsInit(u32 width, u32 height);
void graphicsExit(void);
void* graphicsFrameBegin(u32* out_stride);
void graphicsFrameEnd(void);

View File

@ -8,7 +8,7 @@ static bool powerCacheIsCharging;
static PsmSession powerSession;
bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
ChargerType charger = ChargerType_None;
PsmChargerType charger = PsmChargerType_Unconnected;
bool hwReadsSucceeded = false;
bool use_cache = false;
Result rc = 0;
@ -31,7 +31,7 @@ bool powerGetDetails(uint32_t *batteryCharge, bool *isCharging) {
else {
rc = psmGetChargerType(&charger);
hwReadsSucceeded &= R_SUCCEEDED(rc);
*isCharging = (charger > ChargerType_None);
*isCharging = (charger != PsmChargerType_Unconnected);
}
powerCacheCharge = *batteryCharge;

View File

@ -9,6 +9,6 @@ void thermalstatusExit(void) {
}
bool thermalstatusGetDetails(s32 *temperature) {
return R_SUCCEEDED(tsGetTemperatureMilliC(TsLocation_Internal, temperature));
return R_SUCCEEDED(tsGetTemperature(TsLocation_Internal, temperature));
}

View File

@ -5,43 +5,33 @@
#define VERTICAL_SWIPE_MINIMUM_DISTANCE 300
#define HORIZONTAL_SWIPE_VERTICAL_PLAY 250
#define HORIZONTAL_SWIPE_MINIMUM_DISTANCE 300
#define LISTING_START_Y 475
#define LISTING_END_Y 647
#define BUTTON_START_Y 672
#define BUTTON_END_Y 704
#define BACK_BUTTON_START_X 966
#define BACK_BUTTON_END_X 1048
#define LAUNCH_BUTTON_START_X 1092
#define LAUNCH_BUTTON_END_X 1200
#define STAR_BUTTON_START_X 426
#define STAR_BUTTON_END_X 490
#define STAR_BUTTON_START_Y 100
#define STAR_BUTTON_END_Y 161
#define distance(x1, y1, x2, y2) (int) sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))
struct touchInfo_s touchInfo;
void touchInit() {
void touchInit(void) {
touchInfo.gestureInProgress = false;
touchInfo.isTap = true;
touchInfo.initMenuXPos = 0;
touchInfo.initMenuIndex = 0;
touchInfo.lastSlideSpeed = 0;
hidInitializeTouchScreen();
}
void handleTappingOnApp(menu_s* menu, int px) {
int i = 0;
menuEntry_s *me = NULL;
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) {
int entry_start_x = 29 + i * (140 + 30);
int entry_start_x = layoutobj->posStart[0] + i * layoutobj->posEnd[0];
int screen_width = 1280;
if (entry_start_x >= (screen_width - menu->xPos))
break;
if (px >= (entry_start_x + menu->xPos) && px <= (entry_start_x + menu->xPos) + 140 ) {
if (px >= (entry_start_x + menu->xPos) && px <= (entry_start_x + menu->xPos) + layoutobj->size[0]) {
launchMenuEntryTask(me);
break;
}
@ -58,91 +48,100 @@ void handleTappingOnOpenLaunch(menu_s* menu) {
}
}
static inline bool checkInsideTextLayoutObject(ThemeLayoutId id, int x, int y) {
ThemeLayoutObject *layoutobj = &themeCurrent.layoutObjects[id];
if (!layoutobj->visible) return false;
return x > layoutobj->posFinal[0] && x < layoutobj->posFinal[0]+(layoutobj->touchSize[0] ? layoutobj->touchSize[0] : layoutobj->textSize[0]) && y > layoutobj->posFinal[1]-layoutobj->touchSize[1] && y < layoutobj->posFinal[1];
}
void handleTouch(menu_s* menu) {
touchPosition currentTouch;
u32 touches = hidTouchCount();
ThemeLayoutObject *layoutobj = NULL;
HidTouchScreenState touch = {0};
hidGetTouchScreenStates(&touch, 1);
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuListTiles];
int entries_count = layoutobj->posEnd[0];
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MenuList];
// On touch start.
if (touches == 1 && !touchInfo.gestureInProgress) {
hidTouchRead(&currentTouch, 0);
if (touch.count == 1 && !touchInfo.gestureInProgress) {
touchInfo.gestureInProgress = true;
touchInfo.firstTouch = currentTouch;
touchInfo.prevTouch = currentTouch;
touchInfo.firstTouch = touch.touches[0];
touchInfo.prevTouch = touch.touches[0];
touchInfo.isTap = true;
touchInfo.initMenuXPos = menu->xPos;
touchInfo.initMenuIndex = menu->curEntry;
touchInfo.lastSlideSpeed = 0;
menu->slideSpeed = 0;
}
// On touch moving.
else if (touches >= 1 && touchInfo.gestureInProgress) {
hidTouchRead(&currentTouch, 0);
else if (touch.count >= 1 && touchInfo.gestureInProgress) {
touchInfo.lastSlideSpeed = ((int)(touch.touches[0].x - touchInfo.prevTouch.x));
touchInfo.prevTouch = currentTouch;
touchInfo.prevTouch = touch.touches[0];
if (touchInfo.isTap && (abs(touchInfo.firstTouch.px - currentTouch.px) > TAP_MOVEMENT_GAP || abs(touchInfo.firstTouch.py - currentTouch.py) > TAP_MOVEMENT_GAP)) {
if (touchInfo.isTap && (abs(touchInfo.firstTouch.x - touch.touches[0].x) > TAP_MOVEMENT_GAP || abs(touchInfo.firstTouch.y - touch.touches[0].y) > TAP_MOVEMENT_GAP)) {
touchInfo.isTap = false;
}
if (!menuIsMsgBoxOpen() && touchInfo.firstTouch.py > LISTING_START_Y && touchInfo.firstTouch.py < LISTING_END_Y && !touchInfo.isTap && menu->nEntries > 7) {
menu->xPos = touchInfo.initMenuXPos + (currentTouch.px - touchInfo.firstTouch.px);
menu->curEntry = touchInfo.initMenuIndex + ((int) (touchInfo.firstTouch.px - currentTouch.px) / 170);
if (!menuIsMsgBoxOpen() && touchInfo.firstTouch.y > layoutobj->posStart[1] && touchInfo.firstTouch.y < layoutobj->posStart[1]+layoutobj->size[1] && !touchInfo.isTap && menu->nEntries > entries_count) {
if (menu->curEntry < 0)
menu->curEntry = 0;
if (menu->curEntry >= menu->nEntries - 6)
menu->curEntry = menu->nEntries - 7;
if (!touchInfo.isTap) {
menu->slideSpeed = touchInfo.lastSlideSpeed;
}
}
}
// On touch end.
else if (touchInfo.gestureInProgress) {
int x1 = touchInfo.firstTouch.px;
int y1 = touchInfo.firstTouch.py;
int x2 = touchInfo.prevTouch.px;
int y2 = touchInfo.prevTouch.py;
int x1 = touchInfo.firstTouch.x;
int y1 = touchInfo.firstTouch.y;
int x2 = touchInfo.prevTouch.x;
int y2 = touchInfo.prevTouch.y;
if (!touchInfo.isTap) {
menu->slideSpeed = touchInfo.lastSlideSpeed;
}
bool netloader_active = menuIsNetloaderActive();
if (menuIsMsgBoxOpen() && !netloader_active) {
layoutobj = &themeCurrent.layoutObjects[ThemeLayoutId_MsgBoxSeparator];
MessageBox currMsgBox = menuGetCurrentMsgBox();
int start_x = 1280 / 2 - currMsgBox.width / 2;
int start_y = (720 / 2 - currMsgBox.height / 2) + (currMsgBox.height - 80);
int start_y = (720 / 2 - currMsgBox.height / 2) + currMsgBox.height;
int end_x = start_x + currMsgBox.width;
int end_y = start_y + 80;
int end_y = start_y;
start_y+= layoutobj->posStart[1];
if (x1 > start_x && x1 < end_x && y1 > start_y && y1 < end_y && touchInfo.isTap) {
menuCloseMsgBox();
}
} else if (touchInfo.isTap && !netloader_active) {
// App Icons
if (y1 > LISTING_START_Y && y1 < LISTING_END_Y) {
handleTappingOnApp(menu, touchInfo.prevTouch.px);
if (y1 > layoutobj->posStart[1] && y1 < layoutobj->posStart[1]+layoutobj->size[1]) {
handleTappingOnApp(menu, touchInfo.prevTouch.x);
}
// Bottom Buttons
else if (y1 > BUTTON_START_Y && y1 < BUTTON_END_Y) {
// Back Button for non-empty directory
if (menu->nEntries != 0 && x1 > BACK_BUTTON_START_X && x1 < BACK_BUTTON_END_X) {
else {
// Back Button
if (checkInsideTextLayoutObject(ThemeLayoutId_ButtonB, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonBText, x1, y1)) {
launchMenuBackTask();
}
// Open/Launch Button / Back Button for empty directories
else if (x1 > LAUNCH_BUTTON_START_X && x1 < LAUNCH_BUTTON_END_X) {
if (menu->nEntries == 0) {
launchMenuBackTask();
} else {
// Open/Launch Button
else if (menu->nEntries != 0 && (checkInsideTextLayoutObject(ThemeLayoutId_ButtonA, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonAText, x1, y1))) {
handleTappingOnOpenLaunch(menu);
}
}
}
// Star
else {
// Star Button
else if (menu->nEntries != 0) {
int i;
menuEntry_s* me;
for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next);
if (me->type != ENTRY_TYPE_THEME && x1 > STAR_BUTTON_START_X && x1 < STAR_BUTTON_END_X
&& y1 > STAR_BUTTON_START_Y && y1 < STAR_BUTTON_END_Y) {
if (me->type != ENTRY_TYPE_THEME && (checkInsideTextLayoutObject(ThemeLayoutId_ButtonX, x1, y1) || checkInsideTextLayoutObject(ThemeLayoutId_ButtonXText, x1, y1))) {
menuHandleXButton();
}
}
}
}
// Vertical Swipe
else if (abs(x1 - x2) < VERTICAL_SWIPE_HORIZONTAL_PLAY && distance(x1, y1, x2, y2) > VERTICAL_SWIPE_MINIMUM_DISTANCE) {
// Swipe up to go back
@ -155,7 +154,7 @@ void handleTouch(menu_s* menu) {
}
}
// Horizontal Swipe
else if (y1 < LISTING_START_Y && y2 < LISTING_START_Y) {
else if (y1 < layoutobj->posStart[1] && y2 < layoutobj->posStart[1]) {
if (abs(y1 - y2) < HORIZONTAL_SWIPE_VERTICAL_PLAY && distance(x1, y1, x2, y2) > HORIZONTAL_SWIPE_MINIMUM_DISTANCE) {
// Swipe left to go into theme-menu
if (x1 - x2 > 0) {

View File

@ -5,11 +5,13 @@
struct touchInfo_s {
bool gestureInProgress;
touchPosition firstTouch;
touchPosition prevTouch;
HidTouchState firstTouch;
HidTouchState prevTouch;
bool isTap;
int initMenuXPos;
int initMenuIndex;
int lastSlideSpeed;
};
void touchInit(void);
void handleTouch(menu_s* menu);

View File

@ -1,6 +1,7 @@
#include <SFML/Graphics.hpp>
#include <string.h>
#include <math.h>
#include <physfs.h>
extern "C" {
@ -10,12 +11,13 @@ extern "C" {
color_t pixels[720][1280];
int main()
int main(int argc, char **argv)
{
sf::RenderWindow window(sf::VideoMode(1280, 720), "Test");
window.setFramerateLimit(60);
menuStartupPath();
PHYSFS_init(argv[0]);
assetsInit();
themeStartup(THEME_PRESET_LIGHT);
textInit();
@ -62,6 +64,7 @@ int main()
netloaderExit();
fontExit();
assetsExit();
PHYSFS_deinit();
return 0;
}

BIN
resources/eth_none_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B