diff --git a/main.c b/main.c
index 5f6ccbf..7017759 100644
--- a/main.c
+++ b/main.c
@@ -1108,7 +1108,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
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 */
@@ -1593,25 +1592,16 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
H("",L(LS_ADD_ENTRY));
H("");
- /* Panel: Spiele */
+ /* Panel: Spiele & Images (kombiniert) */
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("
%s
",L(LS_IMAGES));
H("
",L(LS_LOADING));
H("
");
@@ -1721,8 +1711,7 @@ 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();"
+ "if(el.getAttribute('data-p')==='spl')loadGamePanel();"
"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);}"
@@ -1777,32 +1766,65 @@ 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();""});");
- H("function loadGames(){"
- "var el=document.getElementById('games-list');if(!el)return;"
- "var old=el.innerHTML;"
- "el.innerHTML='Lade...
';"
- "fetch('/api/sm/games',{method:'POST'}).then(function(r){return r.json();})"
- ".then(function(d){"
- "if(d.error||d.status){el.innerHTML='SM API: '+(d.error||'Fehler')+'
';return;}"
- "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?'● aktiv':'○';"
+ H("function loadGamePanel(){"
+ "var gl=document.getElementById('games-list');"
+ "var il=document.getElementById('images-list');"
+ "if(gl)gl.innerHTML='Lade...
';"
+ "if(il)il.innerHTML='';"
+ "var gp=fetch('/api/sm/games',{method:'POST'}).then(function(r){return r.json();}).catch(function(){return null;});"
+ "var ip=fetch('/api/sm/images',{method:'POST'}).then(function(r){return r.json();}).catch(function(){return null;});"
+ "Promise.all([gp,ip]).then(function(res){"
+ "var gd=res[0],id=res[1];"
+ "if(!gl)return;"
+ "if(!gd||gd.error||gd.status){"
+ "gl.innerHTML='SM API: '+(gd&&gd.error?gd.error:'nicht erreichbar')+'
';}"
+ "else if(!gd.games||!gd.games.length){"
+ "gl.innerHTML='Keine Spiele gefunden
';}"
+ "else{"
+ "var h='';"
+ "gd.games.forEach(function(g){"
+ "var tid=g.title_id||'';"
+ "var mn=g.mounted"
+ "?'● aktiv'"
+ ":'○ inaktiv';"
"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=old;});}"
+ "?''"
+ ":'';"
+ "var ip2=g.image_path?''+g.image_path+'
':'';"
+ "h+='';"
+ "h+='

';"
+ "h+='
';"
+ "h+='
'+(g.title_name||tid)+'
';"
+ "h+='
'+tid+'
';"
+ "h+=ip2+'
';"
+ "h+='
'+mn+ba+'
';"
+ "});gl.innerHTML=h;}"
+ "if(!il)return;"
+ "if(!id||id.error||id.status){"
+ "il.innerHTML=''+(id&&id.error?id.error:'nicht erreichbar')+'
';return;}"
+ "if(!id.images||!id.images.length){il.innerHTML='Keine Image-Dateien gefunden
';return;}"
+ "var ih='';"
+ "ih+='';"
+ "ih+='| Datei / Pfad | ';"
+ "ih+='Typ | Gr\\u00f6\\u00dfe | ';"
+ "ih+='Mount |
';"
+ "id.images.forEach(function(img){"
+ "var parts=(img.path||'').split('/');"
+ "var fn=parts.pop()||img.path;"
+ "var dir=parts.join('/')||'/';"
+ "var ext=fn.indexOf('.')>-1?fn.split('.').pop().toUpperCase():'';"
+ "var sz=img.size?(img.size>1073741824?(img.size/1073741824).toFixed(2)+' GB':(img.size/1048576).toFixed(0)+' MB'):'?';"
+ "var st=img.mounted?'● aktiv':'○';"
+ "ih+='';"
+ "ih+=''+fn+'';"
+ "ih+=' '+dir+' | ';"
+ "ih+=''+ext+' | ';"
+ "ih+=''+sz+' | ';"
+ "ih+=''+st+' |
';"
+ "});ih+='
';il.innerHTML=ih;});}"
+ "function loadGames(){loadGamePanel();}"
"function mountGame(tid){"
"var el=document.getElementById('games-list');"
"if(el)el.innerHTML='Mounting '+tid+'...
';"
@@ -1810,8 +1832,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
".then(function(r){return r.json();})"
".then(function(d){"
"if(d.error&&el)el.innerHTML=''+d.error+'
';"
- "setTimeout(loadGames,3000);})"
- ".catch(function(){setTimeout(loadGames,3000);});}"
+ "setTimeout(loadGamePanel,3000);})"
+ ".catch(function(){setTimeout(loadGamePanel,3000);});}"
"function unmountGame(tid){"
"var el=document.getElementById('games-list');"
"if(el)el.innerHTML='Unmounting '+tid+'...
';"
@@ -1819,37 +1841,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
".then(function(r){return r.json();})"
".then(function(d){"
"if(d.error&&el)el.innerHTML=''+d.error+'
';"
- "setTimeout(loadGames,3000);})"
- ".catch(function(){setTimeout(loadGames,3000);});}"
- "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.error||d.status){el.innerHTML='SM API: '+(d.error||'Fehler')+'
';return;}"
- "if(!d.images||!d.images.length){el.innerHTML='Keine Images gefunden
';return;}"
- "var h='';"
- "h+='';"
- "h+='| Datei / Pfad | ';"
- "h+='Typ | ';"
- "h+='Gr\\u00f6\\u00dfe | ';"
- "h+='Mount |
';"
- "d.images.forEach(function(img){"
- "var parts=(img.path||'').split('/');"
- "var fn=parts.pop()||img.path;"
- "var dir=parts.join('/')||'/';"
- "var ext=fn.indexOf('.')>-1?fn.split('.').pop().toUpperCase():'';"
- "var sz=img.size?(img.size>1073741824?(img.size/1073741824).toFixed(2)+' GB':(img.size/1048576).toFixed(0)+' MB'):'?';"
- "var st=img.mounted?'● aktiv':'○';"
- "h+='';"
- "h+='';"
- "h+=''+fn+'';"
- "h+=' '+dir+' | ';"
- "h+=''+ext+' | ';"
- "h+=''+sz+' | ';"
- "h+=''+st+' |
';"
- "});h+='
';el.innerHTML=h;})"
- ".catch(function(){el.innerHTML='SM API nicht erreichbar
';});}");
+ "setTimeout(loadGamePanel,3000);})"
+ ".catch(function(){setTimeout(loadGamePanel,3000);});}");
H("function addPath(){"
"var c=document.getElementById('paths-list');"
"var d=document.createElement('div');d.className='path-row';"
@@ -2863,6 +2856,22 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
write_prefs(&p);
mg_http_reply(c,200,"Content-Type: application/json\r\n","{\"ok\":true}");
}
+ else if(mg_match(hm->uri,mg_str("/api/game/icon"),NULL)){
+ char tid[64]={0}; mg_http_get_var(&hm->query,"title_id",tid,sizeof(tid));
+ /* sanitise: only allow alphanumeric title IDs */
+ for(int i=0;tid[i];i++) if(!isalnum((unsigned char)tid[i])){tid[0]=0;break;}
+ if(!tid[0]){mg_http_reply(c,400,"","");return;}
+ char p[128];
+ snprintf(p,sizeof(p),"/system_data/priv/appmeta/%s/icon0.png",tid);
+ FILE *f=fopen(p,"rb");
+ if(!f){mg_http_reply(c,404,"","");return;}
+ fseek(f,0,SEEK_END); long sz=ftell(f); fseek(f,0,SEEK_SET);
+ if(sz<=0||sz>524288){fclose(f);mg_http_reply(c,404,"","");return;}
+ char *buf=malloc(sz); if(!buf){fclose(f);mg_http_reply(c,500,"","");return;}
+ fread(buf,1,sz,f); fclose(f);
+ mg_http_reply(c,200,"Content-Type: image/png\r\nCache-Control: max-age=3600\r\n","%.*s",(int)sz,buf);
+ free(buf);
+ }
else if(mg_match(hm->uri,mg_str("/api/sm/games"),NULL)||
mg_match(hm->uri,mg_str("/api/sm/images"),NULL)){
const char *smpath=mg_match(hm->uri,mg_str("/api/sm/games"),NULL)?"/api/v1/games":"/api/v1/images";