Move to read db on console to reduce strain on the ftp.

This commit is contained in:
Greg
2022-12-18 19:42:49 -07:00
parent 4e9c8d9f0e
commit 241396f9ff
21 changed files with 539 additions and 367 deletions
+2 -1
View File
@@ -20,6 +20,8 @@ enum APICommands
/* ####### Apps functions ####### */
APP_START,
API_APPS_GET_LIST,
API_APPS_GET_INFO_STR,
API_APPS_STATUS,
API_APPS_START,
API_APPS_STOP,
@@ -104,7 +106,6 @@ enum APICommands
API_TARGET_SET_LED,
API_TARGET_DUMP_PROC,
API_TARGET_SET_SETTINGS,
API_TARGET_GET_APPDB,
API_TARGET_GETFILE,
TARGET_END,
+181
View File
@@ -0,0 +1,181 @@
#include "Common.h"
#include "AppDatabase.h"
#include "sqlite3.h"
sqlite3* AppDatabase::OpenDatabase()
{
sqlite3* db;
auto res = sqlite3_open("/system_data/priv/mms/app.db", &db);
if (res != SQLITE_OK)
{
klog("OpenDatabase(): Failed because %s\n", sqlite3_errmsg(db));
return nullptr;
}
else
{
return db;
}
}
const char* GetText(sqlite3_stmt* stmt, int column)
{
auto res = sqlite3_column_text(stmt, column);
if (res != 0)
{
return (const char*)res;
}
return "";
}
bool AppDatabase::GetApps(std::vector<AppInfo> &Apps)
{
int res;
auto db = OpenDatabase();
if (db == nullptr)
{
return false;
}
// Get the current user id.
int ForegroundAccountId;
sceUserServiceGetForegroundUser(&ForegroundAccountId);
// build statement.
char query[0x200];
snprintf(query, sizeof(query), "SELECT * FROM tbl_appbrowse_0%i", ForegroundAccountId);
// Prepare statement.
sqlite3_stmt* stmt;
res = sqlite3_prepare(db, query, -1, &stmt, NULL);
if (res != SQLITE_OK)
{
klog("sqlite3_prepare(): Failed because %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return false;
}
// Execute step until all rows are read.
while ((res = sqlite3_step(stmt)) == SQLITE_ROW)
{
AppInfo info;
strcpy(info.TitleId, GetText(stmt, 0));
strcpy(info.ContentId, GetText(stmt, 1));
strcpy(info.TitleName, GetText(stmt, 2));
strcpy(info.MetaDataPath, GetText(stmt, 3));
strcpy(info.LastAccessTime, GetText(stmt, 4));
info.Visible = sqlite3_column_int(stmt, 8);
info.SortPriority = sqlite3_column_int(stmt, 9);
info.DispLocation = sqlite3_column_int(stmt, 12);
info.CanRemove = sqlite3_column_int(stmt, 13) == 1;
strcpy(info.Category, GetText(stmt, 14));
info.ContentSize = sqlite3_column_int(stmt, 22);
strcpy(info.InstallDate, GetText(stmt, 23));
strcpy(info.UICategory, GetText(stmt, 25));
Apps.push_back(info);
}
if (res != SQLITE_DONE) {
printf("GetApps(): Res %d Error: %s\n", res, sqlite3_errmsg(db));
}
// Release resources.
sqlite3_finalize(stmt);
sqlite3_close(db);
return true;
}
bool AppDatabase::GetAppInfoString(const char* TitleId, char* Out, size_t OutSize, const char* Key)
{
int res;
auto db = OpenDatabase();
if (db == nullptr)
{
return false;
}
// build statement.
char query[0x200];
snprintf(query, sizeof(query), "SELECT * FROM tbl_appinfo WHERE titleId='%s' AND key='%s'", TitleId, Key);
// Prepare statement.
sqlite3_stmt* stmt;
res = sqlite3_prepare(db, query, -1, &stmt, NULL);
if (res != SQLITE_OK)
{
klog("sqlite3_prepare(): Failed because %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return false;
}
res = sqlite3_step(stmt);
if (res == SQLITE_ROW)
{
strlcpy(Out, GetText(stmt, 2), OutSize);
// Release resources.
sqlite3_finalize(stmt);
sqlite3_close(db);
return true;
}
if (res != SQLITE_DONE) {
printf("GetApps(): Res %d Error: %s\n", res, sqlite3_errmsg(db));
}
// Release resources.
sqlite3_finalize(stmt);
sqlite3_close(db);
return false;
}
bool AppDatabase::SetVisibility(const char* TitleId, VisibilityType Visibility)
{
int res;
auto db = OpenDatabase();
if (db == nullptr)
{
return false;
}
// Get the current user id.
int ForegroundAccountId;
sceUserServiceGetForegroundUser(&ForegroundAccountId);
// build statement.
char query[0x200];
snprintf(query, sizeof(query), "UPDATE tbl_appbrowse_0%i SET visible=%i WHERE titleId='%s'", ForegroundAccountId, Visibility, TitleId);
// Execute Statement.
res = sqlite3_exec(db, query, nullptr, nullptr, nullptr);
if (res != SQLITE_OK)
{
klog("sqlite3_exec(): Failed because %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return false;
}
// Release resources.
sqlite3_close(db);
return true;
}
AppDatabase::AppDatabase()
{
}
AppDatabase::~AppDatabase()
{
}
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include "Common.h"
#include "sqlite3.h"
class AppDatabase
{
public:
enum VisibilityType
{
VT_NONE,
VT_VISIBLE,
VT_INVISIBLE,
};
enum DispLocation
{
DL_NONE = 0,
DL_CONTENTAREA = 1,
DL_TV_VIDEO = 2,
DL_LIBRARY = 4,
DL_UNK = 5,
};
struct AppInfo
{
char TitleId[10];
char ContentId[100];
char TitleName[200];
char MetaDataPath[100];
char LastAccessTime[100];
int Visible;
int SortPriority;
int DispLocation;
bool CanRemove;
char Category[10];
int ContentSize;
char InstallDate[100];
char UICategory[10];
};
static bool GetApps(std::vector<AppInfo> &Apps);
static bool GetAppInfoString(const char* TitleId, char* Out, size_t OutSize, const char* Key);
static bool SetVisibility(const char* TitleId, VisibilityType Visibility);
AppDatabase();
~AppDatabase();
private:
static sqlite3* OpenDatabase();
};
+61 -4
View File
@@ -2,20 +2,38 @@
#include "Apps.h"
#include <orbis/SysCore.h>
#include <orbis/SystemService.h>
#include "AppDatabase.h"
void Apps::HandleAPI(OrbisNetId Sock, APIPacket* Packet)
{
// Get the titleId of the App
char titleId[9];
sceNetRecv(Sock, titleId, sizeof(titleId), 0);
char titleId[10];
memset(titleId, 0, sizeof(titleId));
// For commands that need it get the titleId of the App
if (Packet->Command > API_APPS_GET_LIST)
{
sceNetRecv(Sock, titleId, sizeof(titleId), 0);
}
switch (Packet->Command)
{
default:
break;
case API_APPS_STATUS:
case API_APPS_GET_LIST:
GetAppsList(Sock);
break;
case API_APPS_GET_INFO_STR:
GetAppInfoString(Sock, titleId);
break;
case API_APPS_STATUS:
SendAppStatus(Sock, titleId);
break;
@@ -34,6 +52,45 @@ void Apps::HandleAPI(OrbisNetId Sock, APIPacket* Packet)
}
}
void Apps::GetAppsList(OrbisNetId Sock)
{
std::vector<AppDatabase::AppInfo> AppList;
if (!AppDatabase::GetApps(AppList))
{
SockSendInt(Sock, 0);
return;
}
// Send the number of apps.
SockSendInt(Sock, AppList.size());
// Send all of the apps.
for (const auto& App : AppList)
{
sceNetSend(Sock, &App, sizeof(AppDatabase::AppInfo), 0);
}
}
void Apps::GetAppInfoString(OrbisNetId Sock, const char* TitleId)
{
// Get the key we are interested in.
char KeyValue[50];
sceNetRecv(Sock, KeyValue, sizeof(KeyValue), 0);
klog("TitleId: %s\n", TitleId);
klog("Key: %s\n", KeyValue);
// Look up the key for that titleId in the app.db.
char OutStr[200];
memset(OutStr, 0, sizeof(OutStr));
AppDatabase::GetAppInfoString(TitleId, OutStr, sizeof(OutStr), KeyValue);
klog("OutStr: %s\n", OutStr);
// Send back the result.
sceNetSend(Sock, OutStr, sizeof(OutStr), 0);
}
void Apps::SendAppStatus(OrbisNetId Sock, const char* TitleId)
{
int appId = 0;
+2
View File
@@ -11,6 +11,8 @@ public:
void HandleAPI(OrbisNetId Sock, APIPacket* Packet);
private:
void GetAppsList(OrbisNetId Sock);
void GetAppInfoString(OrbisNetId Sock, const char* TitleId);
void SendAppStatus(OrbisNetId Sock, const char* TitleId);
void StartApp(OrbisNetId Sock, const char* TitleId);
void KillApp(OrbisNetId Sock, const char* TitleId);
+5 -2
View File
@@ -51,8 +51,8 @@ call build.bat $(IntDir) "$(TargetName)" "$(SolutionDir)"</NMakeReBuildCommandLi
del /s /q /f $(IntDir)\*.elf
del /s /q /f $(IntDir)\*.oelf</NMakeCleanCommandLine>
<OutDir>$(SolutionDir)</OutDir>
<NMakeIncludeSearchPath>$(OO_PS4_TOOLCHAIN)\include;External\GoldHEN_Plugins_SDK\include;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
<IncludePath>E:\Greg\Repos\Orbis-Suite-3.0\External\GoldHEN_Plugins_SDK\include;$(IncludePath)</IncludePath>
<NMakeIncludeSearchPath>$(OO_PS4_TOOLCHAIN)\include;External\GoldHEN_Plugins_SDK\include;External\LibSQLite-ps4\include;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
<IncludePath>E:\Greg\Repos\Orbis-Suite-3.0\External\LibSQLite-ps4\include;E:\Greg\Repos\Orbis-Suite-3.0\External\GoldHEN_Plugins_SDK\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<NMakeOutput>eboot.bin</NMakeOutput>
@@ -63,6 +63,7 @@ del /s /q /f $(IntDir)\*.oelf</NMakeCleanCommandLine>
<ItemGroup>
<ClCompile Include="API.cpp" />
<ClCompile Include="APIHelper.cpp" />
<ClCompile Include="AppDatabase.cpp" />
<ClCompile Include="Apps.cpp" />
<ClCompile Include="Breakpoint.cpp" />
<ClCompile Include="Debug.cpp" />
@@ -81,9 +82,11 @@ del /s /q /f $(IntDir)\*.oelf</NMakeCleanCommandLine>
<ClCompile Include="Watchpoint.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\External\LibSQLite-ps4\include\sqlite3.h" />
<ClInclude Include="API.h" />
<ClInclude Include="APIHelper.h" />
<ClInclude Include="APIPackets.h" />
<ClInclude Include="AppDatabase.h" />
<ClInclude Include="Apps.h" />
<ClInclude Include="Breakpoint.h" />
<ClInclude Include="Common.h" />
@@ -43,6 +43,9 @@
<Filter Include="Header Files\Utilities\PS Utils">
<UniqueIdentifier>{5094835e-2ace-4366-93ee-a65317ee8a62}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\SQLite">
<UniqueIdentifier>{9184ee1e-6be1-4208-9c34-f2cca837b49f}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="build.bat" />
@@ -97,6 +100,9 @@
<ClCompile Include="Apps.cpp">
<Filter>Source Files\API\CmdGroups</Filter>
</ClCompile>
<ClCompile Include="AppDatabase.cpp">
<Filter>Source Files\Utilities\PS Utils</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Common.h">
@@ -156,5 +162,11 @@
<ClInclude Include="Apps.h">
<Filter>Header Files\API\CmdGroups</Filter>
</ClInclude>
<ClInclude Include="..\..\External\LibSQLite-ps4\include\sqlite3.h">
<Filter>Header Files\SQLite</Filter>
</ClInclude>
<ClInclude Include="AppDatabase.h">
<Filter>Header Files\Utilities\PS Utils</Filter>
</ClInclude>
</ItemGroup>
</Project>
+6 -2
View File
@@ -34,8 +34,8 @@ void* SocketListener::DoWork()
// Make new TCP Socket
this->Socket = sceNetSocket("Listener Socket", ORBIS_NET_AF_INET, ORBIS_NET_SOCK_STREAM, ORBIS_NET_IPPROTO_TCP);
// Set Sending and reciving time out to 1000 ms
int sock_timeout = 10000;
// Set Sending and reciving time out to 2s
int sock_timeout = 2000000;
sceNetSetsockopt(this->Socket, ORBIS_NET_SOL_SOCKET, ORBIS_NET_SO_SNDTIMEO, &sock_timeout, sizeof(sock_timeout));
sceNetSetsockopt(this->Socket, ORBIS_NET_SOL_SOCKET, ORBIS_NET_SO_RCVTIMEO, &sock_timeout, sizeof(sock_timeout));
@@ -96,6 +96,10 @@ void* SocketListener::DoWork()
int optval = 1;
sceNetSetsockopt(ClientSocket, ORBIS_NET_SOL_SOCKET, ORBIS_NET_SO_NOSIGPIPE, &optval, sizeof(optval));
int sock_timeout = 2000000;
sceNetSetsockopt(ClientSocket, ORBIS_NET_SOL_SOCKET, ORBIS_NET_SO_SNDTIMEO, &sock_timeout, sizeof(sock_timeout));
sceNetSetsockopt(ClientSocket, ORBIS_NET_SOL_SOCKET, ORBIS_NET_SO_RCVTIMEO, &sock_timeout, sizeof(sock_timeout));
// Set up thread params.
ClientThreadParams* Params = new ClientThreadParams();
Params->socketListener = this;
+11 -11
View File
@@ -1,11 +1,11 @@
#pragma once
#define ORBISLIB_MAJOR 3
#define ORBISLIB_MINOR 0
#define ORBISLIB_BUILDVERSION 577
#define stringify(a) stringify_(a)
#define stringify_(a) #a
#if defined(_DEBUG)
#define ORBISLIB_BUILDSTRING ("[OrbisLib Daemon " stringify(ORBISLIB_MAJOR) "." stringify(ORBISLIB_MINOR) "] Dev Build " stringify(ORBISLIB_BUILDVERSION) " " __DATE__ " " __TIME__)
#else
#define ORBISLIB_BUILDSTRING ("[OrbisLib Daemon " stringify(ORBISLIB_MAJOR) "." stringify(ORBISLIB_MINOR) "] Build " stringify(ORBISLIB_BUILDVERSION) " " __DATE__ " " __TIME__)
#endif
#pragma once
#define ORBISLIB_MAJOR 3
#define ORBISLIB_MINOR 0
#define ORBISLIB_BUILDVERSION 635
#define stringify(a) stringify_(a)
#define stringify_(a) #a
#if defined(_DEBUG)
#define ORBISLIB_BUILDSTRING ("[OrbisLib Daemon " stringify(ORBISLIB_MAJOR) "." stringify(ORBISLIB_MINOR) "] Dev Build " stringify(ORBISLIB_BUILDVERSION) " " __DATE__ " " __TIME__)
#else
#define ORBISLIB_BUILDSTRING ("[OrbisLib Daemon " stringify(ORBISLIB_MAJOR) "." stringify(ORBISLIB_MINOR) "] Build " stringify(ORBISLIB_BUILDVERSION) " " __DATE__ " " __TIME__)
#endif
+3 -3
View File
@@ -1,7 +1,7 @@
SETLOCAL EnableDelayedExpansion
Rem Libraries to link in
set libraries=-lc++ -lc -lSceSysModule -lkernel -lSceVideoOut -lSceSystemService -lSceSysCore -lSceSystemStateMgr -lSceNet -lScePad -lSceUserService -lSceRegMgr -lSceFreeType -lSceMsgDialog -lSceCommonDialog -lGoldHEN_Hook
set libraries=-lc++ -lc -lSceSysModule -lkernel -lSceVideoOut -lSceSystemService -lSceSysCore -lSceSystemStateMgr -lSceNet -lScePad -lSceUserService -lSceRegMgr -lSceFreeType -lSceMsgDialog -lSceCommonDialog -lGoldHEN_Hook -lSQLite
Rem Read the script arguments into local vars
set intdir=%1
@@ -13,7 +13,7 @@ set outputOelf=%intdir%%targetname%.oelf
Rem Compile object files for all the source files
for %%f in (*.cpp) do (
clang++ -cc1 -triple x86_64-scei-ps4-elf -I"%OO_PS4_TOOLCHAIN%\\include" -I"%OO_PS4_TOOLCHAIN%\\include\\c++\\v1" -I"..\\..\\External\\GoldHEN_Plugins_SDK\\include" -I"..\\..\\External\\ps4-libjbc" -DORBISLIB_DEBUG -emit-obj -o %intdir%\%%~nf.o %%~nf.cpp
clang++ -cc1 -triple x86_64-scei-ps4-elf -I"%OO_PS4_TOOLCHAIN%\\include" -I"%OO_PS4_TOOLCHAIN%\\include\\c++\\v1" -I"..\\..\\External\\GoldHEN_Plugins_SDK\\include" -I"..\\..\\External\\ps4-libjbc" -I"..\\..\\External\\LibSQLite-ps4\\include" -DORBISLIB_DEBUG -emit-obj -o %intdir%\%%~nf.o %%~nf.cpp
)
Rem Get a list of object files for linking
@@ -21,7 +21,7 @@ set obj_files=
for %%f in (%1\\*.o) do set obj_files=!obj_files! .\%%f
Rem Link the input ELF
ld.lld -m elf_x86_64 -pie --script "%OO_PS4_TOOLCHAIN%\link.x" --eh-frame-hdr -o "%outputElf%" "-L%OO_PS4_TOOLCHAIN%\\lib" "-L..\\..\\External\\GoldHEN_Plugins_SDK" %libraries% --verbose "%OO_PS4_TOOLCHAIN%\lib\crt1.o" %obj_files% "..\\..\\External\\ps4-libjbc\\jbc.o"
ld.lld -m elf_x86_64 -pie --script "%OO_PS4_TOOLCHAIN%\link.x" --eh-frame-hdr -o "%outputElf%" "-L%OO_PS4_TOOLCHAIN%\\lib" "-L..\\..\\External\\GoldHEN_Plugins_SDK" "-L..\\..\\External\\LibSQLite-ps4" %libraries% --verbose "%OO_PS4_TOOLCHAIN%\lib\crt1.o" %obj_files% "..\\..\\External\\ps4-libjbc\\jbc.o"
Rem Create the eboot
%OO_PS4_TOOLCHAIN%\bin\windows\create-fself.exe -in "%outputElf%" --out "%outputOelf%" --eboot "eboot.bin" --paid 0x3800000000010003
+1 -4
View File
@@ -2,11 +2,8 @@
#include "Version.h"
#include "API.h"
#include "GoldHEN.h"
#include <orbis/SystemService.h>
#include <orbis/SysCore.h>
#include <orbis/libkernel.h>
#include <orbis/Net.h>
#include <orbis/AppInstUtil.h>
#include "AppDatabase.h"
#include "GeneralIPC.h"
@@ -4,12 +4,17 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace OrbisSuite
{
public record AppInfo(string TitleId, string ContentId, string TitleName, string MetaDataPath, DateTime LastAccessTime,
int Visible, int SortPriority, int DisplayLocation, bool CanRemove, string Category, int ContentSize, DateTime InstallDate, string UICategory);
public class Application
{
private Target Target;
@@ -19,6 +24,95 @@ namespace OrbisSuite
this.Target = Target;
}
public List<AppInfo> GetAppList()
{
var AppList = new List<AppInfo>();
// Check to see the API is online.
if (!Target.Info.Details.IsAPIAvailable)
{
return AppList;
}
// Make the initial request.
APIResults apiResult = API.CallLong(Target.Info.IPAddress, Settings.CreateInstance().APIPort, new APIPacket() { PacketVersion = Config.PacketVersion, Command = APICommands.API_APPS_GET_LIST }, out Socket Sock);
// Make sure the request was sucessful.
if (apiResult != APIResults.API_OK)
return AppList;
// Get the number of apps installed.
int Count = Sock.RecvInt32();
// Itterate through the count to recieve all the apps details.
for (int i = 0; i < Count; i++)
{
// Recieve the bytes of the struct.
var Packet = new AppInfoPacket();
var RawPacket = new byte[Marshal.SizeOf(Packet)];
var bytes = Sock.Receive(RawPacket);
if (bytes <= 0)
break;
// Convert the recieved bytes to a struct.
Helpers.BytestoStruct(RawPacket, ref Packet);
// Try to parse the date time strings.
if (!DateTime.TryParse(Packet.LastAccessTime, out DateTime LastAccessTime))
LastAccessTime = DateTime.MinValue;
if (!DateTime.TryParse(Packet.LastAccessTime, out DateTime InstallDate))
InstallDate = DateTime.MinValue;
// For some reason there is garbage after the string so this stops that :)
var firstNullIndex = Array.FindIndex(Packet.TitleName, b => b == 0);
string titleName = Encoding.UTF8.GetString(Packet.TitleName, 0, firstNullIndex);
// Add the entry to the list.
AppList.Add(new AppInfo(Packet.TitleId, Packet.ContentId, titleName, Packet.MetaDataPath, LastAccessTime, Packet.Visible,
Packet.SortPriority, Packet.DispLocation, Packet.CanRemove == 1, Packet.Category, Packet.ContentSize, InstallDate, Packet.UICategory));
}
// close socket.
Sock.Close();
return AppList;
}
public string GetAppInfoString(string TitleId, string Key)
{
if (!Regex.IsMatch(TitleId, @"[a-zA-Z]{4}\d{5}"))
{
Console.WriteLine($"Invaild titleId format {TitleId}");
return string.Empty;
}
if (!Target.Info.Details.IsAPIAvailable)
{
return string.Empty;
}
var apiResult = API.CallLong(Target.Info.IPAddress, Settings.CreateInstance().APIPort, new APIPacket() { PacketVersion = Config.PacketVersion, Command = APICommands.API_APPS_GET_INFO_STR }, out Socket Sock);
if (apiResult != APIResults.API_OK)
return string.Empty;
// Send the titleId of the app.
Sock.Send(Encoding.ASCII.GetBytes(TitleId.PadRight(10, '\0')).Take(10).ToArray());
// Send the bytes of the key string.
Sock.Send(Encoding.ASCII.GetBytes(Key.PadRight(50, '\0')));
var resultBuffer = new byte[200];
Sock.Receive(resultBuffer);
// close socket.
Sock.Close();
return Encoding.ASCII.GetString(resultBuffer);
}
public AppState GetAppState(string TitleId)
{
if (!Regex.IsMatch(TitleId, @"[a-zA-Z]{4}\d{5}"))
@@ -32,13 +126,13 @@ namespace OrbisSuite
return AppState.STATE_NOT_RUNNING;
}
APIResults apiResult = API.CallLong(Target.Info.IPAddress, Settings.CreateInstance().APIPort, new APIPacket() { PacketVersion = Config.PacketVersion, Command = APICommands.API_APP_STATUS }, out Socket Sock);
APIResults apiResult = API.CallLong(Target.Info.IPAddress, Settings.CreateInstance().APIPort, new APIPacket() { PacketVersion = Config.PacketVersion, Command = APICommands.API_APPS_STATUS }, out Socket Sock);
if (apiResult != APIResults.API_OK)
return AppState.STATE_ERROR;
// Send the titleId of the app.
var bytes = Encoding.ASCII.GetBytes(TitleId.PadRight(9, '\0')).Take(9).ToArray();
var bytes = Encoding.ASCII.GetBytes(TitleId.PadRight(10, '\0')).Take(10).ToArray();
Sock.Send(bytes);
// Get the state from API.
@@ -63,13 +157,13 @@ namespace OrbisSuite
return false;
}
APIResults apiResult = API.CallLong(Target.Info.IPAddress, Settings.CreateInstance().APIPort, new APIPacket() { PacketVersion = Config.PacketVersion, Command = APICommands.API_APP_START }, out Socket Sock);
APIResults apiResult = API.CallLong(Target.Info.IPAddress, Settings.CreateInstance().APIPort, new APIPacket() { PacketVersion = Config.PacketVersion, Command = APICommands.API_APPS_START }, out Socket Sock);
if (apiResult != APIResults.API_OK)
return false;
// Send the titleId of the app.
var bytes = Encoding.ASCII.GetBytes(TitleId.PadRight(9, '\0')).Take(9).ToArray();
var bytes = Encoding.ASCII.GetBytes(TitleId.PadRight(10, '\0')).Take(10).ToArray();
Sock.Send(bytes);
// Get the state from API.
@@ -94,13 +188,13 @@ namespace OrbisSuite
return false;
}
APIResults apiResult = API.CallLong(Target.Info.IPAddress, Settings.CreateInstance().APIPort, new APIPacket() { PacketVersion = Config.PacketVersion, Command = APICommands.API_APP_STOP }, out Socket Sock);
APIResults apiResult = API.CallLong(Target.Info.IPAddress, Settings.CreateInstance().APIPort, new APIPacket() { PacketVersion = Config.PacketVersion, Command = APICommands.API_APPS_STOP }, out Socket Sock);
if (apiResult != APIResults.API_OK)
return false;
// Send the titleId of the app.
var bytes = Encoding.ASCII.GetBytes(TitleId.PadRight(9, '\0')).Take(9).ToArray();
var bytes = Encoding.ASCII.GetBytes(TitleId.PadRight(10, '\0')).Take(10).ToArray();
Sock.Send(bytes);
// Get the state from API.
@@ -1,4 +1,5 @@
using System.Runtime.InteropServices;
using System.Text;
namespace OrbisSuite.Common
{
@@ -17,12 +18,14 @@ namespace OrbisSuite.Common
/* ####### Apps functions ####### */
APP_START,
API_APP_STATUS,
API_APP_START,
API_APP_STOP,
API_APP_SUSPEND,
API_APP_RESUME,
API_APP_DELETE,
API_APPS_GET_LIST,
API_APPS_GET_INFO_STR,
API_APPS_STATUS,
API_APPS_START,
API_APPS_STOP,
API_APPS_SUSPEND,
API_APPS_RESUME,
API_APPS_DELETE,
APP_END,
/* ############################## */
@@ -101,7 +104,6 @@ namespace OrbisSuite.Common
API_TARGET_SET_LED,
API_TARGET_DUMP_PROC,
API_TARGET_SET_SETTINGS,
API_TARGET_GET_APPDB,
API_TARGET_GETFILE,
TARGET_END,
@@ -158,6 +160,32 @@ namespace OrbisSuite.Common
STATE_SUSPENDED,
};
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi), Serializable]
public struct AppInfoPacket
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string TitleId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string ContentId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 200)]
public byte[] TitleName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string MetaDataPath;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string LastAccessTime;
public int Visible;
public int SortPriority;
public int DispLocation;
public char CanRemove;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string Category;
public int ContentSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string InstallDate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string UICategory;
};
#endregion
#region Debug
@@ -1,113 +0,0 @@
using SQLite;
using System.Data;
namespace OrbisSuite.Common.Database.App
{
public class Master
{
public string type { get; set; }
public string name { get; set; }
public string tbl_name { get; set; }
public int rootpage { get; set; }
public string sql { get; set; }
}
[Table("tbl_appbrowse")]
public class AppBrowse
{
[PrimaryKey, NotNull]
public string titleId { get; set; } = "";
public string contentId { get; set; } = "";
public string titleName { get; set; } = "";
public string metaDataPath { get; set; } = "";
public int visible { get; set; } = 0;
public int sortPriority { get; set; } = 0;
public int dispLocation { get; set; } = 0;
public int canRemove { get; set; } = 0;
public string category { get; set; } = "";
public int contentSize { get; set; } = 0;
public string installDate { get; set; } = "";
public string uiCategory { get; set; } = "";
public static bool AppBrowseRemoveUserId(string dbPath, int UserId)
{
var db = new SQLiteConnection(dbPath);
// List all of the tables in this data base.
var dbList = db.Query<Master>("SELECT name FROM sqlite_master WHERE type = 'table'");
// Make sure we got some tables back.
if (dbList.Count <= 0)
{
return false;
}
// The name of the table with the user id appended to the end.
var tableName = $"tbl_appbrowse_{UserId.ToString().PadLeft(10, '0')}";
// Search for the table we want to rename to not have the userId on the end.
var appBrowseTable = dbList.Find(e => e.name.Equals(tableName))?.name;
// Make sure we found a table matching our pattern.
if (appBrowseTable != null)
{
// Make sure we haven't already renamed this table.
if (appBrowseTable.Equals("tbl_appbrowse"))
{
return true;
}
// Rename it so we can use it.
var result = db.Execute($"ALTER TABLE `{appBrowseTable}` RENAME TO `tbl_appbrowse`") > 0;
db.Close();
return result;
}
else
{
return false;
}
}
public static bool AppBrowseAddUserId(string dbPath, int UserId)
{
var db = new SQLiteConnection(dbPath);
// The name of the table with the user id appended to the end.
var tableName = $"tbl_appbrowse_{UserId.ToString().PadLeft(10, '0')}";
// Rename it so we can use it.
var result = db.Execute($"ALTER TABLE `tbl_appbrowse` RENAME TO `{tableName}`") > 0;
db.Close();
return result;
}
public static List<AppBrowse> GetAppBrowseList(string dbPath)
{
var db = new SQLiteConnection(dbPath);
var result = db.Table<AppBrowse>().ToList();
db.Close();
return result;
}
/*public static bool Save(string dbPath)
{
}*/
}
}
@@ -1,31 +0,0 @@
using SQLite;
using System;
using System.Linq.Expressions;
namespace OrbisSuite.Common.Database.App
{
[Table("tbl_version")]
public class AppBrowseVersion
{
[PrimaryKey, NotNull]
public string category { get; set; } = "";
public int status { get; set; }
public static int GetAppBrowseVersion(string DataBasePath)
{
var db = new SQLiteConnection(DataBasePath);
var result = db.Find((Expression<Func<AppBrowseVersion, bool>>)(x => x.category.Equals("sync_server")));
db.Close();
if(result != null)
{
return result.status;
}
else
{
return 0;
}
}
}
}
@@ -1,33 +0,0 @@
using SQLite;
using System.Linq.Expressions;
namespace OrbisSuite.Common.Database.App
{
[Table("tbl_appinfo")]
public class AppInfo
{
[PrimaryKey, NotNull]
public string titleId { get; set; } = "";
[PrimaryKey, NotNull]
public string key { get; set; } = "";
public string val { get; set; }
public static string GetStringFromAppInfo(string DataBasePath, string TitleId, string Key)
{
var db = new SQLiteConnection(DataBasePath);
var result = db.Find((Expression<Func<AppInfo, bool>>)(x => x.titleId.Equals(TitleId) && x.key.Equals(Key)));
db.Close();
if (result != null)
{
return result.val;
}
else
{
return "";
}
}
}
}
@@ -1,6 +1,7 @@
using System.IO;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
@@ -1,24 +1,12 @@
using OrbisSuite;
using OrbisSuite.Common;
using OrbisSuite.Common.Database.App;
using SimpleUI.Controls;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using static System.Net.Mime.MediaTypeNames;
namespace OrbisNeighborHood.Controls
{
@@ -27,11 +15,11 @@ namespace OrbisNeighborHood.Controls
/// </summary>
public partial class AppPanel : UserControl
{
public AppBrowse App;
public AppInfo App;
private AppState AppState = AppState.STATE_NOT_RUNNING;
public AppPanel(AppBrowse App, string AppVersion)
public AppPanel(AppInfo App, string AppVersion)
{
InitializeComponent();
@@ -44,17 +32,17 @@ namespace OrbisNeighborHood.Controls
Task.Run(() => UpdateApp());
}
public void Update(AppBrowse App, string AppVersion)
public void Update(AppInfo App, string AppVersion)
{
// Set the Info about this application.
ApplicationNameElement.Text = App.titleName;
TitleIdElement.FieldText = App.titleId;
ApplicationNameElement.Text = App.TitleName;
TitleIdElement.FieldText = App.TitleId;
VersionElement.FieldText = AppVersion.TrimStart('0');
TypeElement.FieldText = $"{App.uiCategory} ({App.category})";
SizeElement.FieldText = App.contentSize <= 0 ? "N/A" : Utilities.BytesToString(App.contentSize);
TypeElement.FieldText = $"{App.UICategory} ({App.Category})";
SizeElement.FieldText = App.ContentSize <= 0 ? "N/A" : Utilities.BytesToString(App.ContentSize);
// Get the path to our icon and make sure it exists.
string iconPath = $@"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}\Orbis Suite\AppCache\{OrbisLib.Instance.SelectedTarget.Info.Details.ForegroundAccountId.ToString("X")}\{App.titleId}\icon0.png";
string iconPath = $@"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}\Orbis Suite\AppCache\{App.TitleId}\icon0.png";
if(!string.IsNullOrEmpty(iconPath) && File.Exists(iconPath) && new FileInfo(iconPath).Length > 0)
{
// Load and cache image in memory.
@@ -68,17 +56,14 @@ namespace OrbisNeighborHood.Controls
IconImage.Source = image;
}
if (DateTime.TryParse(App.installDate, out var dateTime))
{
InstallDateElement.FieldText = dateTime.ToString("ddd dd MMM yyy hh:mm tt");
}
InstallDateElement.FieldText = App.InstallDate.ToString("ddd dd MMM yyy hh:mm tt");
// Set the tool tips.
StartStop.ToolTip = $"Start {App.titleName}.";
Visibility.ToolTip = $"Hide {App.titleName} from Home Menu.";
ChangeIcon.ToolTip = $"Change the icon of {App.titleName}.";
StartStop.ToolTip = $"Start {App.TitleName}.";
Visibility.ToolTip = $"Hide {App.TitleName} from Home Menu.";
ChangeIcon.ToolTip = $"Change the icon of {App.TitleName}.";
// 3 more TBD here.
Delete.ToolTip = $"Delete {App.titleName}.";
Delete.ToolTip = $"Delete {App.TitleName}.";
}
private void UpdateApp()
@@ -88,30 +73,30 @@ namespace OrbisNeighborHood.Controls
var currentTarget = OrbisLib.Instance.SelectedTarget;
// Get Current App status.
var newAppState = currentTarget.Application.GetAppState(App.titleId);
var newAppState = currentTarget.Application.GetAppState(App.TitleId);
if (newAppState >= 0)
AppState = newAppState;
// App status.
if (AppState == AppState.STATE_RUNNING || AppState == AppState.STATE_SUSPENDED)
{
Dispatcher.Invoke(() => StartStop.ToolTip = $"Stop {App.titleName}.");
Dispatcher.Invoke(() => StartStop.ToolTip = $"Stop {App.TitleName}.");
Dispatcher.Invoke(() => StartStop.ImageSource = "/OrbisNeighborHood;component/Images/Stop.png");
}
else
{
Dispatcher.Invoke(() => StartStop.ToolTip = $"Start {App.titleName}.");
Dispatcher.Invoke(() => StartStop.ToolTip = $"Start {App.TitleName}.");
Dispatcher.Invoke(() => StartStop.ImageSource = "/OrbisNeighborHood;component/Images/Start.png");
}
// App Visibility.
if (App.visible == 0 || App.visible == 2)
if (App.Visible == 0 || App.Visible == 2)
{
Dispatcher.Invoke(() => Visibility.ToolTip = $"Show {App.titleName} from Home Menu.");
Dispatcher.Invoke(() => Visibility.ToolTip = $"Show {App.TitleName} from Home Menu.");
}
else
{
Dispatcher.Invoke(() => Visibility.ToolTip = $"Hide {App.titleName} from Home Menu.");
Dispatcher.Invoke(() => Visibility.ToolTip = $"Hide {App.TitleName} from Home Menu.");
}
Thread.Sleep(1000);
@@ -127,11 +112,11 @@ namespace OrbisNeighborHood.Controls
var currentTarget = OrbisLib.Instance.SelectedTarget;
if (AppState == AppState.STATE_RUNNING || AppState == AppState.STATE_SUSPENDED)
{
currentTarget.Application.Stop(App.titleId);
currentTarget.Application.Stop(App.TitleId);
}
else
{
currentTarget.Application.Start(App.titleId);
currentTarget.Application.Start(App.TitleId);
}
});
}
@@ -6,7 +6,6 @@ using System.Windows;
using System.Windows.Controls;
using System.IO;
using System.Threading.Tasks;
using OrbisSuite.Common.Database.App;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
@@ -18,10 +17,6 @@ namespace OrbisNeighborHood.MVVM.View
/// </summary>
public partial class AppListView : UserControl
{
private static string _AppDatabaseRemotePath = @"/system_data/priv/mms/app.db";
private static Mutex appDataBaseMtx = new Mutex();
private static readonly List<string> TitleIdExlusionList = new List<string>()
{
"NPXS20108",
@@ -38,6 +33,10 @@ namespace OrbisNeighborHood.MVVM.View
"NPXS20106",
"NPXS20114",
"NPXS20120",
"NPXS20109",
"NPXS20112",
"NPXS20133",
"NPXS20132",
// Destiny? lol
"CUSA00219",
@@ -55,8 +54,14 @@ namespace OrbisNeighborHood.MVVM.View
// Refresh the info about the current target.
RefreshTargetInfo();
// Create task to periodically check for app.db changes.
Task.Run(() => CheckAppDatabase());
Task.Run(() =>
{
// Initially populate the app list.
InitAppList();
// Create task to periodically check for app.db changes.
// Task.Run(() => CheckAppDatabase());
});
}
#region Properties
@@ -103,32 +108,32 @@ namespace OrbisNeighborHood.MVVM.View
#endregion
public void AddApp(string UserDirectoryPath, string AppVersion, AppBrowse App)
public void AddApp(string appCachePath, AppInfo App)
{
var currentTarget = OrbisLib.Instance.SelectedTarget;
// Make sure the titleId format is correct. Helps weed out bad entries and folders.
if (!Regex.IsMatch(App.titleId, @"[a-zA-Z]{4}\d{5}"))
if (!Regex.IsMatch(App.TitleId, @"[a-zA-Z]{4}\d{5}"))
return;
// Skip the Destiny entries that just exist for some reason even after a restore?... lol
if ((App.titleId.Equals("CUSA00219") || App.titleId.Equals("CUSA00568") || App.titleId.Equals("CUSA01000")) && App.contentSize <= 0)
if ((App.TitleId.Equals("CUSA00219") || App.TitleId.Equals("CUSA00568") || App.TitleId.Equals("CUSA01000")) && App.ContentSize <= 0)
return;
// Skip some that aren't technically an app.
if (TitleIdExlusionList.Contains(App.titleId))
if (TitleIdExlusionList.Contains(App.TitleId))
return;
// Weed out some more bad entries created by default.
if (App.titleName.Length <= 2)
if (App.TitleName.Length <= 2)
return;
// Make sure only add ones with a category.
if (App.uiCategory.Length <= 0 || App.category.Length <= 0)
if (App.UICategory.Length <= 0 || App.Category.Length <= 0)
return;
// Directory to cache stuff for app.
string currentAppPath = Path.Combine(UserDirectoryPath, App.titleId);
string currentAppPath = Path.Combine(appCachePath, App.TitleId);
// Create Directory for current app.
if (!Directory.Exists(currentAppPath))
@@ -137,27 +142,30 @@ namespace OrbisNeighborHood.MVVM.View
}
// Cache icon0.png for app if we have not already.
if (!File.Exists(Path.Combine(currentAppPath, "icon0.png")) && !string.IsNullOrEmpty(App.metaDataPath) && currentTarget.Info.Details.IsAvailable) //TODO: Maybe add a isFTPAvailable.
if (!File.Exists(Path.Combine(currentAppPath, "icon0.png")) && !string.IsNullOrEmpty(App.MetaDataPath) && currentTarget.Info.Details.IsAvailable) //TODO: Maybe add a isFTPAvailable.
{
currentTarget.FTP.DownloadFile($"{App.metaDataPath}/icon0.png", Path.Combine(currentAppPath, "icon0.png"));
currentTarget.FTP.DownloadFile($"{App.MetaDataPath}/icon0.png", Path.Combine(currentAppPath, "icon0.png"));
}
// Fetch the App version.
var appVersion = currentTarget.Application.GetAppInfoString(App.TitleId, "APP_VER");
// Add or update app list item.
Dispatcher.Invoke(() =>
{
var panel = AppList.Items.Cast<AppPanel>().ToList().Find(x => x.App.titleId == App.titleId);
var panel = AppList.Items.Cast<AppPanel>().ToList().Find(x => x.App.TitleId == App.TitleId);
if (panel != null)
{
panel.Update(App, AppVersion);
panel.Update(App, appVersion);
}
else
{
AppList.Items.Add(new AppPanel(App, AppVersion));
AppList.Items.Add(new AppPanel(App, appVersion));
}
});
}
private async Task InitAppList()
private void InitAppList()
{
// Clear lists so we can re-populate them.
Dispatcher.Invoke(() => AppList.Items.Clear());
@@ -181,48 +189,19 @@ namespace OrbisNeighborHood.MVVM.View
Directory.CreateDirectory(appCachePath);
}
// Path where we will store this data.
var ForegroundAccountId = currentTarget.Info.Details.ForegroundAccountId;
string userDirectoryPath = Path.Combine(appCachePath, ForegroundAccountId.ToString("X"));
// Create Directory for database.
if (!Directory.Exists(userDirectoryPath))
{
Directory.CreateDirectory(userDirectoryPath);
}
// Check to see if we have a database.
string appDataBasePath = Path.Combine(userDirectoryPath, "app.db");
if (!File.Exists(appDataBasePath))
{
Console.WriteLine("No app.db is cached.");
return;
}
appDataBaseMtx.WaitOne();
// Rename the table we need in the app.db.
AppBrowse.AppBrowseRemoveUserId(appDataBasePath, ForegroundAccountId);
// Get all apps from the app.db
var appList = AppBrowse.GetAppBrowseList(appDataBasePath);
var appList = currentTarget.Application.GetAppList();
foreach (var app in appList)
{
Parallel.Invoke(() =>
{
AddApp(userDirectoryPath, AppInfo.GetStringFromAppInfo(appDataBasePath, app.titleId, "APP_VER"), app);
AddApp(appCachePath, app);
});
}
appDataBaseMtx.ReleaseMutex();
}
private async Task CheckAppDatabase()
{
// Update the current App list. Called here because it must be initialized before the update is called.
await InitAppList();
while (true)
{
try
@@ -238,75 +217,30 @@ namespace OrbisNeighborHood.MVVM.View
continue;
}
// Path where we will store this data.
var ForegroundAccountId = currentTarget.Info.Details.ForegroundAccountId;
string userDirectoryPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), @"Orbis Suite\AppCache\", ForegroundAccountId.ToString("X"));
// Appcache location.
string appCachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), @"Orbis Suite\AppCache\");
// Download database.
string appDataBasePath = Path.Combine(userDirectoryPath, "app.db");
if (File.Exists(appDataBasePath))
// Get the current app list.
var appList = currentTarget.Application.GetAppList();
// Check for deletions.
foreach (var app in AppList.Items.Cast<AppPanel>().ToList())
{
// Download the new app.db.
string newAppDataBasePath = Path.Combine(userDirectoryPath, "new_app.db");
if (currentTarget.FTP.DownloadFile(_AppDatabaseRemotePath, newAppDataBasePath))
if (appList.Find(x => x.TitleId == app.App.TitleId) == null)
{
// Get app.db versions.
var newVersion = AppBrowseVersion.GetAppBrowseVersion(newAppDataBasePath);
var oldVersion = AppBrowseVersion.GetAppBrowseVersion(appDataBasePath);
// Compare the app.db versions.
if (newVersion > oldVersion)
{
appDataBaseMtx.WaitOne();
// Remove the out dated database.
File.Delete(appDataBasePath);
// Rename the new database.
File.Move(newAppDataBasePath, appDataBasePath);
// Rename the table we need in the app.db.
AppBrowse.AppBrowseRemoveUserId(appDataBasePath, ForegroundAccountId);
// Get all apps from the app.db
var appList = AppBrowse.GetAppBrowseList(appDataBasePath);
// Check for deletions.
foreach (var app in AppList.Items.Cast<AppPanel>().ToList())
{
if (appList.Find(x => x.titleId == app.App.titleId) == null)
{
Dispatcher.Invoke(() => AppList.Items.Remove(app));
}
}
// Check for new apps / updates.
foreach (var app in appList)
{
Parallel.Invoke(() =>
{
AddApp(userDirectoryPath, AppInfo.GetStringFromAppInfo(appDataBasePath, app.titleId, "APP_VER"), app);
});
}
appDataBaseMtx.ReleaseMutex();
}
else
{
// Remove new database as ours is current.
File.Delete(newAppDataBasePath);
}
Dispatcher.Invoke(() => AppList.Items.Remove(app));
}
}
else if (currentTarget.Info.Details.IsAvailable)
// Check for new apps / updates.
foreach (var app in appList)
{
// Download the app database for the first time.
if (OrbisLib.Instance.SelectedTarget.FTP.DownloadFile(_AppDatabaseRemotePath, appDataBasePath))
Parallel.Invoke(() =>
{
// Initialize the Application List now that we have an app.db.
InitAppList();
}
AddApp(appCachePath, app);
});
}
await Task.Delay(1000);
}
catch (Exception ex)
@@ -1 +1 @@
1856
1956
@@ -1 +1 @@
Version 3.0.1856 Debug Build Saturday December 17 2022 6:19 PM
Version 3.0.1956 Debug Build Sunday December 18 2022 7:40 PM