├── doc ├── ShellSecBat.png ├── ShellDateSecBat.png ├── ShellDriveState.png └── ShellPSTVDisplay.png ├── ShellDateSecBat └── ShellSecBat.txt ├── export.yml ├── LICENSE.md ├── CMakeLists.txt ├── README.md └── main.c /doc/ShellSecBat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationNT414C/ShellSecBat/HEAD/doc/ShellSecBat.png -------------------------------------------------------------------------------- /doc/ShellDateSecBat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationNT414C/ShellSecBat/HEAD/doc/ShellDateSecBat.png -------------------------------------------------------------------------------- /doc/ShellDriveState.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationNT414C/ShellSecBat/HEAD/doc/ShellDriveState.png -------------------------------------------------------------------------------- /doc/ShellPSTVDisplay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationNT414C/ShellSecBat/HEAD/doc/ShellPSTVDisplay.png -------------------------------------------------------------------------------- /ShellDateSecBat/ShellSecBat.txt: -------------------------------------------------------------------------------- 1 | Features:11 2 | Time:110/ 3 | Drives:11.111111 4 | LeftKey:101 5 | RightKey:201 6 | -------------------------------------------------------------------------------- /export.yml: -------------------------------------------------------------------------------- 1 | shellbat: 2 | attributes: 0 3 | version: 4 | major: 1 5 | minor: 0 6 | main: 7 | start: module_start 8 | stop: module_stop 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 OperationNT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) 4 | if(DEFINED ENV{VITASDK}) 5 | set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file") 6 | else() 7 | message(FATAL_ERROR "Please define VITASDK to point to your SDK path!") 8 | endif() 9 | endif() 10 | 11 | project(ShellSecBat) 12 | include("${VITASDK}/share/vita.cmake" REQUIRED) 13 | 14 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-q -Wall -O3 -std=gnu99") 15 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions") 16 | 17 | include_directories( 18 | ) 19 | 20 | link_directories( 21 | ${CMAKE_CURRENT_BINARY_DIR} 22 | ) 23 | 24 | add_executable(shellsecbat 25 | ${CMAKE_SOURCE_DIR}/main.c 26 | ) 27 | 28 | target_link_libraries(shellsecbat 29 | taihen_stub 30 | SceLibKernel_stub 31 | k 32 | gcc 33 | SceSysmem_stub 34 | SceCtrl_stub 35 | SceRegistryMgr_stub 36 | SceRtc_stub 37 | ScePower_stub 38 | SceKernelModulemgr_stub 39 | SceIofilemgr_stub 40 | ) 41 | 42 | set_target_properties(shellsecbat 43 | PROPERTIES LINK_FLAGS "-nostdlib" 44 | ) 45 | 46 | vita_create_self(shellsecbat.suprx shellsecbat 47 | UNSAFE 48 | CONFIG ${CMAKE_SOURCE_DIR}/export.yml 49 | ) 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShellSecBat 2 | 3 | Henkaku plugin that status bar displayed content customization. 4 | 5 | This is a fusion between "ShellBat" and "LastSeconds" plugins. 6 | It also solves "12 hours" format issue when those plugins were manually combined. 7 | 8 | ![Screenshot](https://github.com/OperationNT414C/ShellSecBat/blob/master/doc/ShellSecBat.png?raw=true) 9 | 10 | Configurated ShellSecBat allows to add the date to the status bar (in addition to the time with seconds and the battery percent). 11 | 12 | ![Screenshot](https://github.com/OperationNT414C/ShellSecBat/blob/master/doc/ShellDateSecBat.png?raw=true) 13 | 14 | You can also visualize your drive state (total size and remaining space) by maintaining SELECT button and L or R trigger. 15 | 16 | ![Screenshot](https://github.com/OperationNT414C/ShellSecBat/blob/master/doc/ShellDriveState.png?raw=true) 17 | 18 | On a PlayStation TV, the useless battery percent is hidden (even if the configuration tries to enable it). All other features (seconds, date and drive state) are still available. 19 | 20 | ![Screenshot](https://github.com/OperationNT414C/ShellSecBat/blob/master/doc/ShellPSTVDisplay.png?raw=true) 21 | 22 | 23 | ### Installation 24 | 25 | Add the plugin under `*main` section in `ur0:tai/config.txt` or `ux0:tai/config.txt`: 26 | 27 | ``` 28 | *main 29 | ux0:tai/shellsecbat.suprx 30 | ``` 31 | 32 | Please remove any other plugin which impacts the status bar display (like "ShellBat", "ShellDateSecBat" or "LastSeconds"). 33 | 34 | 35 | ### Configuration 36 | 37 | ShellSecBat can be configurated through a "ShellSecBat.txt" file. The file size should not exceed 116 bytes and its content should look like: 38 | 39 | ``` 40 | Features:11 41 | Time:100/ 42 | Drives:11.111111 43 | LeftKey:101 44 | RightKey:201 45 | ``` 46 | 47 | This configuration sample matches the official default ShellSecBat settings (when there is no found configuration file). 48 | 49 | Only the following paths are supported for the configuration file (they will be check in this same order): 50 | 51 | * `ux0:/data/ShellSecBat.txt` 52 | * `ux0:/tai/ShellSecBat.txt` 53 | * `ur0:/tai/ShellSecBat.txt` 54 | * `ur0:/plugins/ShellSecBat.txt` 55 | 56 | 57 | The file must follows the following rules: 58 | 59 | ``` 60 | Features:[Drives display][Battery display] 61 | Time:[Seconds display][Date display][Year display][Date separator] 62 | Drives:[Skip unmounted][Free space display][Space decimal separator][imc0:][ur0:][ux0:][uma0:][xmc0:][grw0:] 63 | LeftKey:[Keys combination for previous drive display] 64 | RightKey:[Keys combination for next drive display] 65 | ``` 66 | 67 | 68 | You should be aware that configuration file reading is very basic (and it could easily fail if there is even the slightest mistake). 69 | 70 | Keywords `Feature:`, `Time:`, `Drives:`, `LeftKey:` and `RightKey:` are used to find configuration parts. 71 | Once a configuration part is found, configuration parameters must directly follow the `:` character (without any blank character between): 72 | 73 | * Valid configuration part: `Features:01` 74 | * Invalid configuration part: `Features: 01` 75 | 76 | For parameters which can be activated (all parameters except keys and separators), `1` means that it is enabled and any other character means that it is disabled. 77 | 78 | `LeftKey:` and `RightKey:` must be followed by an hexadecimal value which describes the key combination. Hexadecimal flags for each key can be found here (on `SceCtrlButtons` section): 79 | 80 | https://github.com/vitasdk/vita-headers/blob/master/include/psp2/ctrl.h 81 | 82 | 83 | Additional rules are applied: 84 | * Keywords can appear in any order in the configuration but parameters must always exactly match what the keyword expects 85 | * `Drives:`, `LeftKey:` and `RightKey:` won't be parsed if "[Drives display]" is disabled 86 | * On PlayStation TV, "[Battery display]" is ignored as it will always be disabled 87 | 88 | 89 | You can simulate ShellSecBat and ShellDateSecBat previous versions (prior to configuration file support) with the following configurations: 90 | 91 | * ShellSecBat V4 behavior: `Features:11 Time:100/ Drives:01.011100 LeftKey:101 RightKey:201` 92 | * ShellDateSecBat V4 behavior: `Features:11 Time:110/ Drives:01.011100 LeftKey:101 RightKey:201` 93 | 94 | 95 | 96 | ### Credits 97 | 98 | * nowrep for "ShellBat" (https://github.com/nowrep/vita-shellbat) 99 | * theorywrong for "LastSeconds" (https://github.com/theorywrong/LastSeconds) 100 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ShellSecBat plugin 3 | * Copyright (c) 2017 OperationNT 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | 39 | static char *devices[] = { 40 | "imc0:", 41 | "ur0:", 42 | "ux0:", 43 | "uma0:", 44 | "xmc0:", 45 | "grw0:" 46 | }; 47 | #define N_DEVICES (sizeof(devices) / sizeof(char **)) 48 | 49 | 50 | // Configuration format 51 | // Features:[Drives display][Battery display] 52 | // Time:[Seconds display][Date display][Year display][Date separator] 53 | // Drives:[Skip unmounted][Free space display][Space decimal separator][imc0:][ur0:][ux0:][uma0:][xmc0:][grw0:] 54 | // LeftKey:[Keys combination for previous drive display] 55 | // RightKey:[Keys combination for next drive display] 56 | 57 | // File content for default settings 58 | // Features:11 Time:100/ Drives:11.111111 LeftKey:101 RightKey:201 59 | 60 | // File content for ShellSecBat V4 behavior 61 | // Features:11 Time:100/ Drives:01.011100 LeftKey:101 RightKey:201 62 | 63 | // File content for ShellDateSecBat V4 behavior 64 | // Features:11 Time:110/ Drives:01.011100 LeftKey:101 RightKey:201 65 | 66 | 67 | static char *configPathes[] = { 68 | "ux0:/data/ShellSecBat.txt", 69 | "ux0:/tai/ShellSecBat.txt", 70 | "ur0:/tai/ShellSecBat.txt", 71 | "ur0:/plugins/ShellSecBat.txt" 72 | }; 73 | #define N_PATHES (sizeof(configPathes) / sizeof(char **)) 74 | 75 | static int displayDrives = 1; 76 | static int displayBattery = 1; 77 | 78 | static int displaySeconds = 1; 79 | static int displayDate = 0; 80 | static int displayYear = 0; 81 | static char dateSeparator = '/'; 82 | 83 | static int skipUnmounted = 1; 84 | static int displayFree = 1; 85 | static char spaceSeparator = '.'; 86 | static int displayedDrives[N_DEVICES] = {1,1,1,1,1,1}; 87 | static unsigned int driveKeys[2] = {SCE_CTRL_SELECT|SCE_CTRL_LTRIGGER, SCE_CTRL_SELECT|SCE_CTRL_RTRIGGER}; 88 | 89 | 90 | static char* seekChar(char* iStr, char iChar) 91 | { 92 | while (*iStr != iChar && *iStr != '\0') 93 | iStr++; 94 | return (*iStr != '\0') ? iStr : NULL; 95 | } 96 | 97 | static char* seekDoubleChar(char* iStr, char iChar1, char iChar2) 98 | { 99 | while ((iStr[0] != iChar1 || iStr[1] != iChar2) && *iStr != '\0') 100 | iStr++; 101 | return (*iStr != '\0') ? iStr : NULL; 102 | } 103 | 104 | static void getHexValue(char* iStr, unsigned int* oValue) 105 | { 106 | char* str = iStr; 107 | while (((*str >= '0' && *str <= '9') || (*str >= 'a' && *str <= 'f') || (*str >= 'A' && *str <= 'F')) && str-iStr < 8) 108 | str++; 109 | 110 | char size = str-iStr; 111 | if (size <= 0 || (*str != ' ' && *str != '\t' && *str != '\r' && *str != '\n' && *str != '\0')) 112 | return; 113 | 114 | *oValue = 0; 115 | unsigned int factor = 1; 116 | for (str -= 1; str >= iStr; str--, factor *= 16) 117 | { 118 | if (*str >= 'a' && *str <= 'f') 119 | *str -= ('a' - 'A'); 120 | if (*str >= 'A' && *str <= 'F') 121 | *oValue += (10 + *str - 'A') * factor; 122 | else 123 | *oValue += (*str - '0') * factor; 124 | } 125 | } 126 | 127 | 128 | static char *units[] = { 129 | "B ", 130 | "KB", 131 | "MB", 132 | "GB", 133 | "TB", 134 | "PB" 135 | }; 136 | #define N_UNITS (sizeof(units) / sizeof(char **)) 137 | 138 | static int unitType(SceOff iBytes, int* oConv, int* oCent) 139 | { 140 | SceOff convBytes = iBytes; 141 | SceOff divider = 1; 142 | int res = 0; 143 | while (convBytes > 1023 && res < N_UNITS-1) 144 | { 145 | divider *= 1024; 146 | convBytes /= 1024; 147 | res++; 148 | } 149 | *oConv = (int)convBytes; 150 | *oCent = (int)((iBytes%divider)/(divider/102)); 151 | return res; 152 | } 153 | 154 | static int displayed_disk = N_DEVICES; 155 | 156 | /* 157 | offset 0x1844f0: 158 | int status_draw_battery_patched(int a1, uint8_t a2); 159 | */ 160 | 161 | static SceUID g_hooks[2]; 162 | 163 | uint32_t text_addr, text_size, data_addr, data_size; 164 | 165 | // Functions 166 | 167 | /** 168 | * ScePafWidget_39B15B98 169 | * 170 | * @param widget Widget ptr 171 | * @param size Font size (22.0 - standard font size in statusbar) 172 | * @param unk0 Unknown, pass 1 173 | * @param pos Start of part of text to set size 174 | * @param len Length of part of text to set size from pos 175 | */ 176 | static int (*scePafWidgetSetFontSize)(void *widget, float size, int unk0, int pos, int len); 177 | 178 | 179 | static void get_functions_retail_355() 180 | { 181 | scePafWidgetSetFontSize = (void*) text_addr + 0x45c950; 182 | } 183 | 184 | static void get_functions_retail_360() 185 | { 186 | scePafWidgetSetFontSize = (void*) text_addr + 0x45ce80; 187 | } 188 | 189 | static void get_functions_retail_365_368() 190 | { 191 | scePafWidgetSetFontSize = (void*) text_addr + 0x45D2C8; 192 | } 193 | 194 | static void get_functions_testkit_360() 195 | { 196 | scePafWidgetSetFontSize = (void*) text_addr + 0x453038; 197 | } 198 | 199 | static void get_functions_testkit_365() 200 | { 201 | scePafWidgetSetFontSize = (void*) text_addr + 0x453478; 202 | } 203 | 204 | static void get_functions_devkit_360() 205 | { 206 | scePafWidgetSetFontSize = (void*) text_addr + 0x44E5F8; 207 | } 208 | 209 | static void get_functions_devkit_365() 210 | { 211 | scePafWidgetSetFontSize = (void*) text_addr + 0x44EA68; 212 | } 213 | 214 | static int digit_len(int num) 215 | { 216 | if (num < 10) { 217 | return 1; 218 | } else if (num < 100) { 219 | return 2; 220 | } else { 221 | return 3; 222 | } 223 | } 224 | 225 | typedef struct { 226 | int start; 227 | int length; 228 | float height; 229 | } TextMod; 230 | 231 | #define N_FONT_CHANGE 3 232 | static TextMod fontChange[N_FONT_CHANGE] = { {-1, 2, 16.0} , {-1, 0, 20.0} , {-1, 1, 16.0} }; 233 | 234 | static int in_draw_time = 0; 235 | 236 | static tai_hook_ref_t ref_hook0; 237 | static int status_draw_time_patched(void *a1, int a2) 238 | { 239 | in_draw_time = 1; 240 | int out = TAI_CONTINUE(int, ref_hook0, a1, a2); 241 | in_draw_time = 0; 242 | if (a1) { 243 | for (int i = 0; i < N_FONT_CHANGE; i++) 244 | { 245 | TextMod* curFont = &fontChange[i]; 246 | if (curFont->start != -1) 247 | scePafWidgetSetFontSize(a1, curFont->height, 1, curFont->start, curFont->length); 248 | } 249 | } 250 | return out; 251 | } 252 | 253 | static tai_hook_ref_t ref_hook1; 254 | static uint16_t **some_strdup_patched(uint16_t **a1, uint16_t *a2, int a2_size) 255 | { 256 | if (in_draw_time) 257 | { 258 | char buff[32]; 259 | int len; 260 | int i; 261 | 262 | if (displayDrives) 263 | { 264 | // Here, buttons state is checked every 1.001 seconds. 265 | SceCtrlData pad; 266 | sceCtrlPeekBufferPositive(0, &pad, 1); 267 | 268 | int nextDrive = ((pad.buttons & driveKeys[1]) == driveKeys[1]) ? 1 : 0; 269 | int prevDrive = ((pad.buttons & driveKeys[0]) == driveKeys[0]) ? 1 : 0; 270 | 271 | SceIoDevInfo info; 272 | int res = -1; 273 | 274 | if (1 == nextDrive + prevDrive) // XOR on drive display commands 275 | { 276 | while (res < 0) 277 | { 278 | do 279 | { 280 | displayed_disk = (nextDrive ? (displayed_disk+1) : (displayed_disk+N_DEVICES))%(N_DEVICES+1); 281 | } 282 | while (displayed_disk < N_DEVICES && !displayedDrives[displayed_disk]); 283 | 284 | if (displayed_disk == N_DEVICES) 285 | break; 286 | 287 | res = sceIoDevctl(devices[displayed_disk], 0x3001, NULL, 0, &info, sizeof(SceIoDevInfo)); 288 | if (!skipUnmounted) 289 | break; 290 | } 291 | } 292 | else if (displayed_disk < N_DEVICES) 293 | res = sceIoDevctl(devices[displayed_disk], 0x3001, NULL, 0, &info, sizeof(SceIoDevInfo)); 294 | 295 | if (displayed_disk < N_DEVICES) 296 | { 297 | if (res >= 0) 298 | { 299 | int freeConv = 0; 300 | int freeDec = 0; 301 | int freeUnit = unitType(displayFree ? info.free_size : (info.max_size-info.free_size), &freeConv, &freeDec); 302 | 303 | int maxConv = 0; 304 | int maxDec = 0; 305 | int maxUnit = unitType(info.max_size, &maxConv, &maxDec); 306 | 307 | if (freeDec > 0) 308 | { 309 | len = sceClibSnprintf(buff, 32, "%s %d%c%02d%s/%d%c%02d%s", devices[displayed_disk], 310 | freeConv, spaceSeparator, freeDec, units[freeUnit], 311 | maxConv, spaceSeparator, maxDec, units[maxUnit]); 312 | } 313 | else 314 | { 315 | len = sceClibSnprintf(buff, 32, "%s %d%s/%d%c%02d%s", devices[displayed_disk], 316 | freeConv, units[freeUnit], 317 | maxConv, spaceSeparator, maxDec, units[maxUnit]); 318 | } 319 | 320 | i = 6; 321 | while (i < len && buff[i] != '/') i++; 322 | fontChange[0].start = i-2; 323 | fontChange[0].length = 2; 324 | 325 | fontChange[2].start = len-2; 326 | fontChange[2].length = 2; 327 | } 328 | else 329 | { 330 | // The 2 spaces at string end avoid a font size issue. 331 | len = sceClibSnprintf(buff, 32, "%s Not mounted ", devices[displayed_disk]); 332 | 333 | fontChange[0].start = -1; 334 | fontChange[2].start = -1; 335 | } 336 | 337 | for (i = 0; i < len; ++i) { 338 | a2[i] = buff[i]; 339 | } 340 | a2[len] = 0; 341 | 342 | fontChange[1].start = 0; 343 | fontChange[1].length = 4; 344 | return TAI_CONTINUE(uint16_t**, ref_hook1, a1, a2, len); 345 | } 346 | } 347 | 348 | SceDateTime time_local; 349 | SceDateTime time_utc; 350 | sceRtcGetCurrentClock(&time_utc, 0); 351 | 352 | SceRtcTick tick; 353 | sceRtcGetTick(&time_utc, &tick); 354 | sceRtcConvertUtcToLocalTime(&tick, &tick); 355 | sceRtcSetTick(&time_local, &tick); 356 | 357 | if (displayDate) 358 | { 359 | int date_format = SCE_SYSTEM_PARAM_DATE_FORMAT_DDMMYYYY; 360 | sceRegMgrGetKeyInt("/CONFIG/DATE", "date_format", &date_format); 361 | 362 | if (displayYear) 363 | { 364 | if (SCE_SYSTEM_PARAM_DATE_FORMAT_DDMMYYYY == date_format) 365 | len = sceClibSnprintf(buff, 32, "%02d%c%02d%c%04d ", time_local.day, dateSeparator, time_local.month, dateSeparator, time_local.year); 366 | else if (SCE_SYSTEM_PARAM_DATE_FORMAT_YYYYMMDD == date_format) 367 | len = sceClibSnprintf(buff, 32, "%04d%c%02d%c%02d ", time_local.year, dateSeparator, time_local.month, dateSeparator, time_local.day); 368 | else 369 | len = sceClibSnprintf(buff, 32, "%02d%c%02d%c%04d ", time_local.month, dateSeparator, time_local.day, dateSeparator, time_local.year); 370 | } 371 | else 372 | { 373 | if (SCE_SYSTEM_PARAM_DATE_FORMAT_DDMMYYYY == date_format) 374 | len = sceClibSnprintf(buff, 32, "%02d%c%02d ", time_local.day, dateSeparator, time_local.month); 375 | else 376 | len = sceClibSnprintf(buff, 32, "%02d%c%02d ", time_local.month, dateSeparator, time_local.day); 377 | } 378 | 379 | uint16_t tmp[16]; 380 | for (i = 0; i < a2_size; ++i) { 381 | tmp[i] = a2[i]; 382 | } 383 | for (i = 0; i < len; ++i) { 384 | a2[i] = buff[i]; 385 | } 386 | for (i = 0; i < a2_size; ++i) { 387 | a2[len+i] = tmp[i]; 388 | } 389 | a2_size += len; 390 | } 391 | 392 | int is_ampm = (a2[a2_size - 1] == 'M'); 393 | if (displaySeconds) 394 | { 395 | len = sceClibSnprintf(buff, 32, ":%02d", time_local.second); 396 | if (is_ampm) 397 | { 398 | for (i = 0; i < 3; ++i) { 399 | a2[a2_size + len - 3 + i] = a2[a2_size - 3 + i]; 400 | } 401 | for (i = 0; i < len; ++i) { 402 | a2[a2_size - 3 + i] = buff[i]; 403 | } 404 | } 405 | else 406 | { 407 | for (i = 0; i < len; ++i) { 408 | a2[a2_size + i] = buff[i]; 409 | } 410 | } 411 | a2_size += len; 412 | } 413 | 414 | if (displayBattery) 415 | { 416 | static int oldpercent = 0; 417 | int percent = scePowerGetBatteryLifePercent(); 418 | if (percent < 0 || percent > 100) { 419 | percent = oldpercent; 420 | } 421 | oldpercent = percent; 422 | 423 | len = sceClibSnprintf(buff, 32, " %d%%", percent); 424 | for (i = 0; i < len; ++i) { 425 | a2[a2_size + i] = buff[i]; 426 | } 427 | 428 | fontChange[0].start = is_ampm ? (a2_size - 2) : -1; 429 | 430 | fontChange[1].start = a2_size + 2; 431 | fontChange[1].length = digit_len(percent); 432 | 433 | fontChange[2].start = fontChange[1].start + fontChange[1].length; 434 | fontChange[2].length = 1; 435 | 436 | a2_size += len; 437 | } 438 | else 439 | { 440 | fontChange[0].start = is_ampm ? (a2_size - 2) : -1; 441 | fontChange[1].start = -1; 442 | fontChange[2].start = -1; 443 | } 444 | a2[a2_size] = 0; 445 | 446 | return TAI_CONTINUE(uint16_t**, ref_hook1, a1, a2, a2_size); 447 | } 448 | return TAI_CONTINUE(uint16_t**, ref_hook1, a1, a2, a2_size); 449 | } 450 | 451 | void _start() __attribute__ ((weak, alias ("module_start"))); 452 | int module_start(SceSize argc, const void *args) 453 | { 454 | tai_module_info_t info; 455 | info.size = sizeof(info); 456 | int ret = taiGetModuleInfo("SceShell", &info); 457 | if (ret < 0) { 458 | return SCE_KERNEL_START_FAILED; 459 | } 460 | 461 | // Modified from TheOfficialFloW/Adrenaline 462 | SceKernelModuleInfo mod_info; 463 | mod_info.size = sizeof(SceKernelModuleInfo); 464 | ret = sceKernelGetModuleInfo(info.modid, &mod_info); 465 | if (ret < 0) { 466 | return SCE_KERNEL_START_FAILED; 467 | } 468 | text_addr = (uint32_t) mod_info.segments[0].vaddr; 469 | text_size = (uint32_t) mod_info.segments[0].memsz; 470 | data_addr = (uint32_t) mod_info.segments[1].vaddr; 471 | data_size = (uint32_t) mod_info.segments[1].memsz; 472 | 473 | uint32_t offsets[2]; 474 | 475 | switch (info.module_nid) { 476 | case 0x8978D25D: // retail 3.55 SceShell 477 | offsets[0] = 0x183EF0; 478 | offsets[1] = 0x40DBD8; 479 | get_functions_retail_355(); 480 | break; 481 | 482 | case 0x0552F692: // retail 3.60 SceShell 483 | offsets[0] = 0x183ea4; 484 | offsets[1] = 0x40e0b4; 485 | get_functions_retail_360(); 486 | break; 487 | 488 | case 0x5549BF1F: // retail 3.65 SceShell 489 | case 0x34B4D82E: // retail 3.67 SceShell 490 | case 0x12DAC0F3: // retail 3.68 SceShell 491 | offsets[0] = 0x183F6C; 492 | offsets[1] = 0x40E4FC; 493 | get_functions_retail_365_368(); 494 | break; 495 | 496 | case 0xEAB89D5C: // PTEL 3.60 SceShell 497 | offsets[0] = 0x17C2D8; 498 | offsets[1] = 0x404828; 499 | get_functions_testkit_360(); 500 | break; 501 | 502 | case 0x587F9CED: // PTEL 3.65 SceShell 503 | offsets[0] = 0x17C3A0; 504 | offsets[1] = 0x404C68; 505 | get_functions_testkit_365(); 506 | break; 507 | 508 | case 0x6CB01295: // PDEL 3.60 SceShell 509 | offsets[0] = 0x17B8DC; 510 | offsets[1] = 0x400028; 511 | get_functions_devkit_360(); 512 | break; 513 | 514 | case 0xE6A02F2B: // PDEL 3.65 SceShell 515 | offsets[0] = 0x17B9A4; 516 | offsets[1] = 0x400498; 517 | get_functions_devkit_365(); 518 | break; 519 | 520 | default: 521 | return SCE_KERNEL_START_FAILED; 522 | } 523 | 524 | g_hooks[0] = taiHookFunctionOffset(&ref_hook0, 525 | info.modid, 526 | 0, // segidx 527 | offsets[0], // offset 528 | 1, // thumb 529 | status_draw_time_patched); 530 | g_hooks[1] = taiHookFunctionOffset(&ref_hook1, 531 | info.modid, 532 | 0, // segidx 533 | offsets[1], // offset 534 | 1, // thumb 535 | some_strdup_patched); 536 | 537 | // Setup configuration from file 538 | displayBattery = (sceKernelGetModel() != SCE_KERNEL_MODEL_VITATV); 539 | 540 | SceUID fd = -1; 541 | int i = 0; 542 | int fileSize = 0; 543 | while (i < N_PATHES && fd < 0) 544 | { 545 | fd = sceIoOpen(configPathes[i], SCE_O_RDONLY, 0666); 546 | if (fd >= 0) 547 | { 548 | fileSize = sceIoLseek(fd, 0, SCE_SEEK_END); 549 | if (fileSize > 116) 550 | { 551 | sceIoClose(fd); 552 | fd = -1; 553 | } 554 | } 555 | i++; 556 | } 557 | 558 | if (fd >= 0) 559 | { 560 | char configStr[128]; 561 | sceIoLseek(fd, 0, SCE_SEEK_SET); 562 | sceIoRead(fd, configStr, fileSize); 563 | sceIoClose(fd); 564 | configStr[fileSize] = '\0'; 565 | sceClibMemset(&(configStr[fileSize+1]), ' ', 11); 566 | 567 | // Look for features 568 | char* seeked = configStr; 569 | if ((seeked = seekDoubleChar(seeked, 'F', 'e')) && (seeked = seekChar(seeked, ':'))) 570 | { 571 | displayDrives = ('1' == seeked[1]); 572 | displayBattery = (displayBattery && '1' == seeked[2]); 573 | } 574 | 575 | seeked = configStr; 576 | if ((seeked = seekDoubleChar(seeked, 'T', 'i')) && (seeked = seekChar(seeked, ':'))) 577 | { 578 | displaySeconds = ('1' == seeked[1]); 579 | displayDate = ('1' == seeked[2]); 580 | if (displayDate) 581 | { 582 | displayYear = ('1' == seeked[3]); 583 | if ('\0' != seeked[4]) 584 | dateSeparator = seeked[4]; 585 | } 586 | } 587 | 588 | if (displayDrives) 589 | { 590 | seeked = configStr; 591 | if ((seeked = seekDoubleChar(seeked, 'D', 'r')) && (seeked = seekChar(seeked, ':'))) 592 | { 593 | skipUnmounted = ('1' == seeked[1]); 594 | displayFree = ('1' == seeked[2]); 595 | if ('\0' != seeked[3]) 596 | spaceSeparator = seeked[3]; 597 | for (i = 0; i < N_DEVICES; i++) 598 | displayedDrives[i] = ('1' == seeked[4+i]); 599 | } 600 | 601 | seeked = configStr; 602 | if ((seeked = seekDoubleChar(seeked, 'L', 'e')) && (seeked = seekChar(seeked, ':'))) 603 | getHexValue(&seeked[1], &driveKeys[0]); 604 | 605 | seeked = configStr; 606 | if ((seeked = seekDoubleChar(seeked, 'R', 'i')) && (seeked = seekChar(seeked, ':'))) 607 | getHexValue(&seeked[1], &driveKeys[1]); 608 | } 609 | } 610 | 611 | return SCE_KERNEL_START_SUCCESS; 612 | } 613 | 614 | int module_stop(SceSize argc, const void *args) 615 | { 616 | if (g_hooks[0] >= 0) taiHookRelease(g_hooks[0], ref_hook0); 617 | if (g_hooks[1] >= 0) taiHookRelease(g_hooks[1], ref_hook1); 618 | 619 | return SCE_KERNEL_STOP_SUCCESS; 620 | } 621 | --------------------------------------------------------------------------------