2017-09-26 08:59:34 +02:00
|
|
|
// MIT License
|
|
|
|
|
|
|
|
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
|
|
|
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
|
|
// in the Software without restriction, including without limitation the rights
|
|
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
|
|
// copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
// SOFTWARE.
|
|
|
|
|
|
|
|
#include "surf.h"
|
|
|
|
#include "fs.h"
|
|
|
|
#include "net.h"
|
|
|
|
#include "console.h"
|
|
|
|
|
|
|
|
#include "ext/gif.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#define MAIN_OFFSET 4
|
|
|
|
#define MENU_HEIGHT 10
|
|
|
|
#define MAX_CARTS 256
|
|
|
|
#define ANIM 10
|
|
|
|
#define COVER_WIDTH 140
|
|
|
|
#define COVER_HEIGHT 116
|
|
|
|
#define COVER_Y 5
|
|
|
|
#define COVER_X (TIC80_WIDTH - COVER_WIDTH - COVER_Y)
|
|
|
|
|
2017-11-20 07:14:37 +01:00
|
|
|
#if defined(__WINDOWS__) || defined(__LINUX__) || defined(__MACOSX__)
|
2017-09-26 08:59:34 +02:00
|
|
|
#define CAN_OPEN_URL 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
s32 start;
|
|
|
|
s32 end;
|
|
|
|
s32 duration;
|
|
|
|
|
|
|
|
s32* val;
|
|
|
|
} Anim;
|
|
|
|
|
|
|
|
typedef struct Movie Movie;
|
|
|
|
|
|
|
|
struct Movie
|
|
|
|
{
|
|
|
|
Anim** items;
|
|
|
|
|
|
|
|
s32 time;
|
|
|
|
s32 duration;
|
|
|
|
s32 count;
|
|
|
|
|
|
|
|
Movie* next;
|
|
|
|
void (*done)(Surf* surf);
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct
|
|
|
|
{
|
|
|
|
s32 topBarY;
|
|
|
|
s32 bottomBarY;
|
|
|
|
s32 menuX;
|
|
|
|
s32 menuHeight;
|
|
|
|
} AnimVar;
|
|
|
|
|
|
|
|
static Anim topBarShowAnim = {0, MENU_HEIGHT, ANIM, &AnimVar.topBarY};
|
|
|
|
static Anim bottomBarShowAnim = {0, MENU_HEIGHT, ANIM, &AnimVar.bottomBarY};
|
|
|
|
|
|
|
|
static Anim topBarHideAnim = {MENU_HEIGHT, 0, ANIM, &AnimVar.topBarY};
|
|
|
|
static Anim bottomBarHideAnim = {MENU_HEIGHT, 0, ANIM, &AnimVar.bottomBarY};
|
|
|
|
|
|
|
|
static Anim menuLeftHideAnim = {0, -240, ANIM, &AnimVar.menuX};
|
|
|
|
static Anim menuRightHideAnim = {0, 240, ANIM, &AnimVar.menuX};
|
|
|
|
static Anim menuHideAnim = {MENU_HEIGHT, 0, ANIM, &AnimVar.menuHeight};
|
|
|
|
|
|
|
|
static Anim menuLeftShowAnim = {240, 0, ANIM, &AnimVar.menuX};
|
|
|
|
static Anim menuRightShowAnim = {-240, 0, ANIM, &AnimVar.menuX};
|
|
|
|
static Anim menuShowAnim = {0, MENU_HEIGHT, ANIM, &AnimVar.menuHeight};
|
|
|
|
|
|
|
|
static Anim* MenuModeShowMovieItems[] =
|
|
|
|
{
|
|
|
|
&topBarShowAnim,
|
|
|
|
&bottomBarShowAnim,
|
|
|
|
&menuRightShowAnim,
|
|
|
|
&menuShowAnim,
|
|
|
|
};
|
|
|
|
|
|
|
|
static Anim* MenuModeHideMovieItems[] =
|
|
|
|
{
|
|
|
|
&topBarHideAnim,
|
|
|
|
&bottomBarHideAnim,
|
|
|
|
&menuLeftHideAnim,
|
|
|
|
&menuHideAnim,
|
|
|
|
};
|
|
|
|
|
|
|
|
static Anim* MenuLeftHideMovieItems[] =
|
|
|
|
{
|
|
|
|
&menuLeftHideAnim,
|
|
|
|
&menuHideAnim,
|
|
|
|
};
|
|
|
|
|
|
|
|
static Anim* MenuRightHideMovieItems[] =
|
|
|
|
{
|
|
|
|
&menuRightHideAnim,
|
|
|
|
&menuHideAnim,
|
|
|
|
};
|
|
|
|
|
|
|
|
static Anim* MenuLeftShowMovieItems[] =
|
|
|
|
{
|
|
|
|
&menuLeftShowAnim,
|
|
|
|
&menuShowAnim,
|
|
|
|
};
|
|
|
|
|
|
|
|
static Anim* MenuRightShowMovieItems[] =
|
|
|
|
{
|
|
|
|
&menuRightShowAnim,
|
|
|
|
&menuShowAnim,
|
|
|
|
};
|
|
|
|
|
|
|
|
static Movie EmptyState;
|
|
|
|
static Movie MenuModeState;
|
|
|
|
|
|
|
|
#define DECLARE_MOVIE(NAME, NEXT) static Movie NAME ## State = \
|
|
|
|
{ \
|
|
|
|
.items = NAME ## MovieItems, \
|
|
|
|
.count = COUNT_OF(NAME ## MovieItems), \
|
|
|
|
.duration = ANIM, \
|
|
|
|
.next = & NEXT ## State, \
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_MOVIE(MenuModeShow, MenuMode);
|
|
|
|
DECLARE_MOVIE(MenuModeHide, Empty);
|
|
|
|
DECLARE_MOVIE(MenuLeftShow, MenuMode);
|
|
|
|
DECLARE_MOVIE(MenuRightShow, MenuMode);
|
|
|
|
DECLARE_MOVIE(MenuLeftHide, MenuLeftShow);
|
|
|
|
DECLARE_MOVIE(MenuRightHide, MenuRightShow);
|
|
|
|
|
|
|
|
typedef struct MenuItem MenuItem;
|
|
|
|
|
|
|
|
struct MenuItem
|
|
|
|
{
|
|
|
|
char* label;
|
|
|
|
const char* name;
|
|
|
|
const char* hash;
|
|
|
|
s32 id;
|
|
|
|
tic_screen* cover;
|
|
|
|
bool dir;
|
2017-11-19 15:44:01 +01:00
|
|
|
bool project;
|
2017-09-26 08:59:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
MenuItem* items;
|
|
|
|
s32 count;
|
|
|
|
Surf* surf;
|
|
|
|
} AddMenuItem;
|
|
|
|
|
|
|
|
static void resetMovie(Surf* surf, Movie* movie, void (*done)(Surf* surf))
|
|
|
|
{
|
|
|
|
surf->state = movie;
|
|
|
|
|
|
|
|
movie->time = 0;
|
|
|
|
for(s32 i = 0; i < movie->count; i++)
|
|
|
|
{
|
|
|
|
Anim* anim = movie->items[i];
|
|
|
|
*anim->val = anim->start;
|
|
|
|
}
|
|
|
|
|
|
|
|
movie->time = 0;
|
|
|
|
movie->done = done;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void drawTopToolbar(Surf* surf, s32 x, s32 y)
|
|
|
|
{
|
|
|
|
tic_mem* tic = surf->tic;
|
|
|
|
|
|
|
|
enum{Height = MENU_HEIGHT};
|
|
|
|
|
|
|
|
tic->api.rect(tic, x, y, TIC80_WIDTH, Height, tic_color_blue);
|
|
|
|
tic->api.rect(tic, x, y + Height, TIC80_WIDTH, 1, tic_color_black);
|
|
|
|
{
|
|
|
|
char label[FILENAME_MAX];
|
|
|
|
|
|
|
|
sprintf(label, "%s", "TIC-80 SURF");
|
|
|
|
|
|
|
|
s32 xl = x + MAIN_OFFSET;
|
|
|
|
s32 yl = y + (Height - TIC_FONT_HEIGHT)/2;
|
|
|
|
tic->api.text(tic, label, xl, yl+1, tic_color_black);
|
|
|
|
tic->api.text(tic, label, xl, yl, tic_color_white);
|
|
|
|
}
|
|
|
|
|
|
|
|
enum{Gap = 10, TipX = 150, SelectWidth = 54};
|
|
|
|
|
|
|
|
u8 colorkey = 0;
|
2017-12-14 12:53:47 +01:00
|
|
|
tic->api.sprite_ex(tic, &tic->config.bank.tiles, 12, TipX, y+1, 1, 1, &colorkey, 1, 1, tic_no_flip, tic_no_rotate);
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
static const char Label[] = "SELECT";
|
|
|
|
tic->api.text(tic, Label, TipX + Gap, y+3, tic_color_black);
|
|
|
|
tic->api.text(tic, Label, TipX + Gap, y+2, tic_color_white);
|
|
|
|
}
|
|
|
|
|
2017-12-14 12:53:47 +01:00
|
|
|
tic->api.sprite_ex(tic, &tic->config.bank.tiles, 13, TipX + SelectWidth, y + 1, 1, 1, &colorkey, 1, 1, tic_no_flip, tic_no_rotate);
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
static const char Label[] = "BACK";
|
|
|
|
tic->api.text(tic, Label, TipX + Gap + SelectWidth, y +3, tic_color_black);
|
|
|
|
tic->api.text(tic, Label, TipX + Gap + SelectWidth, y +2, tic_color_white);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void drawBottomToolbar(Surf* surf, s32 x, s32 y)
|
|
|
|
{
|
|
|
|
tic_mem* tic = surf->tic;
|
|
|
|
|
|
|
|
enum{Height = MENU_HEIGHT};
|
|
|
|
|
|
|
|
tic->api.rect(tic, x, y, TIC80_WIDTH, Height, tic_color_blue);
|
|
|
|
tic->api.rect(tic, x, y + Height, TIC80_WIDTH, 1, tic_color_black);
|
|
|
|
{
|
|
|
|
char label[FILENAME_MAX];
|
|
|
|
|
|
|
|
sprintf(label, "/%s", fsGetDir(surf->fs));
|
|
|
|
|
|
|
|
s32 xl = x + MAIN_OFFSET;
|
|
|
|
s32 yl = y + (Height - TIC_FONT_HEIGHT)/2;
|
|
|
|
tic->api.text(tic, label, xl, yl+1, tic_color_black);
|
|
|
|
tic->api.text(tic, label, xl, yl, tic_color_white);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CAN_OPEN_URL
|
|
|
|
|
|
|
|
if(surf->menu.items[surf->menu.pos].hash)
|
|
|
|
{
|
|
|
|
enum{Gap = 10, TipX = 134, SelectWidth = 54};
|
|
|
|
|
|
|
|
u8 colorkey = 0;
|
|
|
|
|
2017-12-14 12:53:47 +01:00
|
|
|
tic->api.sprite_ex(tic, &tic->config.bank.tiles, 15, TipX + SelectWidth, y + 1, 1, 1, &colorkey, 1, 1, tic_no_flip, tic_no_rotate);
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
static const char Label[] = "WEBSITE";
|
|
|
|
tic->api.text(tic, Label, TipX + Gap + SelectWidth, y +3, tic_color_black);
|
|
|
|
tic->api.text(tic, Label, TipX + Gap + SelectWidth, y +2, tic_color_white);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void drawCover(Surf* surf, s32 pos, s32 x, s32 y)
|
|
|
|
{
|
|
|
|
if(!surf->menu.items[surf->menu.pos].cover)
|
|
|
|
return;
|
|
|
|
|
|
|
|
tic_mem* tic = surf->tic;
|
|
|
|
|
|
|
|
enum{Width = TIC80_WIDTH, Height = TIC80_HEIGHT};
|
|
|
|
|
|
|
|
tic_screen* cover = surf->menu.items[pos].cover;
|
|
|
|
|
|
|
|
if(cover)
|
|
|
|
{
|
|
|
|
for(s32 yc = 0; yc < Height; yc++)
|
|
|
|
memcpy(tic->ram.vram.screen.data + (yc * TIC80_WIDTH)/2, cover->data + (yc * Width)/2, Width/2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void drawInverseRect(tic_mem* tic, s32 x, s32 y, s32 w, s32 h)
|
|
|
|
{
|
|
|
|
if(x < 0)
|
|
|
|
{
|
|
|
|
w += x;
|
|
|
|
x = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(y < 0)
|
|
|
|
{
|
|
|
|
h += y;
|
|
|
|
y = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
w += x;
|
|
|
|
h += y;
|
|
|
|
|
|
|
|
if(w > TIC80_WIDTH)
|
|
|
|
w = TIC80_WIDTH;
|
|
|
|
|
|
|
|
if(h > TIC80_HEIGHT)
|
|
|
|
h = TIC80_HEIGHT;
|
|
|
|
|
|
|
|
for(s32 j = y; j < h; j++)
|
|
|
|
{
|
|
|
|
for(s32 i = x; i < w; i++)
|
|
|
|
{
|
|
|
|
s32 index = i + j*TIC80_WIDTH;
|
|
|
|
u8 color = tic_tool_peek4(tic->ram.vram.screen.data, index);
|
|
|
|
tic_tool_poke4(tic->ram.vram.screen.data, index, color % 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void drawMenu(Surf* surf, s32 x, s32 y, bool bg)
|
|
|
|
{
|
|
|
|
tic_mem* tic = surf->tic;
|
|
|
|
|
|
|
|
enum {Height = MENU_HEIGHT};
|
|
|
|
|
|
|
|
if(bg)
|
|
|
|
{
|
|
|
|
if(AnimVar.menuHeight)
|
|
|
|
drawInverseRect(tic, 0, y + (MENU_HEIGHT - AnimVar.menuHeight)/2 - 1, TIC80_WIDTH, AnimVar.menuHeight+2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tic->api.rect(tic, 0, y + (MENU_HEIGHT - AnimVar.menuHeight)/2, TIC80_WIDTH, AnimVar.menuHeight, tic_color_red);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(s32 i = 0; i < surf->menu.count; i++)
|
|
|
|
{
|
|
|
|
const char* name = surf->menu.items[i].label;
|
|
|
|
|
|
|
|
s32 ym = Height * i + y - surf->menu.pos*MENU_HEIGHT - surf->menu.anim + (MENU_HEIGHT - TIC_FONT_HEIGHT)/2;
|
|
|
|
|
|
|
|
if(bg)
|
|
|
|
{
|
|
|
|
s32 size = tic->api.text(tic, name, 0, -TIC_FONT_HEIGHT, 0);
|
|
|
|
|
|
|
|
drawInverseRect(tic, x + MAIN_OFFSET - 1, ym-1, size+1, TIC_FONT_HEIGHT+2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tic->api.text(tic, name, x + MAIN_OFFSET, ym + 1, tic_color_black);
|
|
|
|
tic->api.text(tic, name, x + MAIN_OFFSET, ym, tic_color_white);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void drawBG(Surf* surf)
|
|
|
|
{
|
|
|
|
tic_mem* tic = surf->tic;
|
|
|
|
|
|
|
|
enum{Size = 16, Width = TIC80_WIDTH/Size+1, Height = TIC80_HEIGHT/Size+1};
|
|
|
|
|
|
|
|
s32 offset = surf->ticks % Size;
|
|
|
|
s32 counter = 0;
|
|
|
|
|
|
|
|
for(s32 j = 0; j < Height + 1; j++)
|
|
|
|
for(s32 i = 0; i < Width + 1; i++)
|
|
|
|
if(counter++ % 2)
|
2017-12-14 12:53:47 +01:00
|
|
|
tic->api.sprite_ex(tic, &tic->config.bank.tiles, 34, i*Size - offset, j*Size - offset, 2, 2, 0, 0, 1, tic_no_flip, tic_no_rotate);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void replace(char* src, const char* what, const char* with)
|
|
|
|
{
|
|
|
|
while(true)
|
|
|
|
{
|
|
|
|
char* pos = strstr(src, what);
|
|
|
|
|
|
|
|
if(pos)
|
|
|
|
{
|
|
|
|
strcpy(pos, pos + strlen(what) - strlen(with));
|
|
|
|
memcpy(pos, with, strlen(with));
|
|
|
|
}
|
|
|
|
else break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-19 15:44:01 +01:00
|
|
|
static bool hasExt(const char* name, const char* ext)
|
|
|
|
{
|
2017-11-19 21:23:27 +01:00
|
|
|
return strcmp(name + strlen(name) - strlen(ext), ext) == 0;
|
2017-11-19 15:44:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void cutExt(char* name, const char* ext)
|
|
|
|
{
|
|
|
|
name[strlen(name)-strlen(ext)] = '\0';
|
|
|
|
}
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
static bool addMenuItem(const char* name, const char* info, s32 id, void* ptr, bool dir)
|
|
|
|
{
|
|
|
|
AddMenuItem* data = (AddMenuItem*)ptr;
|
|
|
|
|
2017-11-19 15:44:01 +01:00
|
|
|
static const char CartExt[] = CART_EXT;
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2017-11-19 15:44:01 +01:00
|
|
|
if(dir
|
|
|
|
|| hasExt(name, CartExt)
|
|
|
|
#if defined(TIC80_PRO)
|
2017-11-23 13:26:39 +01:00
|
|
|
|| hasExt(name, PROJECT_LUA_EXT)
|
|
|
|
|| hasExt(name, PROJECT_MOON_EXT)
|
|
|
|
|| hasExt(name, PROJECT_JS_EXT)
|
2017-11-19 15:44:01 +01:00
|
|
|
#endif
|
|
|
|
)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
MenuItem* item = &data->items[data->count++];
|
|
|
|
|
|
|
|
item->name = SDL_strdup(name);
|
2017-11-19 21:23:27 +01:00
|
|
|
bool project = false;
|
2017-09-26 08:59:34 +02:00
|
|
|
if(dir)
|
|
|
|
{
|
|
|
|
char folder[FILENAME_MAX];
|
|
|
|
sprintf(folder, "[%s]", name);
|
|
|
|
item->label = SDL_strdup(folder);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
item->label = SDL_strdup(name);
|
2017-11-19 15:44:01 +01:00
|
|
|
|
|
|
|
if(hasExt(name, CartExt))
|
|
|
|
cutExt(item->label, CartExt);
|
|
|
|
else
|
|
|
|
{
|
2017-11-19 21:23:27 +01:00
|
|
|
project = true;
|
2017-11-19 15:44:01 +01:00
|
|
|
}
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
replace(item->label, "&", "&");
|
|
|
|
replace(item->label, "'", "'");
|
|
|
|
}
|
|
|
|
|
|
|
|
item->hash = info ? SDL_strdup(info) : NULL;
|
|
|
|
item->id = id;
|
|
|
|
item->dir = dir;
|
|
|
|
item->cover = NULL;
|
2017-11-19 21:23:27 +01:00
|
|
|
item->project = project;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return data->count < MAX_CARTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void resetMenu(Surf* surf)
|
|
|
|
{
|
|
|
|
if(surf->menu.items)
|
|
|
|
{
|
|
|
|
for(s32 i = 0; i < surf->menu.count; i++)
|
|
|
|
{
|
|
|
|
SDL_free((void*)surf->menu.items[i].name);
|
|
|
|
|
|
|
|
const char* hash = surf->menu.items[i].hash;
|
|
|
|
if(hash) SDL_free((void*)hash);
|
|
|
|
|
|
|
|
tic_screen* cover = surf->menu.items[i].cover;
|
|
|
|
if(cover) SDL_free(cover);
|
|
|
|
|
|
|
|
const char* label = surf->menu.items[i].label;
|
|
|
|
if(label) SDL_free((void*)label);
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_free(surf->menu.items);
|
|
|
|
|
|
|
|
surf->menu.items = NULL;
|
|
|
|
surf->menu.count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
surf->menu.pos = 0;
|
|
|
|
surf->menu.anim = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void* requestCover(Surf* surf, const char* hash, s32* size)
|
|
|
|
{
|
|
|
|
char cachePath[FILENAME_MAX] = {0};
|
|
|
|
sprintf(cachePath, TIC_CACHE "%s.gif", hash);
|
|
|
|
|
|
|
|
{
|
|
|
|
void* data = fsLoadRootFile(surf->fs, cachePath, size);
|
|
|
|
|
|
|
|
if(data)
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
char path[FILENAME_MAX] = {0};
|
|
|
|
sprintf(path, "/cart/%s/cover.gif", hash);
|
|
|
|
void* data = netGetRequest(surf->net, path, size);
|
|
|
|
|
|
|
|
if(data)
|
|
|
|
{
|
|
|
|
fsSaveRootFile(surf->fs, cachePath, data, *size, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void updateMenuItemCover(Surf* surf, const u8* cover, s32 size)
|
|
|
|
{
|
|
|
|
tic_mem* tic = surf->tic;
|
|
|
|
|
|
|
|
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
|
|
|
|
|
|
|
|
|
|
|
item->cover = SDL_malloc(sizeof(tic_screen));
|
|
|
|
|
|
|
|
gif_image* image = gif_read_data(cover, size);
|
|
|
|
|
|
|
|
if(image)
|
|
|
|
{
|
|
|
|
if (image->width == TIC80_WIDTH && image->height == TIC80_HEIGHT)
|
|
|
|
{
|
|
|
|
enum { Size = TIC80_WIDTH * TIC80_HEIGHT };
|
|
|
|
|
|
|
|
for (s32 i = 0; i < Size; i++)
|
|
|
|
{
|
|
|
|
const gif_color* c = &image->palette[image->buffer[i]];
|
|
|
|
tic_rgb rgb = { c->r, c->g, c->b };
|
2017-12-14 13:05:43 +01:00
|
|
|
u8 color = tic_tool_find_closest_color(tic->config.palette.colors, &rgb);
|
2017-09-26 08:59:34 +02:00
|
|
|
tic_tool_poke4(item->cover->data, i, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gif_close(image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void loadCover(Surf* surf)
|
|
|
|
{
|
|
|
|
tic_mem* tic = surf->tic;
|
|
|
|
|
|
|
|
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
|
|
|
|
|
|
|
if(!fsIsInPublicDir(surf->fs))
|
|
|
|
{
|
|
|
|
|
|
|
|
s32 size = 0;
|
|
|
|
void* data = fsLoadFile(surf->fs, item->name, &size);
|
|
|
|
|
|
|
|
if(data)
|
|
|
|
{
|
|
|
|
tic_cartridge* cart = (tic_cartridge*)SDL_malloc(sizeof(tic_cartridge));
|
|
|
|
|
|
|
|
if(cart)
|
|
|
|
{
|
2017-11-23 13:26:39 +01:00
|
|
|
if(hasExt(item->name, PROJECT_LUA_EXT))
|
|
|
|
surf->console->loadProject(surf->console, item->name, data, size, cart);
|
2017-11-19 15:44:01 +01:00
|
|
|
else
|
2017-12-14 12:53:47 +01:00
|
|
|
tic->api.load(cart, data, size, true);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
if(cart->cover.size)
|
|
|
|
updateMenuItemCover(surf, cart->cover.data, cart->cover.size);
|
|
|
|
|
|
|
|
SDL_free(cart);
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_free(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(item->hash && !item->cover)
|
|
|
|
{
|
|
|
|
s32 size = 0;
|
|
|
|
|
|
|
|
u8* cover = requestCover(surf, item->hash, &size);
|
|
|
|
|
|
|
|
if(cover)
|
|
|
|
{
|
|
|
|
updateMenuItemCover(surf, cover, size);
|
|
|
|
SDL_free(cover);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void initMenu(Surf* surf)
|
|
|
|
{
|
|
|
|
resetMenu(surf);
|
|
|
|
|
|
|
|
// TODO: calc files count before
|
|
|
|
enum{Count = MAX_CARTS, Size = sizeof(MenuItem) * Count};
|
|
|
|
|
|
|
|
AddMenuItem data =
|
|
|
|
{
|
|
|
|
.items = SDL_malloc(Size),
|
|
|
|
.count = 0,
|
|
|
|
.surf = surf,
|
|
|
|
};
|
|
|
|
|
|
|
|
if(strcmp(fsGetDir(surf->fs), "") != 0)
|
|
|
|
addMenuItem("..", NULL, 0, &data, true);
|
|
|
|
|
|
|
|
fsEnumFiles(surf->fs, addMenuItem, &data);
|
|
|
|
|
|
|
|
surf->menu.items = data.items;
|
|
|
|
surf->menu.count = data.count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void onGoBackDir(Surf* surf)
|
|
|
|
{
|
|
|
|
char last[FILENAME_MAX];
|
|
|
|
strcpy(last, fsGetDir(surf->fs));
|
|
|
|
|
|
|
|
fsDirBack(surf->fs);
|
|
|
|
initMenu(surf);
|
|
|
|
|
|
|
|
const char* current = fsGetDir(surf->fs);
|
|
|
|
|
|
|
|
for(s32 i = 0; i < surf->menu.count; i++)
|
|
|
|
{
|
|
|
|
const MenuItem* item = &surf->menu.items[i];
|
|
|
|
|
|
|
|
if(item->dir)
|
|
|
|
{
|
|
|
|
char path[FILENAME_MAX];
|
|
|
|
|
|
|
|
if(strlen(current))
|
|
|
|
sprintf(path, "%s/%s", current, item->name);
|
|
|
|
else strcpy(path, item->name);
|
|
|
|
|
|
|
|
if(strcmp(path, last) == 0)
|
|
|
|
{
|
|
|
|
surf->menu.pos = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void onGoToDir(Surf* surf)
|
|
|
|
{
|
|
|
|
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
|
|
|
|
|
|
|
fsChangeDir(surf->fs, item->name);
|
|
|
|
initMenu(surf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void changeDirectory(Surf* surf, const char* dir)
|
|
|
|
{
|
|
|
|
if(strcmp(dir, "..") == 0)
|
|
|
|
{
|
|
|
|
if(strcmp(fsGetDir(surf->fs), "") != 0)
|
|
|
|
{
|
|
|
|
playSystemSfx(2);
|
|
|
|
resetMovie(surf, &MenuRightHideState, onGoBackDir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(fsIsDir(surf->fs, dir))
|
|
|
|
{
|
|
|
|
playSystemSfx(2);
|
|
|
|
resetMovie(surf, &MenuLeftHideState, onGoToDir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void onPlayCart(Surf* surf)
|
|
|
|
{
|
|
|
|
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
|
|
|
|
2017-11-19 15:44:01 +01:00
|
|
|
if(item->project)
|
|
|
|
{
|
|
|
|
tic_cartridge* cart = SDL_malloc(sizeof(tic_cartridge));
|
|
|
|
|
|
|
|
if(cart)
|
|
|
|
{
|
|
|
|
s32 size = 0;
|
|
|
|
void* data = fsLoadFile(surf->fs, item->name, &size);
|
|
|
|
|
2017-11-23 13:26:39 +01:00
|
|
|
surf->console->loadProject(surf->console, item->name, data, size, cart);
|
2017-11-19 15:44:01 +01:00
|
|
|
|
|
|
|
SDL_memcpy(&surf->tic->cart, cart, sizeof(tic_cartridge));
|
|
|
|
|
|
|
|
studioRomLoaded();
|
|
|
|
|
|
|
|
SDL_free(cart);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
surf->console->load(surf->console, item->name);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
runGameFromSurf();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void loadCart(Surf* surf)
|
|
|
|
{
|
|
|
|
resetMovie(surf, &MenuModeHideState, onPlayCart);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void processAnim(Surf* surf)
|
|
|
|
{
|
|
|
|
enum{Frames = MENU_HEIGHT};
|
|
|
|
|
|
|
|
{
|
|
|
|
if(surf->state->time > surf->state->duration)
|
|
|
|
{
|
|
|
|
if(surf->state->done)
|
|
|
|
surf->state->done(surf);
|
|
|
|
|
|
|
|
if(surf->state->next)
|
|
|
|
resetMovie(surf, surf->state->next, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(s32 i = 0; i < surf->state->count; i++)
|
|
|
|
{
|
|
|
|
Anim* anim = surf->state->items[i];
|
|
|
|
|
|
|
|
if(surf->state->time < anim->duration)
|
|
|
|
{
|
|
|
|
*anim->val = anim->start + (anim->end - anim->start) * surf->state->time / anim->duration;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*anim->val = anim->end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
surf->state->time++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(surf->menu.anim)
|
|
|
|
{
|
|
|
|
if(surf->menu.anim < 0) surf->menu.anim--;
|
|
|
|
if(surf->menu.anim > 0) surf->menu.anim++;
|
|
|
|
|
|
|
|
if(surf->menu.anim <= -Frames)
|
|
|
|
{
|
|
|
|
surf->menu.anim = 0;
|
|
|
|
surf->menu.pos--;
|
|
|
|
|
|
|
|
if(surf->menu.pos < 0)
|
|
|
|
surf->menu.pos = surf->menu.count-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(surf->menu.anim >= Frames)
|
|
|
|
{
|
|
|
|
surf->menu.anim = 0;
|
|
|
|
surf->menu.pos++;
|
|
|
|
|
|
|
|
if(surf->menu.pos >= surf->menu.count)
|
|
|
|
surf->menu.pos = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void processGamepad(Surf* surf)
|
|
|
|
{
|
|
|
|
tic_mem* tic = surf->tic;
|
|
|
|
|
|
|
|
enum{Frames = MENU_HEIGHT};
|
|
|
|
|
|
|
|
{
|
|
|
|
enum{Hold = 20, Period = Frames};
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
Up, Down, Left, Right, A, B, X, Y
|
|
|
|
};
|
|
|
|
|
|
|
|
if(tic->api.btnp(tic, Up, Hold, Period))
|
|
|
|
{
|
|
|
|
surf->menu.anim = -1;
|
|
|
|
|
|
|
|
playSystemSfx(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(tic->api.btnp(tic, Down, Hold, Period))
|
|
|
|
{
|
|
|
|
surf->menu.anim = 1;
|
|
|
|
|
|
|
|
playSystemSfx(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(tic->api.btnp(tic, A, -1, -1))
|
|
|
|
{
|
|
|
|
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
|
|
|
item->dir ? changeDirectory(surf, item->name) : loadCart(surf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(tic->api.btnp(tic, B, -1, -1))
|
|
|
|
{
|
|
|
|
changeDirectory(surf, "..");
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CAN_OPEN_URL
|
|
|
|
|
|
|
|
if(tic->api.btnp(tic, Y, -1, -1))
|
|
|
|
{
|
|
|
|
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
|
|
|
|
|
|
|
if(!item->dir)
|
|
|
|
{
|
|
|
|
char url[FILENAME_MAX];
|
|
|
|
sprintf(url, "https://" TIC_HOST "/play?cart=%i", item->id);
|
|
|
|
fsOpenSystemPath(surf->fs, url);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tick(Surf* surf)
|
|
|
|
{
|
|
|
|
if(!surf->init)
|
|
|
|
{
|
|
|
|
initMenu(surf);
|
|
|
|
|
|
|
|
resetMovie(surf, &MenuModeShowState, NULL);
|
|
|
|
|
|
|
|
surf->init = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
surf->ticks++;
|
|
|
|
|
|
|
|
while (pollEvent());
|
|
|
|
|
|
|
|
|
|
|
|
tic_mem* tic = surf->tic;
|
|
|
|
tic->api.clear(tic, TIC_COLOR_BG);
|
|
|
|
|
|
|
|
drawBG(surf);
|
|
|
|
|
2017-10-20 11:52:54 +02:00
|
|
|
if(surf->menu.count > 0)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2017-10-20 11:52:54 +02:00
|
|
|
processAnim(surf);
|
|
|
|
|
|
|
|
if(surf->state == &MenuModeState)
|
|
|
|
{
|
|
|
|
processGamepad(surf);
|
|
|
|
}
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
loadCover(surf);
|
|
|
|
|
|
|
|
drawCover(surf, surf->menu.pos, 0, 0);
|
|
|
|
|
|
|
|
if(surf->menu.items[surf->menu.pos].cover)
|
|
|
|
drawMenu(surf, AnimVar.menuX, (TIC80_HEIGHT - MENU_HEIGHT)/2, true);
|
|
|
|
|
|
|
|
drawMenu(surf, AnimVar.menuX, (TIC80_HEIGHT - MENU_HEIGHT)/2, false);
|
|
|
|
|
|
|
|
drawTopToolbar(surf, 0, AnimVar.topBarY - MENU_HEIGHT);
|
|
|
|
drawBottomToolbar(surf, 0, TIC80_HEIGHT - AnimVar.bottomBarY);
|
|
|
|
}
|
2017-10-20 11:52:54 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
static const char Label[] = "You don't have any files...";
|
|
|
|
s32 size = tic->api.text(tic, Label, 0, -TIC_FONT_HEIGHT, tic_color_white);
|
|
|
|
tic->api.text(tic, Label, (TIC80_WIDTH - size) / 2, (TIC80_HEIGHT - TIC_FONT_HEIGHT)/2, tic_color_white);
|
|
|
|
}
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void resume(Surf* surf)
|
|
|
|
{
|
|
|
|
resetMovie(surf, &MenuModeShowState, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void initSurf(Surf* surf, tic_mem* tic, struct Console* console)
|
|
|
|
{
|
|
|
|
*surf = (Surf)
|
|
|
|
{
|
|
|
|
.tic = tic,
|
|
|
|
.console = console,
|
|
|
|
.fs = console->fs,
|
|
|
|
.tick = tick,
|
|
|
|
.ticks = 0,
|
|
|
|
.state = &EmptyState,
|
|
|
|
.init = false,
|
|
|
|
.resume = resume,
|
|
|
|
.menu =
|
|
|
|
{
|
|
|
|
.pos = 0,
|
|
|
|
.anim = 0,
|
|
|
|
.items = NULL,
|
|
|
|
.count = 0,
|
|
|
|
},
|
|
|
|
.net = createNet(),
|
|
|
|
};
|
|
|
|
|
|
|
|
fsMakeDir(surf->fs, TIC_CACHE);
|
|
|
|
}
|