diff --git a/lang.h b/lang.h
index af4f771..afb5892 100644
--- a/lang.h
+++ b/lang.h
@@ -132,7 +132,9 @@ typedef enum {
LS_EMUS = 120,
LS_UPDATE_EMUS = 121,
LS_EMUS_PATH = 122,
- LS_COUNT = 123
+ LS_GAMES = 123,
+ LS_IMAGES = 124,
+ LS_COUNT = 125
} LangStrId;
static int ui_lang = LANG_EN;
@@ -262,6 +264,8 @@ static const char *lang_table[LANG_COUNT][LS_COUNT] = {
"Emulatoren",
"Emulatoren aus Ordner aktualisieren",
"Emulator-Ordner",
+ "Spiele",
+ "Images",
},
/* EN */ {
"running",
@@ -386,6 +390,8 @@ static const char *lang_table[LANG_COUNT][LS_COUNT] = {
"Emulators",
"Update emulators from folder",
"Emulators path",
+ "Games",
+ "Images",
},
/* FR */ {
"actif",
@@ -511,6 +517,8 @@ static const char *lang_table[LANG_COUNT][LS_COUNT] = {
"\u00c9mulateurs",
"Mettre \u00e0 jour les \u00e9mulateurs depuis le dossier",
"Dossier des \u00e9mulateurs",
+ "Jeux",
+ "Images",
},
/* ES */ {
"activo",
@@ -636,6 +644,8 @@ static const char *lang_table[LANG_COUNT][LS_COUNT] = {
"Emuladores",
"Actualizar emuladores desde la carpeta",
"Ruta de emuladores",
+ "Juegos",
+ "Im\u00e1genes",
}
};
diff --git a/main.c b/main.c
index 3ef5850..ea2441e 100644
--- a/main.c
+++ b/main.c
@@ -886,6 +886,33 @@ static int send_elf_to_elfldr(const char *path) {
return err ? -3 : 0;
}
+/* Proxy POST to SM API on port 10101; caller must free() returned string */
+static char *sm_api_post(const char *path, const char *body) {
+ int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) return NULL;
+ struct timeval tv={3,0};
+ setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));
+ setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv));
+ struct sockaddr_in addr; memset(&addr,0,sizeof(addr));
+ addr.sin_family=AF_INET; addr.sin_port=htons(10101);
+ addr.sin_addr.s_addr=inet_addr("127.0.0.1");
+ if(connect(sock,(struct sockaddr*)&addr,sizeof(addr))!=0){close(sock);return NULL;}
+ char req[512];
+ snprintf(req,sizeof(req),"POST %s HTTP/1.0\r\nHost: 127.0.0.1\r\n"
+ "Content-Type: application/json\r\nContent-Length: %zu\r\n\r\n%s",
+ path,strlen(body),body);
+ send(sock,req,strlen(req),0);
+ char *buf=malloc(131072); if(!buf){close(sock);return NULL;}
+ size_t total=0; ssize_t n;
+ while((n=recv(sock,buf+total,131071-total,0))>0) total+=n;
+ buf[total]=0; close(sock);
+ char *bs=strstr(buf,"\r\n\r\n");
+ if(!bs){free(buf);return NULL;} bs+=4;
+ size_t bl=total-(size_t)(bs-buf);
+ char *result=malloc(bl+1); if(!result){free(buf);return NULL;}
+ memcpy(result,bs,bl); result[bl]=0;
+ free(buf); return result;
+}
+
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if(ev!=MG_EV_HTTP_MSG) return;
struct mg_http_message *hm=(struct mg_http_message*)ev_data;
@@ -1107,6 +1134,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
H("
");
H("",L(LS_IMG_OVERRIDES));
H("",L(LS_MANUAL));
+ H("",L(LS_GAMES));
+ H("",L(LS_IMAGES));
H("",L(LS_NOTIFS));
H("");
/* raw panel accessible via button in backup section, no sidebar tab */
@@ -1591,6 +1620,28 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
H("",L(LS_ADD_ENTRY));
H("");
+ /* Panel: Spiele */
+ H("");
+ H("
%s
",L(LS_GAMES));
+ if(!has_api) H("
ab 1.7alpha3
");
+ H("
"
+ "
");
+ H("
",L(LS_LOADING));
+ H("
");
+
+ /* Panel: Images */
+ H("");
+ H("
%s
",L(LS_IMAGES));
+ if(!has_api) H("
ab 1.7alpha3
");
+ H("
"
+ "
");
+ H("
",L(LS_LOADING));
+ H("
");
+
/* Panel: Debug Log */
H("");
H("
Log
");
@@ -1697,6 +1748,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
"sessionStorage.setItem('panel',el.getAttribute('data-p'));"
"if(el.getAttribute('data-p')==='log')refreshLog();"
"if(el.getAttribute('data-p')==='raw'){rawLoad();}"
+ "if(el.getAttribute('data-p')==='spl')loadGames();"
+ "if(el.getAttribute('data-p')==='img')loadImages();"
"var ct=document.querySelector('.content');if(ct)ct.scrollTop=0;}" /* restore panel after USB-triggered reload */
"var _rp=sessionStorage.getItem('panel');"
"if(_rp){var _rb=document.querySelector('.nav-item[data-p=\"'+_rp+'\"]');if(_rb)showP(_rb);}"
@@ -1751,7 +1804,56 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
"if(l){l.textContent=nm;l.title=path;l.style.display='inline';}if(b)b.style.display='inline';}"
"setTimeout(refreshStatus,2000);})"
".catch(function(){if(btn)btn.disabled=false;});}""function showElfPicker(btn,paths,cb){var old=document.getElementById('sm-elf-picker');if(old)old.remove();var picker=document.createElement('div');picker.id='sm-elf-picker';picker.style.cssText='position:fixed;top:0;left:0;width:100%%;height:100%%;background:rgba(0,0,0,.75);display:flex;align-items:center;justify-content:center;z-index:9999;';var box=document.createElement('div');box.style.cssText='background:#0d111a;border:1px solid #1e2a42;border-radius:14px;padding:36px;min-width:500px;max-width:800px;';var title=document.createElement('p');title.style.cssText='margin:0 0 20px;font-size:.7rem;text-transform:uppercase;letter-spacing:.12em;color:#64748b;font-family:monospace;';title.textContent=_t.sel_sm;box.appendChild(title);paths.forEach(function(p){var b=document.createElement('button');b.style.cssText='display:block;width:100%%;margin-bottom:10px;padding:16px 18px;border-radius:8px;border:1px solid #1e2a42;background:#131927;color:#e2e8f0;cursor:pointer;text-align:left;font-family:monospace;font-size:.75rem;word-break:break-all;';b.textContent=p;b.onclick=function(){picker.remove();if(cb)cb(p);else launchElf(btn,p);};box.appendChild(b);});var cancel=document.createElement('button');cancel.style.cssText='display:block;width:100%%;padding:14px;border-radius:8px;border:1px solid #1e2a42;background:transparent;color:#64748b;cursor:pointer;font-family:monospace;font-size:.75rem;margin-top:6px;';cancel.textContent=_t.cancel;cancel.onclick=function(){picker.remove();};box.appendChild(cancel);picker.appendChild(box);document.body.appendChild(picker);}""document.querySelector('form').addEventListener('keydown',function(e){""if(e.key==='Enter'&&e.target.tagName!=='BUTTON')e.preventDefault();""});"
-"function addPath(){"
+"function loadGames(){"
+ "var el=document.getElementById('games-list');if(!el)return;"
+ "el.innerHTML='
Lade...
';"
+ "fetch('/api/sm/games',{method:'POST'}).then(function(r){return r.json();})"
+ ".then(function(d){"
+ "if(!d.games||!d.games.length){el.innerHTML='
Keine Spiele gefunden
';return;}"
+ "var h='
';"
+ "h+='';"
+ "h+='| Titel | ';"
+ "h+='ID | ';"
+ "h+='Mount | ';"
+ "h+=' |
';"
+ "d.games.forEach(function(g){"
+ "var mn=g.mounted?'●':'○';"
+ "var ba=g.mounted"
+ "?''"
+ ":'';"
+ "h+='';"
+ "h+='| '+g.title_name+' | ';"
+ "h+=''+g.title_id+' | ';"
+ "h+=''+mn+' | ';"
+ "h+=''+ba+' |
';"
+ "});h+='
';el.innerHTML=h;})"
+ ".catch(function(){el.innerHTML='
SM API nicht erreichbar
';});}"
+ "function mountGame(tid){fetch('/api/sm/games/mount?title_id='+encodeURIComponent(tid),{method:'POST'})"
+ ".then(function(){setTimeout(loadGames,1500);}).catch(function(){alert(_t.err);});}"
+ "function unmountGame(tid){fetch('/api/sm/games/unmount?title_id='+encodeURIComponent(tid),{method:'POST'})"
+ ".then(function(){setTimeout(loadGames,1500);}).catch(function(){alert(_t.err);});}"
+ "function loadImages(){"
+ "var el=document.getElementById('images-list');if(!el)return;"
+ "el.innerHTML='
Lade...
';"
+ "fetch('/api/sm/images',{method:'POST'}).then(function(r){return r.json();})"
+ ".then(function(d){"
+ "if(!d.images||!d.images.length){el.innerHTML='
Keine Images gefunden
';return;}"
+ "var h='
';"
+ "h+='';"
+ "h+='| Datei | ';"
+ "h+='Gr\\u00f6\\u00dfe | ';"
+ "h+='Mount |
';"
+ "d.images.forEach(function(img){"
+ "var fn=img.path?img.path.split('/').pop():'';"
+ "var sz=img.size?(img.size>1073741824?(img.size/1073741824).toFixed(1)+' GB':(img.size/1048576).toFixed(0)+' MB'):'?';"
+ "var st=img.mounted?'●':'○';"
+ "h+='';"
+ "h+='| '+fn+' | ';"
+ "h+=''+sz+' | ';"
+ "h+=''+st+' |
';"
+ "});h+='
';el.innerHTML=h;})"
+ ".catch(function(){el.innerHTML='
SM API nicht erreichbar
';});}"
+ "function addPath(){"
"var c=document.getElementById('paths-list');"
"var d=document.createElement('div');d.className='path-row';"
"d.innerHTML='