TIC-80-guile/src/console.c

2992 lines
65 KiB
C
Raw Normal View History

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 "console.h"
#include "fs.h"
#include "config.h"
#include "net.h"
#include "ext/gif.h"
#include "ext/file_dialog.h"
#include <zlib.h>
2017-10-20 09:37:30 +02:00
#define CONSOLE_CURSOR_COLOR ((tic_color_red))
#define CONSOLE_BACK_TEXT_COLOR ((tic_color_dark_gray))
#define CONSOLE_FRONT_TEXT_COLOR ((tic_color_white))
#define CONSOLE_ERROR_TEXT_COLOR ((tic_color_red))
2017-09-26 08:59:34 +02:00
#define CONSOLE_CURSOR_BLINK_PERIOD (TIC_FRAMERATE)
#define CONSOLE_CURSOR_DELAY (TIC_FRAMERATE / 2)
#define CONSOLE_BUFFER_WIDTH (STUDIO_TEXT_BUFFER_WIDTH)
#define CONSOLE_BUFFER_HEIGHT (STUDIO_TEXT_BUFFER_HEIGHT)
#define CONSOLE_BUFFER_SCREENS 64
#define CONSOLE_BUFFER_SIZE (CONSOLE_BUFFER_WIDTH * CONSOLE_BUFFER_HEIGHT * CONSOLE_BUFFER_SCREENS)
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_EXPORT 1
#endif
#if defined(__WINDOWS__)
static const char* ExeExt = ".exe";
#endif
// static struct
// {
// char prefix[32];
// bool yes;
// bool menu;
// tic_cartridge file;
// } embed =
// {
// .prefix = "C8B39163816B47209E721136D37B8031",
// .yes = false,
// };
2017-09-26 08:59:34 +02:00
static const char DefaultLuaTicPath[] = TIC_LOCAL "default.tic";
static const char DefaultMoonTicPath[] = TIC_LOCAL "default_moon.tic";
static const char DefaultJSTicPath[] = TIC_LOCAL "default_js.tic";
2017-11-10 17:40:44 +01:00
static const char* getName(const char* name, const char* ext)
2017-09-26 08:59:34 +02:00
{
static char path[FILENAME_MAX];
strcpy(path, name);
size_t ps = strlen(path);
2017-11-10 17:40:44 +01:00
size_t es = strlen(ext);
2017-09-26 08:59:34 +02:00
2017-11-10 17:40:44 +01:00
if(!(ps > es && strstr(path, ext) + es == path + ps))
strcat(path, ext);
2017-09-26 08:59:34 +02:00
return path;
}
2017-11-10 17:40:44 +01:00
static const char* getCartName(const char* name)
{
2017-11-19 15:44:01 +01:00
return getName(name, CART_EXT);
2017-11-10 17:40:44 +01:00
}
2017-09-26 08:59:34 +02:00
static void scrollBuffer(char* buffer)
{
memmove(buffer, buffer + CONSOLE_BUFFER_WIDTH, CONSOLE_BUFFER_SIZE - CONSOLE_BUFFER_WIDTH);
memset(buffer + CONSOLE_BUFFER_SIZE - CONSOLE_BUFFER_WIDTH, 0, CONSOLE_BUFFER_WIDTH);
}
static void scrollConsole(Console* console)
{
while(console->cursor.y >= CONSOLE_BUFFER_HEIGHT * CONSOLE_BUFFER_SCREENS)
{
scrollBuffer(console->buffer);
scrollBuffer((char*)console->colorBuffer);
console->cursor.y--;
}
s32 minScroll = console->cursor.y - CONSOLE_BUFFER_HEIGHT + 1;
if(console->scroll.pos < minScroll)
console->scroll.pos = minScroll;
}
static void consolePrint(Console* console, const char* text, u8 color)
{
printf("%s", text);
const char* textPointer = text;
const char* endText = textPointer + strlen(text);
while(textPointer != endText)
{
char symbol = *textPointer++;
scrollConsole(console);
if(symbol == '\n')
{
console->cursor.x = 0;
console->cursor.y++;
}
else
2017-09-26 08:59:34 +02:00
{
s32 offset = console->cursor.x + console->cursor.y * CONSOLE_BUFFER_WIDTH;
*(console->buffer + offset) = symbol;
*(console->colorBuffer + offset) = color;
console->cursor.x++;
if(console->cursor.x >= CONSOLE_BUFFER_WIDTH)
{
console->cursor.x = 0;
console->cursor.y++;
}
}
}
}
static void printBack(Console* console, const char* text)
{
consolePrint(console, text, CONSOLE_BACK_TEXT_COLOR);
}
static void printFront(Console* console, const char* text)
{
consolePrint(console, text, CONSOLE_FRONT_TEXT_COLOR);
}
static void printError(Console* console, const char* text)
{
consolePrint(console, text, CONSOLE_ERROR_TEXT_COLOR);
}
static void printLine(Console* console)
{
consolePrint(console, "\n", 0);
}
static void commandDoneLine(Console* console, bool newLine)
{
if(newLine)
printLine(console);
if(strlen(fsGetDir(console->fs)))
printBack(console, fsGetDir(console->fs));
printFront(console, ">");
}
static void commandDone(Console* console)
{
commandDoneLine(console, true);
}
static void drawCursor(Console* console, s32 x, s32 y, u8 symbol)
{
bool inverse = console->cursor.delay || console->tickCounter % CONSOLE_CURSOR_BLINK_PERIOD < CONSOLE_CURSOR_BLINK_PERIOD / 2;
if(inverse)
console->tic->api.rect(console->tic, x-1, y-1, TIC_FONT_WIDTH+1, TIC_FONT_HEIGHT+1, CONSOLE_CURSOR_COLOR);
2017-09-26 08:59:34 +02:00
console->tic->api.draw_char(console->tic, symbol, x, y, inverse ? TIC_COLOR_BG : CONSOLE_FRONT_TEXT_COLOR);
}
static void drawConsoleText(Console* console)
{
char* pointer = console->buffer + console->scroll.pos * CONSOLE_BUFFER_WIDTH;
u8* colorPointer = console->colorBuffer + console->scroll.pos * CONSOLE_BUFFER_WIDTH;
const char* end = console->buffer + CONSOLE_BUFFER_SIZE;
s32 x = 0;
s32 y = 0;
while(pointer < end)
{
char symbol = *pointer++;
u8 color = *colorPointer++;
if(symbol)
console->tic->api.draw_char(console->tic, symbol, x * STUDIO_TEXT_WIDTH, y * STUDIO_TEXT_HEIGHT, color);
if(++x == CONSOLE_BUFFER_WIDTH)
{
y++;
x = 0;
}
}
}
static void drawConsoleInputText(Console* console)
{
s32 x = console->cursor.x * STUDIO_TEXT_WIDTH;
s32 y = (console->cursor.y - console->scroll.pos) * STUDIO_TEXT_HEIGHT;
const char* pointer = console->inputBuffer;
const char* end = pointer + strlen(console->inputBuffer);
s32 index = 0;
while(pointer != end)
{
char symbol = *pointer++;
if(console->inputPosition == index)
drawCursor(console, x, y, symbol);
else
console->tic->api.draw_char(console->tic, symbol, x, y, CONSOLE_FRONT_TEXT_COLOR);
index++;
x += STUDIO_TEXT_WIDTH;
if(x == (CONSOLE_BUFFER_WIDTH * STUDIO_TEXT_WIDTH))
{
y += STUDIO_TEXT_HEIGHT;
x = 0;
}
}
if(console->inputPosition == index)
drawCursor(console, x, y, ' ');
}
static void processConsoleHome(Console* console)
{
console->inputPosition = 0;
}
static void processConsoleEnd(Console* console)
{
console->inputPosition = strlen(console->inputBuffer);
}
static void processConsoleDel(Console* console)
{
char* pos = console->inputBuffer + console->inputPosition;
size_t size = strlen(pos);
memmove(pos, pos + 1, size);
2017-09-26 08:59:34 +02:00
}
static void processConsoleBackspace(Console* console)
{
if(console->inputPosition > 0)
{
console->inputPosition--;
processConsoleDel(console);
2017-09-26 08:59:34 +02:00
}
}
static void onConsoleHelpCommand(Console* console, const char* param);
static void onConsoleExitCommand(Console* console, const char* param)
{
exitStudio();
commandDone(console);
}
static s32 writeGifData(const tic_mem* tic, u8* dst, const u8* src, s32 width, s32 height)
{
s32 size = 0;
gif_color* palette = (gif_color*)SDL_malloc(sizeof(gif_color) * TIC_PALETTE_SIZE);
if(palette)
{
2017-12-14 13:05:43 +01:00
const tic_rgb* pal = tic->cart.palette.colors;
2017-09-26 08:59:34 +02:00
for(s32 i = 0; i < TIC_PALETTE_SIZE; i++, pal++)
palette[i].r = pal->r, palette[i].g = pal->g, palette[i].b = pal->b;
gif_write_data(dst, &size, width, height, src, palette, TIC_PALETTE_BPP);
SDL_free(palette);
}
return size;
}
static void loadCart(tic_mem* tic, tic_cartridge* cart, const u8* buffer, s32 size, bool palette)
{
tic->api.load(cart, buffer, size, palette);
2017-09-26 08:59:34 +02:00
if(!palette)
2017-12-14 13:05:43 +01:00
memcpy(cart->palette.data, tic->config.palette.data, sizeof(tic_palette));
2017-09-26 08:59:34 +02:00
}
static bool loadRom(tic_mem* tic, const void* data, s32 size, bool palette)
{
loadCart(tic, &tic->cart, data, size, palette);
tic->api.reset(tic);
return true;
}
static bool onConsoleLoadSectionCommand(Console* console, const char* param)
{
bool result = false;
if(param)
{
static const char* Sections[] =
2017-09-26 08:59:34 +02:00
{
"cover",
2017-09-26 08:59:34 +02:00
"sprites",
"map",
"code",
"sfx",
"music",
"palette",
};
char buf[64] = {0};
for(s32 i = 0; i < COUNT_OF(Sections); i++)
{
2017-11-19 15:44:01 +01:00
sprintf(buf, "%s %s", CART_EXT, Sections[i]);
2017-09-26 08:59:34 +02:00
char* pos = SDL_strstr(param, buf);
if(pos)
{
2017-11-19 15:44:01 +01:00
pos[sizeof(CART_EXT) - 1] = 0;
2017-11-10 17:40:44 +01:00
const char* name = getCartName(param);
2017-09-26 08:59:34 +02:00
s32 size = 0;
void* data = fsLoadFile(console->fs, name, &size);
if(data)
{
tic_cartridge* cart = (tic_cartridge*)SDL_malloc(sizeof(tic_cartridge));
if(cart)
{
loadCart(console->tic, cart, data, size, true);
tic_mem* tic = console->tic;
switch(i)
{
case 0: memcpy(&tic->cart.cover, &cart->cover, sizeof cart->cover); break;
2017-12-14 13:37:05 +01:00
case 1: memcpy(&tic->cart.bank0.tiles, &cart->bank0.tiles, sizeof(tic_tiles)*2); break;
case 2: memcpy(&tic->cart.bank0.map, &cart->bank0.map, sizeof(tic_map)); break;
case 3: memcpy(&tic->cart.bank0.code, &cart->bank0.code, sizeof(tic_code)); break;
case 4: memcpy(&tic->cart.bank0.sfx, &cart->bank0.sfx, sizeof(tic_sfx)); break;
case 5: memcpy(&tic->cart.bank0.music, &cart->bank0.music, sizeof(tic_music)); break;
case 6: memcpy(&tic->cart.palette, &cart->palette, sizeof(tic_palette)); break;
2017-09-26 08:59:34 +02:00
}
studioRomLoaded();
printLine(console);
printFront(console, Sections[i]);
printBack(console, " loaded from ");
printFront(console, name);
printLine(console);
SDL_free(cart);
result = true;
}
SDL_free(data);
}
else printBack(console, "\ncart loading error");
commandDone(console);
}
}
}
return result;
}
static void* getDemoCart(Console* console, tic_script_lang script, s32* size)
{
char path[FILENAME_MAX] = {0};
{
switch(script)
{
case tic_script_lua:
strcpy(path, DefaultLuaTicPath);
break;
case tic_script_moon:
strcpy(path, DefaultMoonTicPath);
break;
case tic_script_js:
strcpy(path, DefaultJSTicPath);
break;
}
void* data = fsLoadRootFile(console->fs, path, size);
if(data && *size)
return data;
}
static const u8 LuaDemoRom[] =
2017-09-26 08:59:34 +02:00
{
#include "../bin/assets/luademo.tic.dat"
};
static const u8 JsDemoRom[] =
2017-09-26 08:59:34 +02:00
{
#include "../bin/assets/jsdemo.tic.dat"
};
static const u8 MoonDemoRom[] =
2017-09-26 08:59:34 +02:00
{
#include "../bin/assets/moondemo.tic.dat"
};
2017-09-26 08:59:34 +02:00
const u8* demo = NULL;
s32 romSize = 0;
switch(script)
{
case tic_script_lua:
demo = LuaDemoRom;
romSize = sizeof LuaDemoRom;
break;
case tic_script_moon:
demo = MoonDemoRom;
romSize = sizeof MoonDemoRom;
break;
case tic_script_js:
demo = JsDemoRom;
romSize = sizeof JsDemoRom;
break;
}
u8* data = NULL;
*size = unzip(&data, demo, romSize);
if(data)
{
fsSaveRootFile(console->fs, path, data, *size, false);
}
return data;
}
static void setCartName(Console* console, const char* name)
{
if(name != console->romName)
strcpy(console->romName, name);
}
2017-09-26 08:59:34 +02:00
static void onConsoleLoadDemoCommandConfirmed(Console* console, const char* param)
{
void* data = NULL;
s32 size = 0;
console->showGameMenu = false;
2017-09-26 08:59:34 +02:00
if(strcmp(param, DefaultLuaTicPath) == 0)
data = getDemoCart(console, tic_script_lua, &size);
else if(strcmp(param, DefaultMoonTicPath) == 0)
data = getDemoCart(console, tic_script_moon, &size);
else if(strcmp(param, DefaultJSTicPath) == 0)
data = getDemoCart(console, tic_script_js, &size);
2017-11-10 17:40:44 +01:00
const char* name = getCartName(param);
2017-09-26 08:59:34 +02:00
setCartName(console, name);
2017-09-26 08:59:34 +02:00
loadRom(console->tic, data, size, true);
studioRomLoaded();
printBack(console, "\ncart ");
printFront(console, console->romName);
printBack(console, " loaded!\n");
SDL_free(data);
}
2017-11-09 18:34:03 +01:00
static void onCartLoaded(Console* console, const char* name)
{
setCartName(console, name);
2017-11-09 18:34:03 +01:00
studioRomLoaded();
printBack(console, "\ncart ");
printFront(console, console->romName);
printBack(console, " loaded!\nuse ");
printFront(console, "RUN");
printBack(console, " command to run it\n");
}
static bool hasExt(const char* name, const char* ext)
{
return strcmp(name + strlen(name) - strlen(ext), ext) == 0;
}
2017-11-24 10:09:25 +01:00
#if defined(TIC80_PRO)
static bool hasProjectExt(const char* name)
2017-11-19 16:41:39 +01:00
{
return hasExt(name, PROJECT_LUA_EXT) || hasExt(name, PROJECT_MOON_EXT) || hasExt(name, PROJECT_JS_EXT);
}
static const char* projectComment(const char* name)
{
return hasExt(name, PROJECT_JS_EXT) ? "//" : "--";
2017-11-19 16:41:39 +01:00
}
static void buf2str(const void* data, s32 size, char* ptr, bool flip)
{
enum {Len = 2};
for(s32 i = 0; i < size; i++, ptr+=Len)
{
sprintf(ptr, "%02x", ((u8*)data)[i]);
if(flip)
{
char tmp = ptr[0];
ptr[0] = ptr[1];
ptr[1] = tmp;
}
}
}
static bool bufferEmpty(const u8* data, s32 size)
{
for(s32 i = 0; i < size; i++)
if(*data++)
return false;
return true;
}
2017-11-23 11:19:31 +01:00
static char* saveTextSection(char* ptr, const char* data)
2017-11-19 16:41:39 +01:00
{
if(strlen(data) == 0)
return ptr;
2017-11-23 11:19:31 +01:00
sprintf(ptr, "%s\n", data);
2017-11-19 16:41:39 +01:00
ptr += strlen(ptr);
return ptr;
}
static char* saveBinaryBuffer(char* ptr, const char* comment, const void* data, s32 size, s32 row, bool flip)
2017-11-19 16:41:39 +01:00
{
if(bufferEmpty(data, size))
return ptr;
sprintf(ptr, "%s %03i:", comment, row);
2017-11-19 16:41:39 +01:00
ptr += strlen(ptr);
buf2str(data, size, ptr, flip);
ptr += strlen(ptr);
sprintf(ptr, "\n");
ptr += strlen(ptr);
return ptr;
}
static char* saveBinarySection(char* ptr, const char* comment, const char* tag, s32 count, const void* data, s32 size, bool flip)
2017-11-19 16:41:39 +01:00
{
if(bufferEmpty(data, size * count))
return ptr;
sprintf(ptr, "%s <%s>\n", comment, tag);
2017-11-19 16:41:39 +01:00
ptr += strlen(ptr);
for(s32 i = 0; i < count; i++, data = (u8*)data + size)
ptr = saveBinaryBuffer(ptr, comment, data, size, i, flip);
2017-11-19 16:41:39 +01:00
sprintf(ptr, "%s </%s>\n\n", comment, tag);
2017-11-19 16:41:39 +01:00
ptr += strlen(ptr);
return ptr;
}
typedef struct {char* tag; s32 count; s32 offset; s32 size; bool flip;} BinarySection;
static const BinarySection BinarySections[] =
{
2017-12-15 09:42:25 +01:00
{"PALETTE", 1, offsetof(tic_cartridge, palette.data), sizeof(tic_palette), false},
{"TILES", TIC_BANK_SPRITES, offsetof(tic_cartridge, bank0.tiles), sizeof(tic_tile), true},
{"SPRITES", TIC_BANK_SPRITES, offsetof(tic_cartridge, bank0.sprites), sizeof(tic_tile), true},
{"MAP", TIC_MAP_HEIGHT, offsetof(tic_cartridge, bank0.map), TIC_MAP_WIDTH, true},
{"WAVES", ENVELOPES_COUNT, offsetof(tic_cartridge, bank0.sfx.waveform), sizeof(tic_waveform), true},
{"SFX", SFX_COUNT, offsetof(tic_cartridge, bank0.sfx.samples), sizeof(tic_sample), true},
{"PATTERNS", MUSIC_PATTERNS, offsetof(tic_cartridge, bank0.music.patterns), sizeof(tic_track_pattern), true},
{"TRACKS", MUSIC_TRACKS, offsetof(tic_cartridge, bank0.music.tracks), sizeof(tic_track), true},
2017-11-19 16:41:39 +01:00
};
static s32 saveProject(Console* console, void* buffer, const char* comment)
2017-11-19 16:41:39 +01:00
{
tic_mem* tic = console->tic;
char* stream = buffer;
2017-12-14 13:37:05 +01:00
char* ptr = saveTextSection(stream, tic->cart.bank0.code.data);
2017-11-19 16:41:39 +01:00
for(s32 i = 0; i < COUNT_OF(BinarySections); i++)
{
const BinarySection* section = &BinarySections[i];
ptr = saveBinarySection(ptr, comment, section->tag, section->count, (u8*)&tic->cart + section->offset, section->size, section->flip);
2017-11-19 16:41:39 +01:00
}
saveBinarySection(ptr, comment, "COVER", 1, &tic->cart.cover, tic->cart.cover.size + sizeof(s32), true);
2017-11-19 16:41:39 +01:00
return strlen(stream);
}
static bool loadTextSection(const char* project, const char* comment, void* dst, s32 size)
2017-11-19 16:41:39 +01:00
{
2017-11-22 08:20:12 +01:00
bool done = false;
2017-11-19 16:41:39 +01:00
2017-11-23 11:19:31 +01:00
const char* start = project;
const char* end = project + strlen(project);
2017-11-19 16:41:39 +01:00
{
2017-11-23 11:19:31 +01:00
char tagbuf[64];
2017-11-19 16:41:39 +01:00
2017-11-23 11:19:31 +01:00
for(s32 i = 0; i < COUNT_OF(BinarySections); i++)
2017-11-19 16:41:39 +01:00
{
sprintf(tagbuf, "\n%s <%s>\n", comment, BinarySections[i].tag);
2017-11-19 16:41:39 +01:00
2017-11-23 11:19:31 +01:00
const char* ptr = SDL_strstr(project, tagbuf);
if(ptr && ptr < end)
end = ptr;
}
}
if(end > start)
{
SDL_memcpy(dst, start, SDL_min(size, end - start));
done = true;
2017-11-19 16:41:39 +01:00
}
2017-11-22 08:20:12 +01:00
return done;
2017-11-19 16:41:39 +01:00
}
static bool loadBinarySection(const char* project, const char* comment, const char* tag, s32 count, void* dst, s32 size, bool flip)
2017-11-19 16:41:39 +01:00
{
char tagbuf[64];
sprintf(tagbuf, "%s <%s>\n", comment, tag);
2017-11-19 16:41:39 +01:00
const char* start = SDL_strstr(project, tagbuf);
2017-11-22 08:20:12 +01:00
bool done = false;
2017-11-19 16:41:39 +01:00
if(start)
{
start += strlen(tagbuf);
sprintf(tagbuf, "\n%s </%s>", comment, tag);
2017-11-19 16:41:39 +01:00
const char* end = SDL_strstr(start, tagbuf);
if(end > start)
{
const char* ptr = start;
if(size > 0)
{
while(ptr < end)
{
static char lineStr[] = "999";
memcpy(lineStr, ptr + sizeof("-- ") - 1, sizeof lineStr - 1);
s32 index = SDL_atoi(lineStr);
if(index < count)
{
ptr += sizeof("-- 999:") - 1;
str2buf(ptr, size*2, (u8*)dst + size*index, flip);
ptr += size*2 + 1;
}
2017-11-30 14:50:21 +01:00
else break;
2017-11-19 16:41:39 +01:00
}
}
else
{
ptr += sizeof("-- 999:") - 1;
str2buf(ptr, end - ptr, (u8*)dst, flip);
}
2017-11-22 08:20:12 +01:00
done = true;
2017-11-19 16:41:39 +01:00
}
}
2017-11-22 08:20:12 +01:00
return done;
2017-11-19 16:41:39 +01:00
}
static bool loadProject(Console* console, const char* name, const char* data, s32 size, tic_cartridge* dst)
2017-11-19 16:41:39 +01:00
{
tic_mem* tic = console->tic;
char* project = (char*)SDL_malloc(size+1);
bool done = false;
if(project)
{
SDL_memcpy(project, data, size);
project[size] = '\0';
// remove all the '\r' chars
{
char *s, *d;
for(s = d = project; (*d = *s); d += (*s++ != '\r'));
}
tic_cartridge* cart = (tic_cartridge*)SDL_malloc(sizeof(tic_cartridge));
if(cart)
{
SDL_memset(cart, 0, sizeof(tic_cartridge));
2017-12-14 13:05:43 +01:00
SDL_memcpy(&cart->palette, &tic->config.palette.data, sizeof(tic_palette));
2017-11-19 16:41:39 +01:00
const char* comment = projectComment(name);
2017-12-14 13:37:05 +01:00
if(loadTextSection(project, comment, cart->bank0.code.data, sizeof(tic_code)))
2017-11-22 08:20:12 +01:00
done = true;
2017-11-19 16:41:39 +01:00
for(s32 i = 0; i < COUNT_OF(BinarySections); i++)
{
const BinarySection* section = &BinarySections[i];
if(loadBinarySection(project, comment, section->tag, section->count, (u8*)cart + section->offset, section->size, section->flip))
2017-11-22 08:20:12 +01:00
done = true;
2017-11-19 16:41:39 +01:00
}
if(loadBinarySection(project, comment, "COVER", 1, &cart->cover, -1, true))
2017-11-22 08:20:12 +01:00
done = true;
2017-11-19 16:41:39 +01:00
SDL_memcpy(dst, cart, sizeof(tic_cartridge));
SDL_free(cart);
}
SDL_free(project);
}
return done;
}
2017-11-21 17:47:36 +01:00
static void updateProject(Console* console)
{
tic_mem* tic = console->tic;
2017-11-23 21:19:53 +01:00
if(strlen(console->romName) && hasProjectExt(console->romName))
2017-11-21 17:47:36 +01:00
{
s32 size = 0;
void* data = fsLoadFile(console->fs, console->romName, &size);
if(data)
{
2017-11-22 08:20:12 +01:00
tic_cartridge* cart = SDL_malloc(sizeof(tic_cartridge));
if(cart)
{
if(loadProject(console, console->romName, data, size, cart))
2017-11-22 08:20:12 +01:00
{
SDL_memcpy(&tic->cart, cart, sizeof(tic_cartridge));
studioRomLoaded();
}
SDL_free(cart);
}
2017-11-21 17:47:36 +01:00
SDL_free(data);
}
}
}
2017-11-19 16:41:39 +01:00
#endif
2017-09-26 08:59:34 +02:00
static void onConsoleLoadCommandConfirmed(Console* console, const char* param)
{
if(onConsoleLoadSectionCommand(console, param)) return;
if(param)
{
s32 size = 0;
2017-11-10 17:40:44 +01:00
const char* name = getCartName(param);
2017-09-26 08:59:34 +02:00
void* data = strcmp(name, CONFIG_TIC_PATH) == 0
2017-09-26 08:59:34 +02:00
? fsLoadRootFile(console->fs, name, &size)
: fsLoadFile(console->fs, name, &size);
if(data)
{
console->showGameMenu = fsIsInPublicDir(console->fs);
loadRom(console->tic, data, size, true);
2017-11-09 18:34:03 +01:00
onCartLoaded(console, name);
2017-09-26 08:59:34 +02:00
SDL_free(data);
}
2017-11-19 16:41:39 +01:00
else
2017-11-09 18:34:03 +01:00
{
2017-11-19 16:41:39 +01:00
#if defined(TIC80_PRO)
const char* name = getName(param, PROJECT_LUA_EXT);
if(!fsExistsFile(console->fs, name))
name = getName(param, PROJECT_MOON_EXT);
if(!fsExistsFile(console->fs, name))
name = getName(param, PROJECT_JS_EXT);
2017-11-19 16:41:39 +01:00
void* data = fsLoadFile(console->fs, name, &size);
if(data)
{
loadProject(console, name, data, size, &console->tic->cart);
2017-11-19 16:41:39 +01:00
onCartLoaded(console, name);
SDL_free(data);
}
else
{
printBack(console, "\ncart loading error");
}
#else
2017-11-09 18:34:03 +01:00
printBack(console, "\ncart loading error");
2017-11-19 16:41:39 +01:00
#endif
2017-11-09 18:34:03 +01:00
}
2017-09-26 08:59:34 +02:00
}
else printBack(console, "\ncart name is missing");
commandDone(console);
}
typedef void(*ConfirmCallback)(Console* console, const char* param);
typedef struct
{
Console* console;
char* param;
ConfirmCallback callback;
} CommandConfirmData;
static void onConfirm(bool yes, void* data)
{
CommandConfirmData* confirmData = (CommandConfirmData*)data;
if(yes)
{
confirmData->callback(confirmData->console, confirmData->param);
}
else commandDone(confirmData->console);
if(confirmData->param)
SDL_free(confirmData->param);
SDL_free(confirmData);
}
static void confirmCommand(Console* console, const char** text, s32 rows, const char* param, ConfirmCallback callback)
{
CommandConfirmData* data = SDL_malloc(sizeof(CommandConfirmData));
data->console = console;
data->param = param ? SDL_strdup(param) : NULL;
data->callback = callback;
showDialog(text, rows, onConfirm, data);
}
static void onConsoleLoadDemoCommand(Console* console, const char* param)
{
if(studioCartChanged())
{
static const char* Rows[] =
2017-09-26 08:59:34 +02:00
{
"YOU HAVE",
"UNSAVED CHANGES",
"",
"DO YOU REALLY WANT",
"TO LOAD CART?",
};
confirmCommand(console, Rows, COUNT_OF(Rows), param, onConsoleLoadDemoCommandConfirmed);
}
else
{
onConsoleLoadDemoCommandConfirmed(console, param);
}
}
static void onConsoleLoadCommand(Console* console, const char* param)
{
if(studioCartChanged())
{
static const char* Rows[] =
2017-09-26 08:59:34 +02:00
{
"YOU HAVE",
"UNSAVED CHANGES",
"",
"DO YOU REALLY WANT",
"TO LOAD CART?",
};
confirmCommand(console, Rows, COUNT_OF(Rows), param, onConsoleLoadCommandConfirmed);
}
else
{
onConsoleLoadCommandConfirmed(console, param);
}
}
static void loadDemo(Console* console, tic_script_lang script)
{
s32 size = 0;
u8* data = getDemoCart(console, script, &size);
if(data)
{
loadRom(console->tic, data, size, false);
SDL_free(data);
2017-09-26 08:59:34 +02:00
}
SDL_memset(console->romName, 0, sizeof console->romName);
studioRomLoaded();
}
static void onConsoleNewCommandConfirmed(Console* console, const char* param)
{
if(param && strlen(param))
{
if(strcmp(param, "lua") == 0)
loadDemo(console, tic_script_lua);
else if(strcmp(param, "moon") == 0 || strcmp(param, "moonscript") == 0)
loadDemo(console, tic_script_moon);
else if(strcmp(param, "js") == 0 || strcmp(param, "javascript") == 0)
loadDemo(console, tic_script_js);
else
{
printError(console, "\nunknown parameter: ");
printError(console, param);
commandDone(console);
return;
}
}
else loadDemo(console, tic_script_lua);
printBack(console, "\nnew cart is created");
commandDone(console);
}
static void onConsoleNewCommand(Console* console, const char* param)
{
if(studioCartChanged())
{
static const char* Rows[] =
2017-09-26 08:59:34 +02:00
{
"YOU HAVE",
"UNSAVED CHANGES",
"",
"DO YOU REALLY WANT",
"TO CREATE NEW CART?",
};
confirmCommand(console, Rows, COUNT_OF(Rows), param, onConsoleNewCommandConfirmed);
}
else
{
onConsoleNewCommandConfirmed(console, param);
}
}
typedef struct
{
s32 count;
Console* console;
} PrintFileNameData;
static bool printFilename(const char* name, const char* info, s32 id, void* data, bool dir)
{
2017-11-09 18:34:03 +01:00
2017-09-26 08:59:34 +02:00
PrintFileNameData* printData = data;
Console* console = printData->console;
printLine(console);
if(dir)
{
2017-11-09 18:34:03 +01:00
2017-09-26 08:59:34 +02:00
printBack(console, "[");
printBack(console, name);
printBack(console, "]");
}
else printFront(console, name);
if(!dir)
printData->count++;
return true;
}
static void onConsoleChangeDirectory(Console* console, const char* param)
{
if(param && strlen(param))
{
if(strcmp(param, "/") == 0)
{
fsHomeDir(console->fs);
}
else if(strcmp(param, "..") == 0)
{
fsDirBack(console->fs);
}
else if(fsIsDir(console->fs, param))
{
fsChangeDir(console->fs, param);
}
else printBack(console, "\ndir doesn't exist");
}
else printBack(console, "\ninvalid dir name");
commandDone(console);
}
static void onConsoleMakeDirectory(Console* console, const char* param)
{
if(param && strlen(param))
fsMakeDir(console->fs, param);
else printBack(console, "\ninvalid dir name");
commandDone(console);
}
static void onConsoleDirCommand(Console* console, const char* param)
{
PrintFileNameData data = {0, console};
printLine(console);
fsEnumFiles(console->fs, printFilename, &data);
if(data.count == 0)
{
printBack(console, "\n\nuse ");
printFront(console, "ADD");
printBack(console, " or ");
printFront(console, "DEMO");
printBack(console, " command to add carts");
}
printLine(console);
commandDone(console);
}
#ifdef CAN_EXPORT
static void onConsoleFolderCommand(Console* console, const char* param)
{
fsOpenWorkingFolder(console->fs);
commandDone(console);
}
#endif
static void onConsoleClsCommand(Console* console, const char* param)
{
memset(console->buffer, 0, CONSOLE_BUFFER_SIZE);
memset(console->colorBuffer, TIC_COLOR_BG, CONSOLE_BUFFER_SIZE);
console->scroll.pos = 0;
console->cursor.x = console->cursor.y = 0;
commandDoneLine(console, false);
}
static void installDemoCart(FileSystem* fs, const char* name, const void* cart, s32 size)
{
u8* data = NULL;
s32 dataSize = unzip(&data, cart, size);
fsSaveFile(fs, name, data, dataSize, true);
SDL_free(data);
}
static void onConsoleInstallDemosCommand(Console* console, const char* param)
{
static const u8 DemoFire[] =
2017-09-26 08:59:34 +02:00
{
#include "../bin/assets/fire.tic.dat"
};
static const u8 DemoP3D[] =
2017-09-26 08:59:34 +02:00
{
#include "../bin/assets/p3d.tic.dat"
};
static const u8 DemoSFX[] =
2017-09-26 08:59:34 +02:00
{
#include "../bin/assets/sfx.tic.dat"
};
static const u8 DemoPalette[] =
2017-09-26 08:59:34 +02:00
{
#include "../bin/assets/palette.tic.dat"
};
static const u8 DemoFont[] =
2017-09-26 08:59:34 +02:00
{
#include "../bin/assets/font.tic.dat"
};
static const u8 DemoMusic[] =
2017-09-26 08:59:34 +02:00
{
#include "../bin/assets/music.tic.dat"
};
static const u8 GameQuest[] =
2017-09-26 08:59:34 +02:00
{
#include "../bin/assets/quest.tic.dat"
};
static const u8 GameTetris[] =
2017-09-26 08:59:34 +02:00
{
#include "../bin/assets/tetris.tic.dat"
};
static const u8 Benchmark[] =
{
#include "../bin/assets/benchmark.tic.dat"
};
2017-09-26 08:59:34 +02:00
FileSystem* fs = console->fs;
static const struct {const char* name; const u8* data; s32 size;} Demos[] =
{
{"fire.tic", DemoFire, sizeof DemoFire},
{"font.tic", DemoFont, sizeof DemoFont},
{"music.tic", DemoMusic, sizeof DemoMusic},
{"p3d.tic", DemoP3D, sizeof DemoP3D},
{"palette.tic", DemoPalette, sizeof DemoPalette},
{"quest.tic", GameQuest, sizeof GameQuest},
{"sfx.tic", DemoSFX, sizeof DemoSFX},
{"tetris.tic", GameTetris, sizeof GameTetris},
{"benchmark.tic", Benchmark, sizeof Benchmark},
2017-09-26 08:59:34 +02:00
};
printBack(console, "\nadded carts:\n\n");
for(s32 i = 0; i < COUNT_OF(Demos); i++)
{
installDemoCart(fs, Demos[i].name, Demos[i].data, Demos[i].size);
printFront(console, Demos[i].name);
printFront(console, "\n");
}
commandDone(console);
}
static void onConsoleSurfCommand(Console* console, const char* param)
{
2017-09-28 16:20:27 +02:00
gotoSurf();
2017-09-26 08:59:34 +02:00
commandDone(console);
}
static void onConsoleCodeCommand(Console* console, const char* param)
{
gotoCode();
commandDone(console);
}
2017-09-26 08:59:34 +02:00
static void onConsoleKeymapCommand(Console* console, const char* param)
{
setStudioMode(TIC_KEYMAP_MODE);
commandDone(console);
}
2017-10-27 02:43:20 +02:00
static void onConsoleVersionCommand(Console* console, const char* param)
{
printBack(console, "\n");
2017-10-31 00:25:57 +01:00
consolePrint(console, TIC_VERSION_LABEL, CONSOLE_BACK_TEXT_COLOR);
2017-10-27 02:43:20 +02:00
commandDone(console);
}
2017-09-26 08:59:34 +02:00
static void onConsoleConfigCommand(Console* console, const char* param)
{
if(param == NULL)
{
onConsoleLoadCommand(console, CONFIG_TIC_PATH);
return;
}
else if(strcmp(param, "reset") == 0)
{
console->config->reset(console->config);
printBack(console, "\nconfiguration reset :)");
}
else if(strcmp(param, "default") == 0 || strcmp(param, "default lua") == 0)
{
onConsoleLoadDemoCommand(console, DefaultLuaTicPath);
}
else if(strcmp(param, "default moon") == 0 || strcmp(param, "default moonscript") == 0)
{
onConsoleLoadDemoCommand(console, DefaultMoonTicPath);
}
else if(strcmp(param, "default js") == 0)
{
onConsoleLoadDemoCommand(console, DefaultJSTicPath);
}
else
{
printError(console, "\nunknown parameter: ");
printError(console, param);
}
commandDone(console);
}
static void onFileDownloaded(GetResult result, void* data)
{
Console* console = (Console*)data;
if(result == FS_FILE_NOT_DOWNLOADED)
printBack(console, "\nfile not downloaded :|");
else if (result == FS_FILE_DOWNLOADED)
printBack(console, "\nfile downloaded :)");
commandDone(console);
}
static void onImportCover(const char* name, const void* buffer, size_t size, void* data)
{
Console* console = (Console*)data;
if(name)
{
static const char GifExt[] = ".gif";
const char* pos = strstr(name, GifExt);
if(pos && strcmp(pos, GifExt) == 0)
{
gif_image* image = gif_read_data(buffer, (s32)size);
if (image)
{
enum
{
Width = TIC80_WIDTH,
Height = TIC80_HEIGHT,
Size = Width * Height,
};
if(image->width == Width && image->height == Height)
{
if(size <= sizeof console->tic->cart.cover.data)
{
console->tic->cart.cover.size = size;
SDL_memcpy(console->tic->cart.cover.data, buffer, size);
printLine(console);
printBack(console, name);
printBack(console, " successfully imported");
2017-09-26 08:59:34 +02:00
}
else printError(console, "\ncover image too big :(");
}
else printError(console, "\ncover image must be 240x136 :(");
gif_close(image);
}
else printError(console, "\nfile importing error :(");
2017-09-26 08:59:34 +02:00
}
else printBack(console, "\nonly .gif files can be imported :|");
2017-09-26 08:59:34 +02:00
}
else printBack(console, "\nfile not imported :|");
commandDone(console);
}
static void onImportSprites(const char* name, const void* buffer, size_t size, void* data)
{
Console* console = (Console*)data;
if(name)
{
static const char GifExt[] = ".gif";
const char* pos = strstr(name, GifExt);
if(pos && strcmp(pos, GifExt) == 0)
{
gif_image* image = gif_read_data(buffer, (s32)size);
if (image)
{
enum
{
Width = TIC_SPRITESHEET_SIZE,
Height = TIC_SPRITESHEET_SIZE*2,
};
s32 w = SDL_min(Width, image->width);
s32 h = SDL_min(Height, image->height);
for (s32 y = 0; y < h; y++)
for (s32 x = 0; x < w; x++)
{
u8 src = image->buffer[x + y * image->width];
const gif_color* c = &image->palette[src];
tic_rgb rgb = {c->r, c->g, c->b};
2017-12-14 13:05:43 +01:00
u8 color = tic_tool_find_closest_color(console->tic->cart.palette.colors, &rgb);
2017-09-26 08:59:34 +02:00
2017-12-14 13:37:05 +01:00
setSpritePixel(getBankTiles()->data, x, y, color);
2017-09-26 08:59:34 +02:00
}
gif_close(image);
printLine(console);
printBack(console, name);
printBack(console, " successfully imported");
}
else printError(console, "\nfile importing error :(");
}
else printBack(console, "\nonly .gif files can be imported :|");
2017-09-26 08:59:34 +02:00
}
else printBack(console, "\nfile not imported :|");
commandDone(console);
}
static void injectMap(Console* console, const void* buffer, s32 size)
{
enum {Size = sizeof(tic_map)};
2017-12-14 13:37:05 +01:00
SDL_memset(getBankMap(), 0, Size);
SDL_memcpy(getBankMap(), buffer, SDL_min(size, Size));
}
2017-09-26 08:59:34 +02:00
static void onImportMap(const char* name, const void* buffer, size_t size, void* data)
{
Console* console = (Console*)data;
if(name && buffer && size <= sizeof(tic_map))
2017-09-26 08:59:34 +02:00
{
injectMap(console, buffer, size);
2017-09-26 08:59:34 +02:00
printLine(console);
printBack(console, "map successfully imported");
}
else printBack(console, "\nfile not imported :|");
commandDone(console);
}
static void onConsoleImportCommand(Console* console, const char* param)
{
if(param == NULL)
{
printBack(console, "\nusage: import sprites|cover|map");
commandDone(console);
}
else if(param && strcmp(param, "sprites") == 0)
fsOpenFileData(onImportSprites, console);
else if(param && strcmp(param, "map") == 0)
fsOpenFileData(onImportMap, console);
else if(param && strcmp(param, "cover") == 0)
fsOpenFileData(onImportCover, console);
else
{
printError(console, "\nunknown parameter: ");
printError(console, param);
commandDone(console);
}
}
static void onSpritesExported(GetResult result, void* data)
{
Console* console = (Console*)data;
if(result == FS_FILE_NOT_DOWNLOADED)
printBack(console, "\nsprites not exported :|");
else if (result == FS_FILE_DOWNLOADED)
printBack(console, "\nsprites successfully exported :)");
commandDone(console);
}
static void onCoverExported(GetResult result, void* data)
{
Console* console = (Console*)data;
if(result == FS_FILE_NOT_DOWNLOADED)
printBack(console, "\ncover image not exported :|");
else if (result == FS_FILE_DOWNLOADED)
printBack(console, "\ncover image successfully exported :)");
commandDone(console);
}
static void exportCover(Console* console)
{
tic_cover_image* cover = &console->tic->cart.cover;
if(cover->size)
{
void* data = SDL_malloc(cover->size);
memcpy(data, cover->data, cover->size);
fsGetFileData(onCoverExported, "cover.gif", data, cover->size, DEFAULT_CHMOD, console);
}
else
{
printBack(console, "\ncover image is empty, run game and\npress [F7] to assign cover image");
commandDone(console);
2017-09-26 08:59:34 +02:00
}
}
static void exportSprites(Console* console)
{
enum
{
Width = TIC_SPRITESHEET_SIZE,
Height = TIC_SPRITESHEET_SIZE*2,
};
2017-12-07 10:01:17 +01:00
enum{ Size = Width * Height * sizeof(gif_color)};
u8* buffer = (u8*)SDL_malloc(Size);
2017-09-26 08:59:34 +02:00
if(buffer)
{
u8* data = (u8*)SDL_malloc(Width * Height);
if(data)
{
for (s32 y = 0; y < Height; y++)
for (s32 x = 0; x < Width; x++)
2017-12-14 13:37:05 +01:00
data[x + y * Width] = getSpritePixel(getBankTiles()->data, x, y);
2017-09-26 08:59:34 +02:00
2017-12-07 10:01:17 +01:00
s32 size = 0;
2017-09-26 08:59:34 +02:00
if((size = writeGifData(console->tic, buffer, data, Width, Height)))
{
// buffer will be freed inside fsGetFileData
fsGetFileData(onSpritesExported, "sprites.gif", buffer, size, DEFAULT_CHMOD, console);
}
else
{
printError(console, "\nsprite export error :(");
commandDone(console);
SDL_free(buffer);
}
SDL_free(data);
}
}
}
static void onMapExported(GetResult result, void* data)
{
Console* console = (Console*)data;
if(result == FS_FILE_NOT_DOWNLOADED)
printBack(console, "\nmap not exported :|");
else if (result == FS_FILE_DOWNLOADED)
printBack(console, "\nmap successfully exported :)");
commandDone(console);
}
static void exportMap(Console* console)
{
enum{Size = sizeof(tic_map)};
void* buffer = SDL_malloc(Size);
if(buffer)
{
2017-12-14 13:37:05 +01:00
SDL_memcpy(buffer, getBankMap()->data, Size);
2017-09-26 08:59:34 +02:00
fsGetFileData(onMapExported, "world.map", buffer, Size, DEFAULT_CHMOD, console);
}
}
2017-11-20 07:14:37 +01:00
#if defined(__EMSCRIPTEN__)
2017-09-26 08:59:34 +02:00
static void onConsoleExportCommand(Console* console, const char* param)
{
if(param == NULL || (param && strcmp(param, "native") == 0) || (param && strcmp(param, "html") == 0))
{
printBack(console, "\nweb/arm version doesn't support html or\nnative export");
printBack(console, "\nusage: export sprites|cover|map");
commandDone(console);
}
else if(param && strcmp(param, "sprites") == 0)
exportSprites(console);
else if(param && strcmp(param, "map") == 0)
exportMap(console);
else if(param && strcmp(param, "cover") == 0)
exportCover(console);
else
{
printError(console, "\nunknown parameter: ");
printError(console, param);
commandDone(console);
}
}
#else
#if !defined(__ANDROID__) && !defined(__MACOSX__) && !defined(__LINUX__)
static void *memmem(const void* haystack, size_t hlen, const void* needle, size_t nlen)
{
const u8* p = haystack;
size_t plen = hlen;
if(!nlen) return NULL;
s32 needle_first = *(u8*)needle;
while (plen >= nlen && (p = memchr(p, needle_first, plen - nlen + 1)))
{
if (!memcmp(p, needle, nlen))
return (void *)p;
p++;
plen = hlen - (p - (const u8*)haystack);
}
return NULL;
}
#endif
typedef struct
{
Console* console;
const char* cartName;
} AppFileReadParam;
typedef struct
2017-09-26 08:59:34 +02:00
{
u8* data;
size_t size;
} MemoryBuffer;
static void writeMemoryData(MemoryBuffer* memory, const u8* src, size_t size)
{
memcpy(memory->data + memory->size, src, size);
memory->size += size;
}
static void writeMemoryString(MemoryBuffer* memory, const char* str)
{
size_t size = strlen(str);
memcpy(memory->data + memory->size, str, size);
memory->size += size;
}
static void onConsoleExportHtmlCommand(Console* console, const char* name)
{
2017-11-08 12:07:23 +01:00
tic_mem* tic = console->tic;
2017-09-26 08:59:34 +02:00
char cartName[FILENAME_MAX];
strcpy(cartName, name);
{
static const char HtmlExt[] = ".html";
const char* pos = NULL;
if((pos = strstr(name, HtmlExt)) && strcmp(pos, HtmlExt) == 0);
else strcat(cartName, HtmlExt);
}
extern const u8 EmbedIndexZip[];
extern const s32 EmbedIndexZipSize;
extern const u8 EmbedTicJsZip[];
extern const s32 EmbedTicJsZipSize;
static const char Placeholder[] = "<script async type=\"text/javascript\" src=\"tic.js\"></script>";
u8* EmbedIndex = NULL;
u32 EmbedIndexSize = unzip(&EmbedIndex, EmbedIndexZip, EmbedIndexZipSize);
u8* EmbedTicJs = NULL;
u32 EmbedTicJsSize = unzip(&EmbedTicJs, EmbedTicJsZip, EmbedTicJsZipSize);
u8* ptr = memmem(EmbedIndex, EmbedIndexSize, Placeholder, sizeof(Placeholder)-1);
if(ptr)
{
MemoryBuffer output = {(u8*)SDL_malloc(EmbedTicJsSize * 2), 0};
if(output.data)
{
writeMemoryData(&output, EmbedIndex, ptr - EmbedIndex);
writeMemoryString(&output, "<script type='text/javascript'>\n");
u8* buffer = (u8*)SDL_malloc(sizeof(tic_cartridge));
2017-09-26 08:59:34 +02:00
if(buffer)
{
writeMemoryString(&output, "var cartridge = [");
2017-11-21 15:31:01 +01:00
s32 size = tic->api.save(&tic->cart, buffer);
2017-09-26 08:59:34 +02:00
2017-11-08 12:07:23 +01:00
if(size)
2017-09-26 08:59:34 +02:00
{
2017-11-08 12:07:23 +01:00
// zip buffer
{
unsigned long outSize = sizeof(tic_cartridge);
u8* output = (u8*)SDL_malloc(outSize);
compress2(output, &outSize, buffer, size, Z_BEST_COMPRESSION);
SDL_free(buffer);
buffer = output;
size = outSize;
}
2017-09-26 08:59:34 +02:00
{
2017-11-08 12:07:23 +01:00
u8* ptr = buffer;
u8* end = ptr + size;
char value[] = "999,";
while(ptr != end)
{
sprintf(value, "%i,", *ptr++);
writeMemoryString(&output, value);
}
2017-09-26 08:59:34 +02:00
}
2017-11-08 12:07:23 +01:00
SDL_free(buffer);
2017-09-26 08:59:34 +02:00
2017-11-08 12:07:23 +01:00
writeMemoryString(&output, "];\n");
2017-09-26 08:59:34 +02:00
2017-11-08 12:07:23 +01:00
writeMemoryData(&output, EmbedTicJs, EmbedTicJsSize);
writeMemoryString(&output, "</script>\n");
2017-09-26 08:59:34 +02:00
2017-11-08 12:07:23 +01:00
ptr += sizeof(Placeholder)-1;
writeMemoryData(&output, ptr, EmbedIndexSize - (ptr - EmbedIndex));
2017-09-26 08:59:34 +02:00
2017-11-08 12:07:23 +01:00
fsGetFileData(onFileDownloaded, cartName, output.data, output.size, DEFAULT_CHMOD, console);
}
2017-09-26 08:59:34 +02:00
}
}
2017-09-26 08:59:34 +02:00
}
SDL_free(EmbedIndex);
SDL_free(EmbedTicJs);
}
#ifdef CAN_EXPORT
// static void* embedCart(Console* console, s32* size)
// {
// tic_mem* tic = console->tic;
2017-09-26 08:59:34 +02:00
// void* data = fsReadFile(console->appPath, size);
2017-11-21 15:31:01 +01:00
// if(data)
// {
// void* start = memmem(data, *size, embed.prefix, sizeof(embed.prefix));
2017-09-26 08:59:34 +02:00
// if(start)
// {
// embed.yes = true;
// SDL_memcpy(&embed.file, &tic->cart, sizeof(tic_cartridge));
// SDL_memcpy(start, &embed, sizeof(embed));
// embed.yes = false;
// }
2017-11-21 15:31:01 +01:00
// return data;
// }
2017-11-21 15:31:01 +01:00
// return NULL;
// }
2017-09-26 08:59:34 +02:00
#if defined(__WINDOWS__)
// static const char* getFileFolder(const char* path)
// {
// static char folder[FILENAME_MAX];
2017-09-26 08:59:34 +02:00
// const char* pos = strrchr(path, '\\');
2017-09-26 08:59:34 +02:00
// if(!pos)
// pos = strrchr(path, '/');
2017-09-26 08:59:34 +02:00
// if(pos)
// {
// s32 size = pos - path;
// memcpy(folder, path, size);
// folder[size] = 0;
2017-09-26 08:59:34 +02:00
// return folder;
// }
2017-09-26 08:59:34 +02:00
// return NULL;
// }
2017-09-26 08:59:34 +02:00
// static bool exportToFolder(Console* console, const char* folder, const char* file)
// {
// const char* workFolder = getFileFolder(console->appPath);
2017-09-26 08:59:34 +02:00
// if(workFolder)
// {
// char src[FILENAME_MAX];
// strcpy(src, workFolder);
// strcat(src, file);
2017-09-26 08:59:34 +02:00
// char dst[FILENAME_MAX];
// strcpy(dst, folder);
// strcat(dst, file);
2017-09-26 08:59:34 +02:00
// return fsCopyFile(src, dst);
// }
2017-09-26 08:59:34 +02:00
// return NULL;
// }
2017-09-26 08:59:34 +02:00
// static void onConsoleExportNativeCommand(Console* console, const char* cartName)
// {
// const char* folder = folder_dialog(console);
// bool done = false;
2017-09-26 08:59:34 +02:00
// if(folder)
// {
// s32 size = 0;
2017-09-26 08:59:34 +02:00
// void* data = embedCart(console, &size);
2017-09-26 08:59:34 +02:00
// if(data)
// {
// char path[FILENAME_MAX];
// strcpy(path, folder);
// strcat(path, "\\game.exe");
2017-09-26 08:59:34 +02:00
// done = fsWriteFile(path, data, size);
2017-09-26 08:59:34 +02:00
// SDL_free(data);
// }
// else
// {
// printBack(console, "\ngame exporting error :(");
// }
// }
2017-09-26 08:59:34 +02:00
// if(done && exportToFolder(console, folder, "\\tic80.dll") &&
// exportToFolder(console, folder, "\\SDL2.dll"))
// printBack(console, "\ngame exported :)");
// else printBack(console, "\ngame not exported :|");
2017-09-26 08:59:34 +02:00
// commandDone(console);
// }
2017-09-26 08:59:34 +02:00
#else
static void onConsoleExportNativeCommand(Console* console, const char* cartName)
{
s32 size = 0;
void* data = embedCart(console, &size);
if(data)
{
fsGetFileData(onFileDownloaded, cartName, data, size, DEFAULT_CHMOD, console);
}
else
{
onFileDownloaded(FS_FILE_NOT_DOWNLOADED, console);
}
}
#endif
#endif
static const char* getExportName(Console* console, bool html)
{
static char name[FILENAME_MAX];
if(strlen(console->romName))
{
memset(name, 0, sizeof name);
memcpy(name, console->romName, strstr(console->romName, ".tic") - console->romName);
}
else
{
strcpy(name, "game");
}
if(html)
strcat(name, ".html");
else
{
#if defined(__WINDOWS__)
strcat(name, ExeExt);
#endif
}
return name;
}
static void onConsoleExportCommand(Console* console, const char* param)
{
if(param)
{
if(strcmp(param, "html") == 0) onConsoleExportHtmlCommand(console, getExportName(console, true));
else
{
if(strcmp(param, "native") == 0)
2017-09-26 08:59:34 +02:00
{
// #ifdef CAN_EXPORT
// onConsoleExportNativeCommand(console, getExportName(console, false));
// #else
2017-09-26 08:59:34 +02:00
printBack(console, "\nnative export isn't supported on this platform\n");
commandDone(console);
// #endif
2017-09-26 08:59:34 +02:00
}
else if(strcmp(param, "sprites") == 0)
{
exportSprites(console);
}
else if(strcmp(param, "map") == 0)
{
exportMap(console);
}
else if(strcmp(param, "cover") == 0)
{
exportCover(console);
}
else
{
printError(console, "\nunknown parameter: ");
printError(console, param);
commandDone(console);
}
}
}
else
{
onConsoleExportHtmlCommand(console, getExportName(console, true));
}
}
#endif
2017-11-19 16:41:39 +01:00
static CartSaveResult saveCartName(Console* console, const char* name)
2017-11-10 17:40:44 +01:00
{
2017-11-19 16:41:39 +01:00
tic_mem* tic = console->tic;
2017-11-10 17:40:44 +01:00
2017-11-19 16:41:39 +01:00
bool success = false;
2017-11-10 14:38:19 +01:00
2017-11-19 16:41:39 +01:00
if(name && strlen(name))
2017-11-10 14:38:19 +01:00
{
2017-11-19 16:41:39 +01:00
u8* buffer = (u8*)SDL_malloc(sizeof(tic_cartridge) * 3);
2017-11-10 14:38:19 +01:00
2017-11-19 16:41:39 +01:00
if(buffer)
2017-09-26 08:59:34 +02:00
{
if(strcmp(name, CONFIG_TIC_PATH) == 0)
{
console->config->save(console->config);
studioRomSaved();
2017-09-26 08:59:34 +02:00
return CART_SAVE_OK;
}
else
{
2017-11-19 16:41:39 +01:00
s32 size = 0;
2017-09-26 08:59:34 +02:00
2017-11-19 16:41:39 +01:00
#if defined(TIC80_PRO)
if(hasProjectExt(name))
2017-11-19 16:41:39 +01:00
{
size = saveProject(console, buffer, projectComment(name));
2017-11-19 16:41:39 +01:00
}
else
#endif
2017-11-19 16:41:39 +01:00
{
name = getCartName(name);
size = tic->api.save(&tic->cart, buffer);
}
2017-09-26 08:59:34 +02:00
if(size && fsSaveFile(console->fs, name, buffer, size, true))
{
setCartName(console, name);
2017-09-26 08:59:34 +02:00
success = true;
studioRomSaved();
}
2017-09-26 08:59:34 +02:00
}
SDL_free(buffer);
2017-09-26 08:59:34 +02:00
}
}
else if (strlen(console->romName))
{
return saveCartName(console, console->romName);
}
else return CART_SAVE_MISSING_NAME;
2017-09-26 08:59:34 +02:00
return success ? CART_SAVE_OK : CART_SAVE_ERROR;
}
static CartSaveResult saveCart(Console* console)
{
return saveCartName(console, NULL);
}
static void onConsoleSaveCommandConfirmed(Console* console, const char* param)
{
CartSaveResult rom = saveCartName(console, param);
if(rom == CART_SAVE_OK)
{
printBack(console, "\ncart ");
printFront(console, console->romName);
printBack(console, " saved!\n");
}
else if(rom == CART_SAVE_MISSING_NAME)
2017-09-26 08:59:34 +02:00
printBack(console, "\ncart name is missing\n");
else
2017-09-26 08:59:34 +02:00
printBack(console, "\ncart saving error");
commandDone(console);
}
static void onConsoleSaveCommand(Console* console, const char* param)
{
2017-11-10 17:40:44 +01:00
if(param && strlen(param) && fsExistsFile(console->fs, getCartName(param)))
2017-09-26 08:59:34 +02:00
{
static const char* Rows[] =
2017-09-26 08:59:34 +02:00
{
"THE CART",
"ALREADY EXISTS",
"",
"DO YOU WANT TO",
"OVERWRITE IT?",
};
confirmCommand(console, Rows, COUNT_OF(Rows), param, onConsoleSaveCommandConfirmed);
}
else
{
onConsoleSaveCommandConfirmed(console, param);
}
}
static void onConsoleRunCommand(Console* console, const char* param)
{
commandDone(console);
console->tic->api.reset(console->tic);
setStudioMode(TIC_RUN_MODE);
}
static void onConsoleResumeCommand(Console* console, const char* param)
{
commandDone(console);
console->tic->api.resume(console->tic);
2017-12-14 09:50:50 +01:00
console->tic->api.sync(console->tic, NULL, 0, false);
2017-09-26 08:59:34 +02:00
setStudioMode(TIC_RUN_MODE);
}
static void onAddFile(const char* name, AddResult result, void* data)
{
Console* console = (Console*)data;
printLine(console);
switch(result)
{
case FS_FILE_EXISTS:
printBack(console, "file ");
printFront(console, name);
printBack(console, " already exists :|");
break;
case FS_FILE_ADDED:
printBack(console, "file ");
printFront(console, name);
printBack(console, " is successfully added :)");
break;
default:
printBack(console, "file not added :(");
break;
}
commandDone(console);
}
static void onConsoleAddCommand(Console* console, const char* param)
{
fsAddFile(console->fs, onAddFile, console);
}
static void onConsoleGetCommand(Console* console, const char* param)
{
if(param)
{
fsGetFile(console->fs, onFileDownloaded, param, console);
return;
}
else printBack(console, "\nfile name is missing");
commandDone(console);
}
static void onConsoleDelCommandConfirmed(Console* console, const char* param)
{
if(param && strlen(param))
{
if(fsIsDir(console->fs, param))
{
printBack(console, fsDeleteDir(console->fs, param)
? "\ndir not deleted"
2017-09-26 08:59:34 +02:00
: "\ndir successfully deleted");
}
else
{
printBack(console, fsDeleteFile(console->fs, param)
? "\nfile not deleted"
2017-09-26 08:59:34 +02:00
: "\nfile successfully deleted");
}
}
else printBack(console, "\nname is missing");
2017-09-26 08:59:34 +02:00
commandDone(console);
}
static void onConsoleDelCommand(Console* console, const char* param)
{
static const char* Rows[] =
2017-09-26 08:59:34 +02:00
{
"", "",
"DO YOU REALLY WANT",
"TO DELETE FILE?",
};
confirmCommand(console, Rows, COUNT_OF(Rows), param, onConsoleDelCommandConfirmed);
}
static void printTable(Console* console, const char* text)
{
printf("%s", text);
const char* textPointer = text;
const char* endText = textPointer + strlen(text);
while(textPointer != endText)
{
char symbol = *textPointer++;
scrollConsole(console);
if(symbol == '\n')
{
console->cursor.x = 0;
console->cursor.y++;
}
else
2017-09-26 08:59:34 +02:00
{
s32 offset = console->cursor.x + console->cursor.y * CONSOLE_BUFFER_WIDTH;
*(console->buffer + offset) = symbol;
u8 color = 0;
switch(symbol)
{
case '+':
case '|':
case '-':
2017-10-20 09:37:30 +02:00
color = (tic_color_gray);
2017-09-26 08:59:34 +02:00
break;
default:
2017-10-20 09:37:30 +02:00
color = (tic_color_white);
2017-09-26 08:59:34 +02:00
}
*(console->colorBuffer + offset) = color;
console->cursor.x++;
if(console->cursor.x >= CONSOLE_BUFFER_WIDTH)
{
console->cursor.x = 0;
console->cursor.y++;
}
}
}
}
static void printRamInfo(Console* console, s32 addr, const char* name, s32 size)
{
char buf[STUDIO_TEXT_BUFFER_WIDTH];
sprintf(buf, "\n| %05X | %-17s | %-5i |", addr, name, size);
printTable(console, buf);
}
static void onConsoleRamCommand(Console* console, const char* param)
{
printLine(console);
printTable(console, "\n+-----------------------------------+" \
"\n| 80K RAM LAYOUT |" \
"\n+-------+-------------------+-------+" \
"\n| ADDR | INFO | SIZE |" \
"\n+-------+-------------------+-------+");
2017-11-11 15:15:06 +01:00
static const struct{s32 addr; const char* info;} Layout[] =
{
{offsetof(tic_ram, vram.screen), "SCREEN"},
{offsetof(tic_ram, vram.palette), "PALETTE"},
{offsetof(tic_ram, vram.mapping), "PALETTE MAP"},
2017-12-10 11:02:13 +01:00
{offsetof(tic_ram, vram.vars.colors), "BORDER"},
2017-11-11 15:15:06 +01:00
{offsetof(tic_ram, vram.vars.offset), "SCREEN OFFSET"},
{offsetof(tic_ram, vram.vars.mask), "GAMEPAD MASK"},
{offsetof(tic_ram, vram.input.gamepad), "GAMEPAD"},
{offsetof(tic_ram, vram.input.reserved), "..."},
2017-12-10 09:32:49 +01:00
{offsetof(tic_ram, tiles), "TILES"},
{offsetof(tic_ram, sprites), "SPRITES"},
{offsetof(tic_ram, map), "MAP"},
2017-11-11 15:15:06 +01:00
{offsetof(tic_ram, persistent), "PERSISTENT MEMORY"},
{offsetof(tic_ram, registers), "SOUND REGISTERS"},
2017-12-10 10:12:09 +01:00
{offsetof(tic_ram, sfx.waveform), "WAVEFORMS"},
2017-12-15 09:42:25 +01:00
{offsetof(tic_ram, sfx.samples), "SFX"},
2017-12-10 10:12:09 +01:00
{offsetof(tic_ram, music.patterns.data), "MUSIC PATTERNS"},
{offsetof(tic_ram, music.tracks.data), "MUSIC TRACKS"},
2017-11-11 15:15:06 +01:00
{offsetof(tic_ram, music_pos), "MUSIC POS"},
{TIC_RAM_SIZE, "..."},
2017-09-26 08:59:34 +02:00
};
enum{Last = COUNT_OF(Layout)-1};
for(s32 i = 0; i < Last; i++)
printRamInfo(console, Layout[i].addr, Layout[i].info, Layout[i+1].addr-Layout[i].addr);
printRamInfo(console, Layout[Last].addr, Layout[Last].info, 0);
printTable(console, "\n+-------+-------------------+-------+");
printLine(console);
commandDone(console);
}
static const struct
2017-09-26 08:59:34 +02:00
{
const char* command;
const char* alt;
const char* info;
void(*handler)(Console*, const char*);
} AvailableConsoleCommands[] =
{
{"help", NULL, "show this info", onConsoleHelpCommand},
{"ram", NULL, "show memory info", onConsoleRamCommand},
2017-12-07 15:13:05 +01:00
{"exit", "quit", "exit the application", onConsoleExitCommand},
2017-09-26 08:59:34 +02:00
{"new", NULL, "create new cart", onConsoleNewCommand},
{"load", NULL, "load cart", onConsoleLoadCommand},
{"save", NULL, "save cart", onConsoleSaveCommand},
{"run", NULL, "run loaded cart", onConsoleRunCommand},
{"resume", NULL, "resume run cart", onConsoleResumeCommand},
{"dir", "ls", "show list of files", onConsoleDirCommand},
{"cd", NULL, "change directory", onConsoleChangeDirectory},
{"mkdir", NULL, "make directory", onConsoleMakeDirectory},
#ifdef CAN_EXPORT
{"folder", NULL, "open working folder in OS", onConsoleFolderCommand},
#endif
{"add", NULL, "add file", onConsoleAddCommand},
{"get", NULL, "download file", onConsoleGetCommand},
{"export", NULL, "export html or native game", onConsoleExportCommand},
{"import", NULL, "import sprites from .gif", onConsoleImportCommand},
{"del", NULL, "delete file or dir", onConsoleDelCommand},
{"cls", NULL, "clear screen", onConsoleClsCommand},
{"demo", NULL, "install demo carts", onConsoleInstallDemosCommand},
{"config", NULL, "edit TIC config", onConsoleConfigCommand},
{"keymap", NULL, "configure keyboard mapping", onConsoleKeymapCommand},
2017-10-27 02:43:20 +02:00
{"version", NULL, "show the current version", onConsoleVersionCommand},
{"edit", NULL, "open cart editor", onConsoleCodeCommand},
2017-09-26 08:59:34 +02:00
{"surf", NULL, "open carts browser", onConsoleSurfCommand},
};
static bool predictFilename(const char* name, const char* info, s32 id, void* data, bool dir)
{
char* buffer = (char*)data;
if(strstr(name, buffer) == name)
{
strcpy(buffer, name);
return false;
}
return true;
}
static void processConsoleTab(Console* console)
{
char* input = console->inputBuffer;
if(strlen(input))
{
char* param = SDL_strchr(input, ' ');
2017-09-26 08:59:34 +02:00
if(param && strlen(++param))
{
fsEnumFiles(console->fs, predictFilename, param);
console->inputPosition = strlen(input);
}
else
{
for(s32 i = 0; i < COUNT_OF(AvailableConsoleCommands); i++)
{
const char* command = AvailableConsoleCommands[i].command;
if(strstr(command, input) == command)
{
strcpy(input, command);
console->inputPosition = strlen(input);
break;
}
}
}
2017-09-26 08:59:34 +02:00
}
}
static void toUpperStr(char* str)
{
while(*str)
{
*str = SDL_toupper(*str);
str++;
}
}
static void onConsoleHelpCommand(Console* console, const char* param)
{
printBack(console, "\navailable commands:\n\n");
size_t maxName = 0;
for(s32 i = 0; i < COUNT_OF(AvailableConsoleCommands); i++)
{
size_t len = strlen(AvailableConsoleCommands[i].command);
2017-12-07 15:13:05 +01:00
{
const char* alt = AvailableConsoleCommands[i].alt;
if(alt)
len += strlen(alt) + 1;
}
2017-09-26 08:59:34 +02:00
if(len > maxName) maxName = len;
}
char upName[64];
for(s32 i = 0; i < COUNT_OF(AvailableConsoleCommands); i++)
{
const char* command = AvailableConsoleCommands[i].command;
{
strcpy(upName, command);
toUpperStr(upName);
printFront(console, upName);
}
const char* alt = AvailableConsoleCommands[i].alt;
if(alt)
{
strcpy(upName, alt);
toUpperStr(upName);
printBack(console, "/");
printFront(console, upName);
}
size_t len = maxName - strlen(command) - (alt ? strlen(alt) : -1);
while(len--) printBack(console, " ");
printBack(console, AvailableConsoleCommands[i].info);
printLine(console);
}
printBack(console, "\npress ");
#if defined(__ANDROID__)
printFront(console, "BACK");
#else
printFront(console, "ESC");
#endif
printBack(console, " to enter UI mode\n");
commandDone(console);
}
static s32 tic_strcasecmp(const char *str1, const char *str2)
{
char a = 0;
char b = 0;
while (*str1 && *str2) {
a = SDL_toupper((unsigned char) *str1);
b = SDL_toupper((unsigned char) *str2);
if (a != b)
break;
++str1;
++str2;
}
a = SDL_toupper(*str1);
b = SDL_toupper(*str2);
return (int) ((unsigned char) a - (unsigned char) b);
}
static void processCommand(Console* console, const char* command)
{
while(*command == ' ')
command++;
char* end = (char*)command + strlen(command) - 1;
while(*end == ' ' && end > command)
*end-- = '\0';
char* param = SDL_strchr(command, ' ');
if(param)
*param++ = '\0';
if(param && !strlen(param)) param = NULL;
for(s32 i = 0; i < COUNT_OF(AvailableConsoleCommands); i++)
if(tic_strcasecmp(command, AvailableConsoleCommands[i].command) == 0 ||
2017-09-26 08:59:34 +02:00
(AvailableConsoleCommands[i].alt && tic_strcasecmp(command, AvailableConsoleCommands[i].alt) == 0))
{
if(AvailableConsoleCommands[i].handler)
{
AvailableConsoleCommands[i].handler(console, param);
command = NULL;
break;
}
}
if(command)
{
printLine(console);
printError(console, "unknown command:");
printError(console, console->inputBuffer);
commandDone(console);
}
}
static void fillInputBufferFromHistory(Console* console)
{
memset(console->inputBuffer, 0, sizeof(console->inputBuffer));
strcpy(console->inputBuffer, console->history->value);
processConsoleEnd(console);
}
static void onHistoryUp(Console* console)
{
if(console->history)
{
if(console->history->next)
console->history = console->history->next;
}
else console->history = console->historyHead;
if(console->history)
fillInputBufferFromHistory(console);
}
static void onHistoryDown(Console* console)
{
if(console->history)
{
if(console->history->prev)
console->history = console->history->prev;
fillInputBufferFromHistory(console);
}
}
static void appendHistory(Console* console, const char* value)
{
HistoryItem* item = (HistoryItem*)SDL_malloc(sizeof(HistoryItem));
item->value = SDL_strdup(value);
item->next = NULL;
item->prev = NULL;
if(console->historyHead == NULL)
{
console->historyHead = item;
return;
}
console->historyHead->prev = item;
item->next = console->historyHead;
console->historyHead = item;
console->history = NULL;
}
static void processConsoleCommand(Console* console)
{
console->inputPosition = 0;
size_t commandSize = strlen(console->inputBuffer);
if(commandSize)
{
printFront(console, console->inputBuffer);
appendHistory(console, console->inputBuffer);
processCommand(console, console->inputBuffer);
memset(console->inputBuffer, 0, sizeof(console->inputBuffer));
}
else commandDone(console);
}
static void error(Console* console, const char* info)
{
2017-10-20 09:37:30 +02:00
consolePrint(console, info ? info : "unknown error", (tic_color_red));
2017-09-26 08:59:34 +02:00
commandDone(console);
}
static void trace(Console* console, const char* text, u8 color)
{
consolePrint(console, text, color);
commandDone(console);
}
static void setScroll(Console* console, s32 val)
{
if(console->scroll.pos != val)
{
console->scroll.pos = val;
if(console->scroll.pos < 0) console->scroll.pos = 0;
if(console->scroll.pos > console->cursor.y) console->scroll.pos = console->cursor.y;
2017-09-26 08:59:34 +02:00
}
}
static void processGesture(Console* console)
{
SDL_Point point = {0, 0};
if(getGesturePos(&point))
{
if(console->scroll.active)
setScroll(console, (console->scroll.start - point.y) / STUDIO_TEXT_HEIGHT);
else
{
console->scroll.start = point.y + console->scroll.pos * STUDIO_TEXT_HEIGHT;
console->scroll.active = true;
}
}
else console->scroll.active = false;
}
static void checkNewVersion(Console* console)
{
Net* net = createNet();
if(net)
{
NetVersion version = netVersionRequest(net);
SDL_free(net);
if((version.major > TIC_VERSION_MAJOR) ||
2017-09-26 08:59:34 +02:00
(version.major == TIC_VERSION_MAJOR && version.minor > TIC_VERSION_MINOR) ||
(version.major == TIC_VERSION_MAJOR && version.minor == TIC_VERSION_MINOR && version.patch > TIC_VERSION_PATCH))
{
char msg[FILENAME_MAX] = {0};
sprintf(msg, "\n A new version %i.%i.%i is available.\n", version.major, version.minor, version.patch);
2017-10-20 09:37:30 +02:00
consolePrint(console, msg, (tic_color_light_green));
2017-09-26 08:59:34 +02:00
}
}
}
static void tick(Console* console)
{
SDL_Event* event = NULL;
while ((event = pollEvent()))
{
switch(event->type)
{
case SDL_MOUSEWHEEL:
{
enum{Scroll = 3};
s32 delta = event->wheel.y > 0 ? -Scroll : Scroll;
setScroll(console, console->scroll.pos + delta);
}
break;
case SDL_KEYDOWN:
{
switch(event->key.keysym.sym)
{
case SDLK_UP:
onHistoryUp(console);
break;
case SDLK_DOWN:
onHistoryDown(console);
break;
case SDLK_RIGHT:
{
console->inputPosition++;
size_t len = strlen(console->inputBuffer);
if(console->inputPosition > len)
console->inputPosition = len;
}
break;
case SDLK_LEFT:
{
if(console->inputPosition > 0)
console->inputPosition--;
}
break;
case SDLK_RETURN: processConsoleCommand(console); break;
case SDLK_BACKSPACE: processConsoleBackspace(console); break;
case SDLK_DELETE: processConsoleDel(console); break;
case SDLK_HOME: processConsoleHome(console); break;
case SDLK_END: processConsoleEnd(console); break;
case SDLK_TAB: processConsoleTab(console); break;
default: break;
}
scrollConsole(console);
console->cursor.delay = CONSOLE_CURSOR_DELAY;
2017-09-26 08:59:34 +02:00
}
break;
case SDL_TEXTINPUT:
{
const char* symbol = event->text.text;
if(strlen(symbol) == 1)
{
char sym = *symbol;
size_t size = strlen(console->inputBuffer);
if(size < sizeof(console->inputBuffer))
{
char* pos = console->inputBuffer + console->inputPosition;
memmove(pos + 1, pos, strlen(pos));
*(console->inputBuffer + console->inputPosition) = sym;
console->inputPosition++;
}
}
console->cursor.delay = CONSOLE_CURSOR_DELAY;
}
break;
}
}
processGesture(console);
if(console->tickCounter == 0)
{
if(!console->embed.yes)
2017-09-26 08:59:34 +02:00
{
loadDemo(console, tic_script_lua);
printBack(console, "\n hello! type ");
printFront(console, "help");
printBack(console, " for help\n");
if(getConfig()->checkNewVersion)
checkNewVersion(console);
commandDone(console);
}
else printBack(console, "\n loading cart...");
}
console->tic->api.clear(console->tic, TIC_COLOR_BG);
drawConsoleText(console);
if(console->embed.yes)
2017-09-26 08:59:34 +02:00
{
if(console->tickCounter >= (u32)(console->skipStart ? 1 : TIC_FRAMERATE))
2017-09-26 08:59:34 +02:00
{
if(!console->skipStart)
2017-09-26 08:59:34 +02:00
console->showGameMenu = true;
// memcpy(&console->tic->cart, &embed.file, sizeof(tic_cartridge));
2017-09-26 08:59:34 +02:00
setStudioMode(TIC_RUN_MODE);
console->embed.yes = false;
console->skipStart = false;
2017-09-26 08:59:34 +02:00
studioRomLoaded();
console->tic->api.reset(console->tic);
2017-09-26 08:59:34 +02:00
printLine(console);
commandDone(console);
console->active = true;
2017-09-26 08:59:34 +02:00
return;
}
}
else
{
2017-09-26 08:59:34 +02:00
if(console->cursor.delay)
console->cursor.delay--;
if(getStudioMode() != TIC_CONSOLE_MODE) return;
drawConsoleInputText(console);
}
console->tickCounter++;
if(console->startSurf)
{
console->startSurf = false;
gotoSurf();
}
2017-09-26 08:59:34 +02:00
}
static bool cmdLoadCart(Console* console, const char* name)
2017-09-26 08:59:34 +02:00
{
bool done = false;
2017-09-26 08:59:34 +02:00
s32 size = 0;
void* data = fsReadFile(name, &size);
if(data)
{
2017-11-19 14:58:54 +01:00
#if defined(TIC80_PRO)
if(hasProjectExt(name))
2017-11-19 14:58:54 +01:00
{
loadProject(console, name, data, size, console->embed.file);
setCartName(console, fsFilename(name));
console->embed.yes = true;
console->skipStart = true;
done = true;
2017-11-19 14:58:54 +01:00
}
else
#endif
2017-11-24 10:09:25 +01:00
if(hasExt(name, CART_EXT))
{
loadCart(console->tic, console->embed.file, data, size, true);
setCartName(console, fsFilename(name));
console->embed.yes = true;
done = true;
2017-11-24 10:09:25 +01:00
}
2017-11-23 15:13:58 +01:00
2017-09-26 08:59:34 +02:00
SDL_free(data);
}
return done;
2017-09-26 08:59:34 +02:00
}
static bool loadFileIntoBuffer(Console* console, char* buffer, const char* fileName)
2017-09-26 08:59:34 +02:00
{
s32 size = 0;
void* contents = fsReadFile(fileName, &size);
2017-09-26 08:59:34 +02:00
if(contents)
{
memset(buffer, 0, TIC_CODE_SIZE);
if(size > TIC_CODE_SIZE)
2017-09-26 08:59:34 +02:00
{
char messageBuffer[256];
sprintf(messageBuffer, "\n code is larger than %i symbols\n", TIC_CODE_SIZE);
2017-09-26 08:59:34 +02:00
printError(console, messageBuffer);
}
memcpy(buffer, contents, SDL_min(size, TIC_CODE_SIZE-1));
SDL_free(contents);
return true;
}
return false;
}
2017-09-26 08:59:34 +02:00
static void tryReloadCode(Console* console, char* codeBuffer)
{
if(console->codeLiveReload.active)
{
const char* fileName = console->codeLiveReload.fileName;
loadFileIntoBuffer(console, codeBuffer, fileName);
}
}
static bool cmdInjectCode(Console* console, const char* param, const char* name)
{
bool done = false;
bool watch = strcmp(param, "-code-watch") == 0;
if(watch || strcmp(param, "-code") == 0)
{
2017-12-14 13:37:05 +01:00
bool loaded = loadFileIntoBuffer(console, console->embed.file->bank0.code.data, name);
2017-09-26 08:59:34 +02:00
if(loaded)
{
console->embed.yes = true;
console->skipStart = true;
done = true;
if(watch)
{
console->codeLiveReload.active = true;
strcpy(console->codeLiveReload.fileName, name);
}
}
2017-09-26 08:59:34 +02:00
}
return done;
2017-09-26 08:59:34 +02:00
}
static bool cmdInjectSprites(Console* console, const char* param, const char* name)
2017-09-26 08:59:34 +02:00
{
bool done = false;
2017-09-26 08:59:34 +02:00
if(strcmp(param, "-sprites") == 0)
{
s32 size = 0;
void* sprites = fsReadFile(name, &size);
if(sprites)
{
2017-09-26 08:59:34 +02:00
gif_image* image = gif_read_data(sprites, size);
if (image)
{
enum
{
Width = TIC_SPRITESHEET_SIZE,
Height = TIC_SPRITESHEET_SIZE*2,
};
s32 w = SDL_min(Width, image->width);
s32 h = SDL_min(Height, image->height);
for (s32 y = 0; y < h; y++)
for (s32 x = 0; x < w; x++)
{
u8 src = image->buffer[x + y * image->width];
const gif_color* c = &image->palette[src];
tic_rgb rgb = {c->r, c->g, c->b};
2017-12-14 13:05:43 +01:00
u8 color = tic_tool_find_closest_color(console->embed.file->palette.colors, &rgb);
2017-09-26 08:59:34 +02:00
2017-12-14 13:37:05 +01:00
setSpritePixel(console->embed.file->bank0.tiles.data, x, y, color);
2017-09-26 08:59:34 +02:00
}
gif_close(image);
}
SDL_free(sprites);
console->embed.yes = true;
console->skipStart = true;
done = true;
2017-09-26 08:59:34 +02:00
}
}
return done;
2017-09-26 08:59:34 +02:00
}
static bool cmdInjectMap(Console* console, const char* param, const char* name)
2017-09-26 08:59:34 +02:00
{
bool done = false;
2017-09-26 08:59:34 +02:00
if(strcmp(param, "-map") == 0)
{
s32 size = 0;
void* map = fsReadFile(name, &size);
if(map)
{
if(size <= sizeof(tic_map))
2017-09-26 08:59:34 +02:00
{
injectMap(console, map, size);
2017-09-26 08:59:34 +02:00
console->embed.yes = true;
console->skipStart = true;
done = true;
2017-09-26 08:59:34 +02:00
}
SDL_free(map);
}
}
return done;
2017-09-26 08:59:34 +02:00
}
void initConsole(Console* console, tic_mem* tic, FileSystem* fs, Config* config, s32 argc, char **argv)
{
if(!console->buffer) console->buffer = SDL_malloc(CONSOLE_BUFFER_SIZE);
if(!console->colorBuffer) console->colorBuffer = SDL_malloc(CONSOLE_BUFFER_SIZE);
if(!console->embed.file) console->embed.file = SDL_malloc(sizeof(tic_cartridge));
2017-09-26 08:59:34 +02:00
*console = (Console)
{
.tic = tic,
.config = config,
.load = onConsoleLoadCommandConfirmed,
2017-11-19 15:44:01 +01:00
#if defined(TIC80_PRO)
.loadProject = loadProject,
2017-11-21 17:47:36 +01:00
.updateProject = updateProject,
2017-11-19 15:44:01 +01:00
#else
.loadProject = NULL,
2017-11-21 17:47:36 +01:00
.updateProject = NULL,
2017-11-19 15:44:01 +01:00
#endif
2017-09-26 08:59:34 +02:00
.error = error,
.trace = trace,
.tick = tick,
.save = saveCart,
.cursor = {.x = 0, .y = 0, .delay = 0},
.scroll =
2017-09-26 08:59:34 +02:00
{
.pos = 0,
.start = 0,
.active = false,
},
.codeLiveReload =
{
.active = false,
.reload = tryReloadCode,
},
.embed =
{
.yes = false,
.menu = false,
.file = NULL,
},
2017-09-26 08:59:34 +02:00
.inputPosition = 0,
.history = NULL,
.historyHead = NULL,
.tickCounter = 0,
.active = false,
.buffer = console->buffer,
.colorBuffer = console->colorBuffer,
.fs = fs,
.showGameMenu = false,
.startSurf = false,
.skipStart = false,
.goFullscreen = false,
2017-09-26 08:59:34 +02:00
};
memset(console->buffer, 0, CONSOLE_BUFFER_SIZE);
memset(console->colorBuffer, TIC_COLOR_BG, CONSOLE_BUFFER_SIZE);
memset(console->codeLiveReload.fileName, 0, FILENAME_MAX);
2017-09-26 08:59:34 +02:00
if(argc)
{
strcpy(console->appPath, argv[0]);
#if defined(__WINDOWS__)
if(!strstr(console->appPath, ExeExt))
strcat(console->appPath, ExeExt);
#endif
}
printFront(console, "\n " TIC_NAME_FULL "");
printBack(console, " " TIC_VERSION_LABEL "\n");
printBack(console, " " TIC_COPYRIGHT "\n");
if(argc > 1)
{
2017-12-14 13:05:43 +01:00
memcpy(console->embed.file->palette.data, tic->config.palette.data, sizeof(tic_palette));
u32 argp = 1;
// trying to load cart
for (s32 i = 1; i < argc; i++)
2017-09-26 08:59:34 +02:00
{
if(cmdLoadCart(console, argv[i]))
{
argp |= 1 << i;
break;
}
2017-09-26 08:59:34 +02:00
}
// process '-key val' params
for (s32 i = 1; i < argc-1; i++)
2017-09-26 08:59:34 +02:00
{
s32 mask = 0b11 << i;
2017-09-26 08:59:34 +02:00
if(~argp & mask)
2017-09-26 08:59:34 +02:00
{
const char* first = argv[i];
const char* second = argv[i + 1];
if(cmdInjectCode(console, first, second)
|| cmdInjectSprites(console, first, second)
|| cmdInjectMap(console, first, second))
argp |= mask;
2017-09-26 08:59:34 +02:00
}
}
// proccess single params
for (s32 i = 1; i < argc; i++)
{
if(~argp & 1 << i)
{
const char* arg = argv[i];
if(strcmp(arg, "-nosound") == 0)
config->data.noSound = true;
else if(strcmp(arg, "-surf") == 0)
console->startSurf = true;
else if(strcmp(arg, "-fullscreen") == 0)
console->goFullscreen = true;
else if(strcmp(arg, "-skip") == 0)
console->skipStart = true;
else continue;
argp |= 0b1 << i;
}
}
for (s32 i = 1; i < argc; i++)
{
if(~argp & 1 << i)
{
char buf[256];
sprintf(buf, "parameter or file not processed: %s\n", argv[i]);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Warning", buf, NULL);
}
}
2017-09-26 08:59:34 +02:00
}
#if defined(__EMSCRIPTEN__)
if(!console->embed.yes)
2017-09-26 08:59:34 +02:00
{
void* cartPtr = (void*)EM_ASM_INT_V
(
if(typeof cartridge != 'undefined' && cartridge.length)
{
var ptr = Module._malloc(cartridge.length);
Module.writeArrayToMemory(cartridge, ptr);
return ptr;
}
else return 0;
);
if(cartPtr)
{
console->embed.yes = true;
2017-09-26 08:59:34 +02:00
s32 cartSize = EM_ASM_INT_V(return cartridge.length;);
u8* data = NULL;
s32 size = unzip(&data, cartPtr, cartSize);
loadCart(tic, console->embed.file, data, size, true);
2017-09-26 08:59:34 +02:00
SDL_free(data);
EM_ASM_({Module._free($0);}, cartPtr);
}
2017-09-26 08:59:34 +02:00
}
2017-09-26 08:59:34 +02:00
#endif
console->active = !console->embed.yes;
2017-09-26 08:59:34 +02:00
}