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 "studio.h"
|
|
|
|
|
|
|
|
#include "start.h"
|
|
|
|
#include "console.h"
|
|
|
|
#include "run.h"
|
|
|
|
#include "sprite.h"
|
|
|
|
#include "map.h"
|
|
|
|
#include "world.h"
|
|
|
|
#include "sfx.h"
|
|
|
|
#include "music.h"
|
|
|
|
#include "history.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "code.h"
|
|
|
|
#include "dialog.h"
|
|
|
|
#include "menu.h"
|
|
|
|
#include "surf.h"
|
|
|
|
|
|
|
|
#include "fs.h"
|
|
|
|
|
|
|
|
#include "ext/gif.h"
|
|
|
|
#include "ext/md5.h"
|
|
|
|
|
2018-02-08 13:29:14 +01:00
|
|
|
#include <zlib.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#include <lua.h>
|
|
|
|
#include <lauxlib.h>
|
|
|
|
#include <lualib.h>
|
|
|
|
|
2017-11-06 08:57:40 +01:00
|
|
|
#define FRAME_SIZE (TIC80_FULLWIDTH * TIC80_FULLHEIGHT * sizeof(u32))
|
2017-12-08 13:03:59 +01:00
|
|
|
#define POPUP_DUR (TIC_FRAMERATE*2)
|
|
|
|
|
2017-12-14 15:19:35 +01:00
|
|
|
#if defined(TIC80_PRO)
|
|
|
|
#define TIC_EDITOR_BANKS (TIC_BANKS)
|
|
|
|
#else
|
|
|
|
#define TIC_EDITOR_BANKS 1
|
|
|
|
#endif
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
u8 data[16];
|
|
|
|
} CartHash;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
bool down;
|
|
|
|
bool click;
|
|
|
|
|
2018-02-01 17:34:42 +01:00
|
|
|
tic_point start;
|
|
|
|
tic_point end;
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
} MouseState;
|
|
|
|
|
2017-12-13 21:04:22 +01:00
|
|
|
static const EditorMode Modes[] =
|
|
|
|
{
|
|
|
|
TIC_CODE_MODE,
|
|
|
|
TIC_SPRITE_MODE,
|
|
|
|
TIC_MAP_MODE,
|
|
|
|
TIC_SFX_MODE,
|
|
|
|
TIC_MUSIC_MODE,
|
|
|
|
};
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
static struct
|
|
|
|
{
|
2018-01-24 12:01:19 +01:00
|
|
|
Studio studio;
|
2018-02-07 16:21:24 +01:00
|
|
|
System* system;
|
2018-01-24 12:01:19 +01:00
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
tic80_local* tic80local;
|
|
|
|
|
2017-11-22 08:20:12 +01:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
CartHash hash;
|
|
|
|
u64 mdate;
|
|
|
|
}cart;
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
EditorMode mode;
|
|
|
|
EditorMode prevMode;
|
|
|
|
EditorMode dialogMode;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
MouseState state[3];
|
|
|
|
} mouse;
|
|
|
|
|
2018-01-24 15:56:55 +01:00
|
|
|
tic_key keycodes[KEYMAP_COUNT];
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2017-12-13 14:02:16 +01:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
bool show;
|
2017-12-21 10:51:34 +01:00
|
|
|
bool chained;
|
2017-12-13 21:04:22 +01:00
|
|
|
|
|
|
|
union
|
|
|
|
{
|
|
|
|
struct
|
|
|
|
{
|
2017-12-21 10:51:34 +01:00
|
|
|
s8 code;
|
|
|
|
s8 sprites;
|
|
|
|
s8 map;
|
|
|
|
s8 sfx;
|
|
|
|
s8 music;
|
2017-12-13 21:04:22 +01:00
|
|
|
} index;
|
|
|
|
|
2017-12-21 10:51:34 +01:00
|
|
|
s8 indexes[COUNT_OF(Modes)];
|
2017-12-13 21:04:22 +01:00
|
|
|
};
|
|
|
|
|
2017-12-13 15:09:25 +01:00
|
|
|
} bank;
|
2017-12-13 14:02:16 +01:00
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
s32 counter;
|
2018-08-03 22:22:58 +02:00
|
|
|
char message[STUDIO_TEXT_BUFFER_WIDTH];
|
2017-09-26 08:59:34 +02:00
|
|
|
} popup;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
2018-08-03 22:22:58 +02:00
|
|
|
char text[STUDIO_TEXT_BUFFER_WIDTH];
|
2017-09-26 08:59:34 +02:00
|
|
|
} tooltip;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
bool record;
|
|
|
|
|
|
|
|
u32* buffer;
|
|
|
|
s32 frames;
|
|
|
|
s32 frame;
|
|
|
|
|
|
|
|
} video;
|
|
|
|
|
2017-12-14 15:08:04 +01:00
|
|
|
struct
|
|
|
|
{
|
2017-12-14 18:31:04 +01:00
|
|
|
Code* code;
|
|
|
|
Sprite* sprite;
|
|
|
|
Map* map;
|
|
|
|
Sfx* sfx;
|
|
|
|
Music* music;
|
|
|
|
} editor[TIC_EDITOR_BANKS];
|
2017-12-14 15:08:04 +01:00
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
struct
|
|
|
|
{
|
2017-12-13 18:14:32 +01:00
|
|
|
Start* start;
|
|
|
|
Console* console;
|
|
|
|
Run* run;
|
|
|
|
World* world;
|
|
|
|
Config* config;
|
|
|
|
Dialog* dialog;
|
|
|
|
Menu* menu;
|
|
|
|
Surf* surf;
|
2017-09-26 08:59:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
FileSystem* fs;
|
|
|
|
|
2018-01-22 10:59:34 +01:00
|
|
|
bool missedFrame;
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
s32 argc;
|
|
|
|
char **argv;
|
2018-01-24 12:01:19 +01:00
|
|
|
s32 samplerate;
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
} impl =
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
.tic80local = NULL,
|
2017-11-22 08:20:12 +01:00
|
|
|
.cart =
|
|
|
|
{
|
|
|
|
.mdate = 0,
|
|
|
|
},
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
.mode = TIC_START_MODE,
|
|
|
|
.prevMode = TIC_CODE_MODE,
|
|
|
|
.dialogMode = TIC_CONSOLE_MODE,
|
|
|
|
|
2017-10-16 05:03:05 +02:00
|
|
|
.keycodes =
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-01-24 15:56:55 +01:00
|
|
|
tic_key_up,
|
|
|
|
tic_key_down,
|
|
|
|
tic_key_left,
|
|
|
|
tic_key_right,
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-01-24 15:56:55 +01:00
|
|
|
tic_key_z, // a
|
|
|
|
tic_key_x, // b
|
|
|
|
tic_key_a, // x
|
|
|
|
tic_key_s, // y
|
2017-09-26 08:59:34 +02:00
|
|
|
},
|
|
|
|
|
2017-12-13 15:09:25 +01:00
|
|
|
.bank =
|
2017-12-13 14:02:16 +01:00
|
|
|
{
|
|
|
|
.show = false,
|
2017-12-21 10:51:34 +01:00
|
|
|
.chained = true,
|
2017-12-13 14:02:16 +01:00
|
|
|
},
|
|
|
|
|
2017-10-16 05:03:05 +02:00
|
|
|
.popup =
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
.counter = 0,
|
|
|
|
.message = "\0",
|
|
|
|
},
|
|
|
|
|
2017-10-16 05:03:05 +02:00
|
|
|
.tooltip =
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
.text = "\0",
|
|
|
|
},
|
|
|
|
|
2017-10-16 05:03:05 +02:00
|
|
|
.video =
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
.record = false,
|
|
|
|
.buffer = NULL,
|
|
|
|
.frames = 0,
|
|
|
|
},
|
|
|
|
|
2018-01-22 10:59:34 +01:00
|
|
|
.missedFrame = false,
|
2017-09-26 08:59:34 +02:00
|
|
|
.argc = 0,
|
|
|
|
.argv = NULL,
|
|
|
|
};
|
|
|
|
|
2018-01-24 14:39:29 +01:00
|
|
|
|
|
|
|
char getKeyboardText()
|
|
|
|
{
|
2018-09-04 22:22:59 +02:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2018-01-24 14:39:29 +01:00
|
|
|
|
2018-09-04 22:22:59 +02:00
|
|
|
static const char Symbols[] = " abcdefghijklmnopqrstuvwxyz0123456789-=[]\\;'`,./ ";
|
|
|
|
static const char Shift[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ)!@#$%^&*(_+{}|:\"~<>? ";
|
2018-01-24 14:39:29 +01:00
|
|
|
|
2018-09-04 22:22:59 +02:00
|
|
|
enum{Count = sizeof Symbols};
|
2018-01-24 14:39:29 +01:00
|
|
|
|
2018-09-04 22:22:59 +02:00
|
|
|
for(s32 i = 0; i < TIC80_KEY_BUFFER; i++)
|
|
|
|
{
|
|
|
|
tic_key key = tic->ram.input.keyboard.keys[i];
|
2018-01-24 14:39:29 +01:00
|
|
|
|
2018-09-04 22:22:59 +02:00
|
|
|
if(key > 0 && key < Count && tic->api.keyp(tic, key, KEYBOARD_HOLD, KEYBOARD_PERIOD))
|
|
|
|
{
|
|
|
|
bool caps = tic->api.key(tic, tic_key_capslock);
|
|
|
|
bool shift = tic->api.key(tic, tic_key_shift);
|
2018-01-24 14:39:29 +01:00
|
|
|
|
2018-09-04 22:22:59 +02:00
|
|
|
return caps
|
|
|
|
? key >= tic_key_a && key <= tic_key_z
|
|
|
|
? shift ? Symbols[key] : Shift[key]
|
|
|
|
: shift ? Shift[key] : Symbols[key]
|
|
|
|
: shift ? Shift[key] : Symbols[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2018-01-24 14:39:29 +01:00
|
|
|
}
|
|
|
|
|
2018-02-13 11:29:33 +01:00
|
|
|
bool keyWasPressed(tic_key key)
|
2018-01-24 14:59:02 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2018-01-24 14:59:02 +01:00
|
|
|
return tic->api.keyp(tic, key, KEYBOARD_HOLD, KEYBOARD_PERIOD);
|
|
|
|
}
|
|
|
|
|
2018-02-13 11:29:33 +01:00
|
|
|
bool anyKeyWasPressed()
|
2018-01-24 14:59:02 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2018-01-24 14:59:02 +01:00
|
|
|
|
|
|
|
for(s32 i = 0; i < TIC80_KEY_BUFFER; i++)
|
|
|
|
{
|
|
|
|
tic_key key = tic->ram.input.keyboard.keys[i];
|
|
|
|
|
|
|
|
if(tic->api.keyp(tic, key, KEYBOARD_HOLD, KEYBOARD_PERIOD))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-12-13 21:04:22 +01:00
|
|
|
tic_tiles* getBankTiles()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
return &impl.studio.tic->cart.banks[impl.bank.index.sprites].tiles;
|
2017-12-13 21:04:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
tic_map* getBankMap()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
return &impl.studio.tic->cart.banks[impl.bank.index.map].map;
|
2017-12-13 21:04:22 +01:00
|
|
|
}
|
|
|
|
|
2018-02-27 12:55:25 +01:00
|
|
|
tic_palette* getBankPalette()
|
|
|
|
{
|
|
|
|
return &impl.studio.tic->cart.banks[impl.bank.index.sprites].palette;
|
|
|
|
}
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
void playSystemSfx(s32 id)
|
|
|
|
{
|
2018-03-12 07:40:48 +01:00
|
|
|
const tic_sample* effect = &impl.config->cart.bank0.sfx.samples.data[id];
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.sfx_ex(impl.studio.tic, id, effect->note, effect->octave, -1, 0, MAX_VOLUME, 0);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void md5(const void* voidData, s32 length, u8* digest)
|
|
|
|
{
|
|
|
|
enum {Size = 512};
|
|
|
|
|
|
|
|
const u8* data = voidData;
|
|
|
|
|
2017-10-16 05:03:05 +02:00
|
|
|
MD5_CTX c;
|
2017-09-26 08:59:34 +02:00
|
|
|
MD5_Init(&c);
|
|
|
|
|
2017-10-16 05:03:05 +02:00
|
|
|
while (length > 0)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
MD5_Update(&c, data, length > Size ? Size: length);
|
|
|
|
|
|
|
|
length -= Size;
|
|
|
|
data += Size;
|
|
|
|
}
|
|
|
|
|
|
|
|
MD5_Final(digest, &c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8* getSpritePtr(tic_tile* tiles, s32 x, s32 y)
|
|
|
|
{
|
|
|
|
enum { SheetCols = (TIC_SPRITESHEET_SIZE / TIC_SPRITESIZE) };
|
|
|
|
return tiles[x / TIC_SPRITESIZE + y / TIC_SPRITESIZE * SheetCols].data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void setSpritePixel(tic_tile* tiles, s32 x, s32 y, u8 color)
|
|
|
|
{
|
|
|
|
// TODO: check spritesheet rect
|
|
|
|
tic_tool_poke4(getSpritePtr(tiles, x, y), (x % TIC_SPRITESIZE) + (y % TIC_SPRITESIZE) * TIC_SPRITESIZE, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 getSpritePixel(tic_tile* tiles, s32 x, s32 y)
|
|
|
|
{
|
|
|
|
// TODO: check spritesheet rect
|
|
|
|
return tic_tool_peek4(getSpritePtr(tiles, x, y), (x % TIC_SPRITESIZE) + (y % TIC_SPRITESIZE) * TIC_SPRITESIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void toClipboard(const void* data, s32 size, bool flip)
|
|
|
|
{
|
|
|
|
if(data)
|
|
|
|
{
|
|
|
|
enum {Len = 2};
|
|
|
|
|
2018-02-06 20:15:56 +01:00
|
|
|
char* clipboard = (char*)malloc(size*Len + 1);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
if(clipboard)
|
|
|
|
{
|
|
|
|
char* ptr = clipboard;
|
|
|
|
|
|
|
|
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];
|
2017-10-16 05:03:05 +02:00
|
|
|
ptr[1] = tmp;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-14 12:52:10 +01:00
|
|
|
getSystem()->setClipboardText(clipboard);
|
2018-02-06 20:15:56 +01:00
|
|
|
free(clipboard);
|
2017-10-16 05:03:05 +02:00
|
|
|
}
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-11 12:44:41 +01:00
|
|
|
void str2buf(const char* str, s32 size, void* buf, bool flip)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
char val[] = "0x00";
|
|
|
|
const char* ptr = str;
|
|
|
|
|
|
|
|
for(s32 i = 0; i < size/2; i++)
|
|
|
|
{
|
|
|
|
if(flip)
|
|
|
|
{
|
|
|
|
val[3] = *ptr++;
|
|
|
|
val[2] = *ptr++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-10-16 05:03:05 +02:00
|
|
|
val[2] = *ptr++;
|
2017-09-26 08:59:34 +02:00
|
|
|
val[3] = *ptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
((u8*)buf)[i] = (u8)strtol(val, NULL, 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-11 10:46:52 +01:00
|
|
|
static void removeWhiteSpaces(char* str)
|
|
|
|
{
|
|
|
|
s32 i = 0;
|
|
|
|
s32 len = strlen(str);
|
|
|
|
|
|
|
|
for (s32 j = 0; j < len; j++)
|
2018-02-06 20:15:56 +01:00
|
|
|
if(!isspace(str[j]))
|
2017-12-11 10:46:52 +01:00
|
|
|
str[i++] = str[j];
|
|
|
|
|
|
|
|
str[i] = '\0';
|
2017-12-10 19:00:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool fromClipboard(void* data, s32 size, bool flip, bool remove_white_spaces)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
if(data)
|
|
|
|
{
|
2018-02-14 12:52:10 +01:00
|
|
|
if(getSystem()->hasClipboardText())
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-14 12:52:10 +01:00
|
|
|
char* clipboard = getSystem()->getClipboardText();
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
if(clipboard)
|
|
|
|
{
|
2017-12-11 10:46:52 +01:00
|
|
|
if (remove_white_spaces)
|
|
|
|
removeWhiteSpaces(clipboard);
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
bool valid = strlen(clipboard) == size * 2;
|
|
|
|
|
2017-11-11 12:44:41 +01:00
|
|
|
if(valid) str2buf(clipboard, strlen(clipboard), data, flip);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-03-01 16:59:41 +01:00
|
|
|
getSystem()->freeClipboardText(clipboard);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
return valid;
|
|
|
|
}
|
2017-10-16 05:03:05 +02:00
|
|
|
}
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void showTooltip(const char* text)
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
strncpy(impl.tooltip.text, text, sizeof impl.tooltip.text - 1);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2017-12-13 14:02:16 +01:00
|
|
|
static void drawExtrabar(tic_mem* tic)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
enum {Size = 7};
|
|
|
|
|
2018-08-03 22:22:58 +02:00
|
|
|
s32 x = (COUNT_OF(Modes) + 1) * Size + 17 * TIC_FONT_WIDTH;
|
2017-09-26 08:59:34 +02:00
|
|
|
s32 y = 0;
|
|
|
|
|
|
|
|
static const u8 Icons[] =
|
|
|
|
{
|
|
|
|
0b00000000,
|
|
|
|
0b00101000,
|
|
|
|
0b00101000,
|
|
|
|
0b00010000,
|
|
|
|
0b01101100,
|
|
|
|
0b01101100,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
|
|
|
|
0b00000000,
|
|
|
|
0b01111000,
|
|
|
|
0b01001000,
|
|
|
|
0b01011100,
|
|
|
|
0b01110100,
|
|
|
|
0b00011100,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
|
|
|
|
0b00000000,
|
|
|
|
0b00111000,
|
|
|
|
0b01000100,
|
|
|
|
0b01111100,
|
|
|
|
0b01101100,
|
|
|
|
0b01111100,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
|
|
|
|
0b00000000,
|
|
|
|
0b00011000,
|
|
|
|
0b00110000,
|
|
|
|
0b01111100,
|
|
|
|
0b00110000,
|
|
|
|
0b00011000,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
|
|
|
|
0b00000000,
|
|
|
|
0b00110000,
|
|
|
|
0b00011000,
|
|
|
|
0b01111100,
|
|
|
|
0b00011000,
|
|
|
|
0b00110000,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const s32 Colors[] = {8, 9, 6, 5, 5};
|
2017-10-09 07:55:15 +02:00
|
|
|
static const StudioEvent Events[] = {TIC_TOOLBAR_CUT, TIC_TOOLBAR_COPY, TIC_TOOLBAR_PASTE, TIC_TOOLBAR_UNDO, TIC_TOOLBAR_REDO};
|
|
|
|
static const char* Tips[] = {"CUT [ctrl+x]", "COPY [ctrl+c]", "PASTE [ctrl+v]", "UNDO [ctrl+z]", "REDO [ctrl+y]"};
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
for(s32 i = 0; i < sizeof Icons / BITS_IN_BYTE; i++)
|
|
|
|
{
|
2018-02-01 17:34:42 +01:00
|
|
|
tic_rect rect = {x + i*Size, y, Size, Size};
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2017-10-20 09:37:30 +02:00
|
|
|
u8 bgcolor = (tic_color_white);
|
|
|
|
u8 color = (tic_color_light_blue);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
if(checkMousePos(&rect))
|
|
|
|
{
|
2018-02-06 20:15:56 +01:00
|
|
|
setCursor(tic_cursor_hand);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
color = Colors[i];
|
|
|
|
showTooltip(Tips[i]);
|
|
|
|
|
2018-02-01 16:32:31 +01:00
|
|
|
if(checkMouseDown(&rect, tic_mouse_left))
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
bgcolor = color;
|
2017-10-20 09:37:30 +02:00
|
|
|
color = (tic_color_white);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
2018-02-01 16:32:31 +01:00
|
|
|
else if(checkMouseClick(&rect, tic_mouse_left))
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
setStudioEvent(Events[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.rect(tic, x + i * Size, y, Size, Size, bgcolor);
|
2017-09-26 08:59:34 +02:00
|
|
|
drawBitIcon(x + i * Size, y, Icons + i*BITS_IN_BYTE, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const StudioConfig* getConfig()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
return &impl.config->data;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 19:24:15 +02:00
|
|
|
static bool isGamepadMode()
|
2018-08-06 23:32:19 +02:00
|
|
|
{
|
2018-08-09 19:24:15 +02:00
|
|
|
return impl.mode == TIC_RUN_MODE
|
|
|
|
|| impl.mode == TIC_SURF_MODE
|
|
|
|
|| impl.mode == TIC_MENU_MODE;
|
2018-08-06 23:32:19 +02:00
|
|
|
}
|
|
|
|
|
2017-12-13 14:02:16 +01:00
|
|
|
#if defined (TIC80_PRO)
|
|
|
|
|
|
|
|
static void drawBankIcon(s32 x, s32 y)
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2017-12-13 15:09:25 +01:00
|
|
|
|
2018-08-03 22:22:58 +02:00
|
|
|
tic_rect rect = {x, y, TIC_FONT_WIDTH, TIC_FONT_HEIGHT};
|
2017-12-13 14:02:16 +01:00
|
|
|
|
|
|
|
static const u8 Icon[] =
|
|
|
|
{
|
|
|
|
0b00000000,
|
|
|
|
0b01111100,
|
|
|
|
0b01000100,
|
|
|
|
0b01000100,
|
|
|
|
0b01111100,
|
|
|
|
0b01111000,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
};
|
|
|
|
|
|
|
|
bool over = false;
|
2017-12-13 21:04:22 +01:00
|
|
|
s32 mode = 0;
|
|
|
|
|
|
|
|
for(s32 i = 0; i < COUNT_OF(Modes); i++)
|
2018-02-15 11:36:15 +01:00
|
|
|
if(Modes[i] == impl.mode)
|
2017-12-13 21:04:22 +01:00
|
|
|
{
|
|
|
|
mode = i;
|
|
|
|
break;
|
|
|
|
}
|
2017-12-13 14:02:16 +01:00
|
|
|
|
|
|
|
if(checkMousePos(&rect))
|
|
|
|
{
|
2018-02-06 20:15:56 +01:00
|
|
|
setCursor(tic_cursor_hand);
|
2017-12-13 14:02:16 +01:00
|
|
|
|
|
|
|
over = true;
|
|
|
|
|
|
|
|
showTooltip("SWITCH BANK");
|
|
|
|
|
2018-02-01 16:32:31 +01:00
|
|
|
if(checkMouseClick(&rect, tic_mouse_left))
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.bank.show = !impl.bank.show;
|
2017-12-13 14:02:16 +01:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.bank.show)
|
2017-12-13 15:09:25 +01:00
|
|
|
{
|
2017-12-13 17:54:00 +01:00
|
|
|
drawBitIcon(x, y, Icon, tic_color_red);
|
2017-12-13 15:09:25 +01:00
|
|
|
|
|
|
|
enum{Size = TOOLBAR_SIZE};
|
|
|
|
|
2017-12-14 15:19:35 +01:00
|
|
|
for(s32 i = 0; i < TIC_EDITOR_BANKS; i++)
|
2017-12-13 15:09:25 +01:00
|
|
|
{
|
2018-08-03 22:22:58 +02:00
|
|
|
tic_rect rect = {x + 2 + (i+1)*Size, 0, Size, Size};
|
2017-12-13 15:09:25 +01:00
|
|
|
|
|
|
|
bool over = false;
|
|
|
|
if(checkMousePos(&rect))
|
|
|
|
{
|
2018-02-06 20:15:56 +01:00
|
|
|
setCursor(tic_cursor_hand);
|
2017-12-13 15:09:25 +01:00
|
|
|
over = true;
|
|
|
|
|
2018-02-01 16:32:31 +01:00
|
|
|
if(checkMouseClick(&rect, tic_mouse_left))
|
2017-12-21 10:51:34 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.bank.chained)
|
|
|
|
memset(impl.bank.indexes, i, sizeof impl.bank.indexes);
|
|
|
|
else impl.bank.indexes[mode] = i;
|
2017-12-21 10:51:34 +01:00
|
|
|
}
|
2017-12-13 15:09:25 +01:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(i == impl.bank.indexes[mode])
|
2017-12-13 15:09:25 +01:00
|
|
|
tic->api.rect(tic, rect.x, rect.y, rect.w, rect.h, tic_color_red);
|
|
|
|
|
2018-08-03 23:36:53 +02:00
|
|
|
tic->api.draw_char(tic, '0' + i, rect.x+1, rect.y+1, i == impl.bank.indexes[mode] ? tic_color_white : over ? tic_color_red : tic_color_peach, false);
|
2017-12-21 10:51:34 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
static const u8 PinIcon[] =
|
|
|
|
{
|
|
|
|
0b00000000,
|
|
|
|
0b00111000,
|
|
|
|
0b00101000,
|
|
|
|
0b01111100,
|
|
|
|
0b00010000,
|
|
|
|
0b00010000,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
};
|
|
|
|
|
2018-08-03 22:22:58 +02:00
|
|
|
tic_rect rect = {x + 4 + (TIC_EDITOR_BANKS+1)*Size, 0, Size, Size};
|
2017-12-21 10:51:34 +01:00
|
|
|
|
|
|
|
bool over = false;
|
|
|
|
|
|
|
|
if(checkMousePos(&rect))
|
|
|
|
{
|
2018-02-06 20:15:56 +01:00
|
|
|
setCursor(tic_cursor_hand);
|
2017-12-21 10:51:34 +01:00
|
|
|
|
|
|
|
over = true;
|
|
|
|
|
2018-02-01 16:32:31 +01:00
|
|
|
if(checkMouseClick(&rect, tic_mouse_left))
|
2017-12-21 10:51:34 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.bank.chained = !impl.bank.chained;
|
2017-12-21 10:51:34 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.bank.chained)
|
|
|
|
memset(impl.bank.indexes, impl.bank.indexes[mode], sizeof impl.bank.indexes);
|
2017-12-21 10:51:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
drawBitIcon(rect.x, rect.y, PinIcon, impl.bank.chained ? tic_color_black : over ? tic_color_dark_gray : tic_color_light_blue);
|
2017-12-13 15:09:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
drawBitIcon(x, y, Icon, over ? tic_color_red : tic_color_peach);
|
|
|
|
}
|
2017-12-13 14:02:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
void drawToolbar(tic_mem* tic, u8 color, bool bg)
|
|
|
|
{
|
|
|
|
if(bg)
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.rect(tic, 0, 0, TIC80_WIDTH, TOOLBAR_SIZE, (tic_color_white));
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
static const u8 TabIcon[] =
|
|
|
|
{
|
|
|
|
0b11111110,
|
|
|
|
0b11111110,
|
|
|
|
0b11111110,
|
|
|
|
0b11111110,
|
|
|
|
0b11111110,
|
|
|
|
0b11111110,
|
|
|
|
0b11111110,
|
|
|
|
0b00000000,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u8 Icons[] =
|
|
|
|
{
|
|
|
|
0b00000000,
|
|
|
|
0b01101100,
|
|
|
|
0b01000100,
|
|
|
|
0b01000100,
|
|
|
|
0b01000100,
|
|
|
|
0b01101100,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
|
|
|
|
0b00000000,
|
|
|
|
0b00111000,
|
|
|
|
0b01010100,
|
|
|
|
0b01111100,
|
|
|
|
0b01111100,
|
|
|
|
0b01010100,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
|
|
|
|
0b00000000,
|
|
|
|
0b01101100,
|
|
|
|
0b01101100,
|
|
|
|
0b00000000,
|
|
|
|
0b01101100,
|
|
|
|
0b01101100,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
|
|
|
|
0b00000000,
|
|
|
|
0b00011000,
|
|
|
|
0b00110100,
|
|
|
|
0b01110100,
|
|
|
|
0b00110100,
|
|
|
|
0b00011000,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
|
|
|
|
0b00000000,
|
|
|
|
0b00111100,
|
|
|
|
0b00100100,
|
|
|
|
0b00100100,
|
|
|
|
0b01101100,
|
|
|
|
0b01101100,
|
|
|
|
0b00000000,
|
|
|
|
0b00000000,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {Size = 7};
|
|
|
|
|
|
|
|
static const char* Tips[] = {"CODE EDITOR [f1]", "SPRITE EDITOR [f2]", "MAP EDITOR [f3]", "SFX EDITOR [f4]", "MUSIC EDITOR [f5]",};
|
|
|
|
|
|
|
|
s32 mode = -1;
|
|
|
|
|
|
|
|
for(s32 i = 0; i < COUNT_OF(Modes); i++)
|
|
|
|
{
|
2018-02-01 17:34:42 +01:00
|
|
|
tic_rect rect = {i * Size, 0, Size, Size};
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
bool over = false;
|
|
|
|
|
|
|
|
if(checkMousePos(&rect))
|
|
|
|
{
|
2018-02-06 20:15:56 +01:00
|
|
|
setCursor(tic_cursor_hand);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
over = true;
|
|
|
|
|
|
|
|
showTooltip(Tips[i]);
|
|
|
|
|
2018-02-01 16:32:31 +01:00
|
|
|
if(checkMouseClick(&rect, tic_mouse_left))
|
2017-09-26 08:59:34 +02:00
|
|
|
setStudioMode(Modes[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(getStudioMode() == Modes[i]) mode = i;
|
|
|
|
|
|
|
|
if(mode == i)
|
|
|
|
drawBitIcon(i * Size, 0, TabIcon, color);
|
|
|
|
|
2017-12-25 21:57:58 +01:00
|
|
|
if(mode == i)
|
|
|
|
drawBitIcon(i * Size, 1, Icons + i * BITS_IN_BYTE, tic_color_black);
|
|
|
|
|
2017-10-20 09:37:30 +02:00
|
|
|
drawBitIcon(i * Size, 0, Icons + i * BITS_IN_BYTE, mode == i ? (tic_color_white) : (over ? (tic_color_dark_gray) : (tic_color_light_blue)));
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if(mode >= 0) drawExtrabar(tic);
|
|
|
|
|
|
|
|
static const char* Names[] =
|
|
|
|
{
|
|
|
|
"CODE EDITOR",
|
|
|
|
"SPRITE EDITOR",
|
|
|
|
"MAP EDITOR",
|
|
|
|
"SFX EDITOR",
|
|
|
|
"MUSIC EDITOR",
|
|
|
|
};
|
|
|
|
|
2017-12-13 14:02:16 +01:00
|
|
|
#if defined (TIC80_PRO)
|
|
|
|
enum {TextOffset = (COUNT_OF(Modes) + 2) * Size - 2};
|
|
|
|
drawBankIcon(COUNT_OF(Modes) * Size + 2, 0);
|
|
|
|
#else
|
|
|
|
enum {TextOffset = (COUNT_OF(Modes) + 1) * Size};
|
|
|
|
#endif
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(mode >= 0 && !impl.bank.show)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(strlen(impl.tooltip.text))
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-08-03 23:36:53 +02:00
|
|
|
impl.studio.tic->api.text(tic, impl.tooltip.text, TextOffset, 1, (tic_color_black), false);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-08-03 23:36:53 +02:00
|
|
|
impl.studio.tic->api.text(tic, Names[mode], TextOffset, 1, (tic_color_dark_gray), false);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void setStudioEvent(StudioEvent event)
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
switch(impl.mode)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2017-12-14 12:38:06 +01:00
|
|
|
case TIC_CODE_MODE:
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Code* code = impl.editor[impl.bank.index.code].code;
|
2017-12-14 12:38:06 +01:00
|
|
|
code->event(code, event);
|
|
|
|
}
|
|
|
|
break;
|
2017-12-14 15:08:04 +01:00
|
|
|
case TIC_SPRITE_MODE:
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Sprite* sprite = impl.editor[impl.bank.index.sprites].sprite;
|
2017-12-14 15:08:04 +01:00
|
|
|
sprite->event(sprite, event);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TIC_MAP_MODE:
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Map* map = impl.editor[impl.bank.index.map].map;
|
2017-12-14 15:08:04 +01:00
|
|
|
map->event(map, event);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TIC_SFX_MODE:
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Sfx* sfx = impl.editor[impl.bank.index.sfx].sfx;
|
2017-12-14 15:08:04 +01:00
|
|
|
sfx->event(sfx, event);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TIC_MUSIC_MODE:
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Music* music = impl.editor[impl.bank.index.music].music;
|
2017-12-14 15:08:04 +01:00
|
|
|
music->event(music, event);
|
|
|
|
}
|
|
|
|
break;
|
2017-09-26 08:59:34 +02:00
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-12 17:15:05 +01:00
|
|
|
ClipboardEvent getClipboardEvent()
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-12 17:15:05 +01:00
|
|
|
bool shift = tic->api.key(tic, tic_key_shift);
|
|
|
|
bool ctrl = tic->api.key(tic, tic_key_ctrl);
|
|
|
|
|
|
|
|
if(ctrl)
|
|
|
|
{
|
2018-02-13 11:29:33 +01:00
|
|
|
if(keyWasPressed(tic_key_insert) || keyWasPressed(tic_key_c)) return TIC_CLIPBOARD_COPY;
|
|
|
|
else if(keyWasPressed(tic_key_x)) return TIC_CLIPBOARD_CUT;
|
|
|
|
else if(keyWasPressed(tic_key_v)) return TIC_CLIPBOARD_PASTE;
|
2018-02-12 17:15:05 +01:00
|
|
|
}
|
|
|
|
else if(shift)
|
|
|
|
{
|
2018-02-13 11:29:33 +01:00
|
|
|
if(keyWasPressed(tic_key_delete)) return TIC_CLIPBOARD_CUT;
|
|
|
|
else if(keyWasPressed(tic_key_insert)) return TIC_CLIPBOARD_PASTE;
|
2018-02-12 17:15:05 +01:00
|
|
|
}
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
return TIC_CLIPBOARD_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void showPopupMessage(const char* text)
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.popup.counter = POPUP_DUR;
|
|
|
|
strcpy(impl.popup.message, text);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void exitConfirm(bool yes, void* data)
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.quit = yes;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void exitStudio()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.mode != TIC_START_MODE && studioCartChanged())
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2017-10-16 05:03:05 +02:00
|
|
|
static const char* Rows[] =
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
"YOU HAVE",
|
|
|
|
"UNSAVED CHANGES",
|
|
|
|
"",
|
|
|
|
"DO YOU REALLY WANT",
|
|
|
|
"TO EXIT?",
|
|
|
|
};
|
|
|
|
|
|
|
|
showDialog(Rows, COUNT_OF(Rows), exitConfirm, NULL);
|
|
|
|
}
|
|
|
|
else exitConfirm(true, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void drawBitIcon(s32 x, s32 y, const u8* ptr, u8 color)
|
|
|
|
{
|
|
|
|
for(s32 i = 0; i < TIC_SPRITESIZE; i++, ptr++)
|
|
|
|
for(s32 col = 0; col < TIC_SPRITESIZE; col++)
|
|
|
|
if(*ptr & 1 << col)
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.pixel(impl.studio.tic, x + TIC_SPRITESIZE - col - 1, y + i, color);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void initWorldMap()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
initWorld(impl.world, impl.studio.tic, impl.editor[impl.bank.index.map].map);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void initRunMode()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
initRun(impl.run, impl.console, impl.studio.tic);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void initSurfMode()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
initSurf(impl.surf, impl.studio.tic, impl.console);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2017-09-28 16:20:27 +02:00
|
|
|
void gotoSurf()
|
|
|
|
{
|
|
|
|
initSurfMode();
|
|
|
|
setStudioMode(TIC_SURF_MODE);
|
|
|
|
}
|
|
|
|
|
2017-11-04 00:11:45 +01:00
|
|
|
void gotoCode()
|
|
|
|
{
|
|
|
|
setStudioMode(TIC_CODE_MODE);
|
|
|
|
}
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
static void initMenuMode()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
initMenu(impl.menu, impl.studio.tic, impl.fs);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void runGameFromSurf()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.reset(impl.studio.tic);
|
2017-09-26 08:59:34 +02:00
|
|
|
setStudioMode(TIC_RUN_MODE);
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.prevMode = TIC_SURF_MODE;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void exitFromGameMenu()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.prevMode == TIC_SURF_MODE)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
setStudioMode(TIC_SURF_MODE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
setStudioMode(TIC_CONSOLE_MODE);
|
|
|
|
}
|
2017-11-21 17:47:36 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.console->showGameMenu = false;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2017-12-19 12:02:56 +01:00
|
|
|
void resumeRunMode()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.mode = TIC_RUN_MODE;
|
2017-12-19 12:02:56 +01:00
|
|
|
}
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
void setStudioMode(EditorMode mode)
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(mode != impl.mode)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
EditorMode prev = impl.mode;
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
if(prev == TIC_RUN_MODE)
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.pause(impl.studio.tic);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
if(mode != TIC_RUN_MODE)
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.reset(impl.studio.tic);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
switch (prev)
|
|
|
|
{
|
|
|
|
case TIC_START_MODE:
|
|
|
|
case TIC_CONSOLE_MODE:
|
2017-10-16 05:03:05 +02:00
|
|
|
case TIC_RUN_MODE:
|
2017-09-26 08:59:34 +02:00
|
|
|
case TIC_DIALOG_MODE:
|
|
|
|
case TIC_MENU_MODE:
|
|
|
|
break;
|
|
|
|
case TIC_SURF_MODE:
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.prevMode = TIC_CODE_MODE;
|
2017-09-26 08:59:34 +02:00
|
|
|
break;
|
2018-02-15 11:36:15 +01:00
|
|
|
default: impl.prevMode = prev; break;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
switch(mode)
|
|
|
|
{
|
|
|
|
case TIC_WORLD_MODE: initWorldMap(); break;
|
|
|
|
case TIC_RUN_MODE: initRunMode(); break;
|
2018-02-15 11:36:15 +01:00
|
|
|
case TIC_SURF_MODE: impl.surf->resume(impl.surf); break;
|
2017-09-26 08:59:34 +02:00
|
|
|
default: break;
|
2017-10-16 05:03:05 +02:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.mode = mode;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EditorMode getStudioMode()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
return impl.mode;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-01-22 06:49:46 +01:00
|
|
|
void changeStudioMode(s32 dir)
|
|
|
|
{
|
2018-02-01 17:34:42 +01:00
|
|
|
const size_t modeCount = sizeof(Modes)/sizeof(Modes[0]);
|
|
|
|
for(size_t i = 0; i < modeCount; i++)
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.mode == Modes[i])
|
2018-02-01 17:34:42 +01:00
|
|
|
{
|
|
|
|
setStudioMode(Modes[(i+dir+modeCount) % modeCount]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-01-22 06:49:46 +01:00
|
|
|
}
|
|
|
|
|
2018-02-13 10:44:16 +01:00
|
|
|
static void showGameMenu()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.pause(impl.studio.tic);
|
|
|
|
impl.studio.tic->api.reset(impl.studio.tic);
|
2017-10-20 10:48:04 +02:00
|
|
|
|
2018-02-13 10:44:16 +01:00
|
|
|
initMenuMode();
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.mode = TIC_MENU_MODE;
|
2018-02-13 10:44:16 +01:00
|
|
|
}
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
void hideGameMenu()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.resume(impl.studio.tic);
|
|
|
|
impl.mode = TIC_RUN_MODE;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
s32 getMouseX()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2018-01-24 12:55:13 +01:00
|
|
|
|
|
|
|
return tic->ram.input.mouse.x;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
s32 getMouseY()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2018-01-24 12:55:13 +01:00
|
|
|
|
|
|
|
return tic->ram.input.mouse.y;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-01 17:34:42 +01:00
|
|
|
static inline bool pointInRect(const tic_point* pt, const tic_rect* rect)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-01 17:34:42 +01:00
|
|
|
return (pt->x >= rect->x)
|
|
|
|
&& (pt->x < (rect->x + rect->w))
|
|
|
|
&& (pt->y >= rect->y)
|
|
|
|
&& (pt->y < (rect->y + rect->h));
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-01 17:34:42 +01:00
|
|
|
bool checkMousePos(const tic_rect* rect)
|
|
|
|
{
|
|
|
|
tic_point pos = {getMouseX(), getMouseY()};
|
|
|
|
return pointInRect(&pos, rect);
|
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
bool checkMouseClick(const tic_rect* rect, tic_mouse_btn button)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
MouseState* state = &impl.mouse.state[button];
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2017-10-16 05:03:05 +02:00
|
|
|
bool value = state->click
|
2018-02-01 17:34:42 +01:00
|
|
|
&& pointInRect(&state->start, rect)
|
|
|
|
&& pointInRect(&state->end, rect);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
if(value) state->click = false;
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
bool checkMouseDown(const tic_rect* rect, tic_mouse_btn button)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
MouseState* state = &impl.mouse.state[button];
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-01 17:34:42 +01:00
|
|
|
return state->down && pointInRect(&state->start, rect);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-06 20:15:56 +01:00
|
|
|
void setCursor(tic_cursor id)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2018-02-12 12:51:57 +01:00
|
|
|
|
|
|
|
tic->ram.vram.vars.cursor.sprite = id;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void hideDialog()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.dialogMode == TIC_RUN_MODE)
|
2017-11-20 08:05:32 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.resume(impl.studio.tic);
|
|
|
|
impl.mode = TIC_RUN_MODE;
|
2017-11-20 08:05:32 +01:00
|
|
|
}
|
2018-02-15 11:36:15 +01:00
|
|
|
else setStudioMode(impl.dialogMode);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void showDialog(const char** text, s32 rows, DialogCallback callback, void* data)
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.mode != TIC_DIALOG_MODE)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
initDialog(impl.dialog, impl.studio.tic, text, rows, callback, data);
|
|
|
|
impl.dialogMode = impl.mode;
|
2017-09-26 08:59:34 +02:00
|
|
|
setStudioMode(TIC_DIALOG_MODE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-20 13:16:42 +01:00
|
|
|
static void resetBanks()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
memset(impl.bank.indexes, 0, sizeof impl.bank.indexes);
|
2017-12-20 13:16:42 +01:00
|
|
|
}
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
static void initModules()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2017-12-14 15:08:04 +01:00
|
|
|
|
2017-12-20 13:16:42 +01:00
|
|
|
resetBanks();
|
|
|
|
|
2017-12-14 15:19:35 +01:00
|
|
|
for(s32 i = 0; i < TIC_EDITOR_BANKS; i++)
|
2017-12-14 15:08:04 +01:00
|
|
|
{
|
2018-09-05 12:10:14 +02:00
|
|
|
initCode(impl.editor[i].code, impl.studio.tic, &tic->cart.code);
|
2018-02-15 11:36:15 +01:00
|
|
|
initSprite(impl.editor[i].sprite, impl.studio.tic, &tic->cart.banks[i].tiles);
|
|
|
|
initMap(impl.editor[i].map, impl.studio.tic, &tic->cart.banks[i].map);
|
|
|
|
initSfx(impl.editor[i].sfx, impl.studio.tic, &tic->cart.banks[i].sfx);
|
|
|
|
initMusic(impl.editor[i].music, impl.studio.tic, &tic->cart.banks[i].music);
|
2017-12-14 15:08:04 +01:00
|
|
|
}
|
2017-12-14 12:38:06 +01:00
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
initWorldMap();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void updateHash()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
md5(&impl.studio.tic->cart, sizeof(tic_cartridge), impl.cart.hash.data);
|
2017-11-22 08:20:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void updateMDate()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.cart.mdate = fsMDate(impl.console->fs, impl.console->romName);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2017-09-28 13:44:52 +02:00
|
|
|
static void updateTitle()
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-13 11:38:11 +01:00
|
|
|
char name[FILENAME_MAX] = TIC_TITLE;
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(strlen(impl.console->romName))
|
|
|
|
sprintf(name, "%s [%s]", TIC_TITLE, impl.console->romName);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.system->setWindowTitle(name);
|
2017-09-28 13:44:52 +02:00
|
|
|
}
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2017-09-28 13:44:52 +02:00
|
|
|
void studioRomSaved()
|
|
|
|
{
|
|
|
|
updateTitle();
|
2017-09-26 08:59:34 +02:00
|
|
|
updateHash();
|
2017-11-22 08:20:12 +01:00
|
|
|
updateMDate();
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void studioRomLoaded()
|
|
|
|
{
|
|
|
|
initModules();
|
|
|
|
|
2017-09-28 13:44:52 +02:00
|
|
|
updateTitle();
|
2017-09-26 08:59:34 +02:00
|
|
|
updateHash();
|
2017-11-22 08:20:12 +01:00
|
|
|
updateMDate();
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool studioCartChanged()
|
|
|
|
{
|
|
|
|
CartHash hash;
|
2018-02-15 11:36:15 +01:00
|
|
|
md5(&impl.studio.tic->cart, sizeof(tic_cartridge), hash.data);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
return memcmp(hash.data, impl.cart.hash.data, sizeof(CartHash)) != 0;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-01-24 15:56:55 +01:00
|
|
|
tic_key* getKeymap()
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
return impl.keycodes;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-01-24 15:56:55 +01:00
|
|
|
static void processGamepadMapping()
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
for(s32 i = 0; i < KEYMAP_COUNT; i++)
|
2018-02-15 12:28:34 +01:00
|
|
|
if(impl.keycodes[i] && tic->api.key(tic, impl.keycodes[i]))
|
2018-08-02 21:06:48 +02:00
|
|
|
tic->ram.input.gamepads.data |= 1 << i;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool isGameMenu()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
return (impl.mode == TIC_RUN_MODE && impl.console->showGameMenu) || impl.mode == TIC_MENU_MODE;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2017-12-07 12:25:39 +01:00
|
|
|
static void goFullscreen()
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.system->goFullscreen();
|
2017-11-08 12:07:23 +01:00
|
|
|
}
|
|
|
|
|
2017-10-08 20:28:51 +02:00
|
|
|
void runProject()
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.reset(impl.studio.tic);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.mode == TIC_RUN_MODE)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
initRunMode();
|
|
|
|
}
|
|
|
|
else setStudioMode(TIC_RUN_MODE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void saveProject()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
CartSaveResult rom = impl.console->save(impl.console);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
if(rom == CART_SAVE_OK)
|
|
|
|
{
|
|
|
|
char buffer[FILENAME_MAX];
|
2018-02-15 11:36:15 +01:00
|
|
|
sprintf(buffer, "%s SAVED :)", impl.console->romName);
|
2017-10-16 05:03:05 +02:00
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
for(s32 i = 0; i < (s32)strlen(buffer); i++)
|
2018-02-06 20:15:56 +01:00
|
|
|
buffer[i] = toupper(buffer[i]);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
showPopupMessage(buffer);
|
|
|
|
}
|
|
|
|
else if(rom == CART_SAVE_MISSING_NAME) showPopupMessage("SAVE: MISSING CART NAME :|");
|
|
|
|
else showPopupMessage("SAVE ERROR :(");
|
|
|
|
}
|
|
|
|
|
2018-02-01 17:34:42 +01:00
|
|
|
static void screen2buffer(u32* buffer, const u32* pixels, tic_rect rect)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2017-11-06 10:01:35 +01:00
|
|
|
pixels += rect.y * TIC80_FULLWIDTH;
|
|
|
|
|
|
|
|
for(s32 i = 0; i < rect.h; i++)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-06 20:15:56 +01:00
|
|
|
memcpy(buffer, pixels + rect.x, rect.w * sizeof(pixels[0]));
|
2017-11-06 10:01:35 +01:00
|
|
|
pixels += TIC80_FULLWIDTH;
|
|
|
|
buffer += rect.w;
|
2017-10-16 05:03:05 +02:00
|
|
|
}
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void setCoverImage()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2017-11-06 10:01:35 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.mode == TIC_RUN_MODE)
|
2018-02-13 14:02:05 +01:00
|
|
|
{
|
|
|
|
enum {Pitch = TIC80_FULLWIDTH*sizeof(u32)};
|
|
|
|
|
2018-02-27 11:42:08 +01:00
|
|
|
tic->api.blit(tic, tic->api.scanline, tic->api.overline, NULL);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
u32* buffer = malloc(TIC80_WIDTH * TIC80_HEIGHT * sizeof(u32));
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
if(buffer)
|
|
|
|
{
|
|
|
|
enum{OffsetLeft = (TIC80_FULLWIDTH-TIC80_WIDTH)/2, OffsetTop = (TIC80_FULLHEIGHT-TIC80_HEIGHT)/2};
|
2017-11-06 10:01:35 +01:00
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
tic_rect rect = {OffsetLeft, OffsetTop, TIC80_WIDTH, TIC80_HEIGHT};
|
2017-10-16 05:03:05 +02:00
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
screen2buffer(buffer, tic->screen, rect);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
gif_write_animation(impl.studio.tic->cart.cover.data, &impl.studio.tic->cart.cover.size,
|
2018-02-13 14:02:05 +01:00
|
|
|
TIC80_WIDTH, TIC80_HEIGHT, (const u8*)buffer, 1, TIC_FRAMERATE, 1);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
free(buffer);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
showPopupMessage("COVER IMAGE SAVED :)");
|
|
|
|
}
|
|
|
|
}
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void onVideoExported(GetResult result, void* data)
|
|
|
|
{
|
|
|
|
if(result == FS_FILE_NOT_DOWNLOADED)
|
|
|
|
showPopupMessage("GIF NOT EXPORTED :|");
|
|
|
|
else if (result == FS_FILE_DOWNLOADED)
|
|
|
|
showPopupMessage("GIF EXPORTED :)");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void stopVideoRecord()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.video.buffer)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
{
|
|
|
|
s32 size = 0;
|
2018-02-15 11:36:15 +01:00
|
|
|
u8* data = malloc(FRAME_SIZE * impl.video.frame);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
gif_write_animation(data, &size, TIC80_FULLWIDTH, TIC80_FULLHEIGHT, (const u8*)impl.video.buffer, impl.video.frame, TIC_FRAMERATE, getConfig()->gifScale);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
fsGetFileData(onVideoExported, "screen.gif", data, size, DEFAULT_CHMOD, NULL);
|
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
free(impl.video.buffer);
|
|
|
|
impl.video.buffer = NULL;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.video.record = false;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if !defined(__EMSCRIPTEN__)
|
|
|
|
|
|
|
|
static void startVideoRecord()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.video.record)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
stopVideoRecord();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.video.frames = getConfig()->gifLength * TIC_FRAMERATE;
|
|
|
|
impl.video.buffer = malloc(FRAME_SIZE * impl.video.frames);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.video.buffer)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.video.record = true;
|
|
|
|
impl.video.frame = 0;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void takeScreenshot()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.video.frames = 1;
|
|
|
|
impl.video.buffer = malloc(FRAME_SIZE);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.video.buffer)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.video.record = true;
|
|
|
|
impl.video.frame = 0;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-13 11:29:33 +01:00
|
|
|
static inline bool keyWasPressedOnce(s32 key)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2018-02-01 17:11:59 +01:00
|
|
|
|
|
|
|
return tic->api.keyp(tic, key, -1, -1);
|
|
|
|
}
|
|
|
|
|
2018-02-26 12:14:10 +01:00
|
|
|
static void switchCrtMonitor()
|
|
|
|
{
|
|
|
|
impl.config->data.crtMonitor = !impl.config->data.crtMonitor;
|
|
|
|
}
|
|
|
|
|
2018-02-01 17:11:59 +01:00
|
|
|
static void processShortcuts()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2017-12-07 14:42:06 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.mode == TIC_START_MODE) return;
|
|
|
|
if(impl.mode == TIC_CONSOLE_MODE && !impl.console->active) return;
|
2017-10-16 05:03:05 +02:00
|
|
|
|
2018-02-13 10:44:16 +01:00
|
|
|
bool alt = tic->api.key(tic, tic_key_alt);
|
|
|
|
bool ctrl = tic->api.key(tic, tic_key_ctrl);
|
|
|
|
|
2018-02-26 12:14:10 +01:00
|
|
|
if(keyWasPressedOnce(tic_key_f6)) switchCrtMonitor();
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
if(isGameMenu())
|
|
|
|
{
|
2018-02-13 11:29:33 +01:00
|
|
|
if(keyWasPressedOnce(tic_key_escape))
|
2018-02-13 10:44:16 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.mode == TIC_MENU_MODE ? hideGameMenu() : showGameMenu();
|
2018-02-13 10:44:16 +01:00
|
|
|
}
|
2018-02-13 11:29:33 +01:00
|
|
|
else if(keyWasPressedOnce(tic_key_f11)) goFullscreen();
|
|
|
|
else if(keyWasPressedOnce(tic_key_return))
|
2018-02-13 10:44:16 +01:00
|
|
|
{
|
|
|
|
if(alt) goFullscreen();
|
|
|
|
}
|
2018-02-13 11:29:33 +01:00
|
|
|
else if(keyWasPressedOnce(tic_key_f7)) setCoverImage();
|
|
|
|
else if(keyWasPressedOnce(tic_key_f8)) takeScreenshot();
|
2018-02-13 10:44:16 +01:00
|
|
|
#if !defined(__EMSCRIPTEN__)
|
2018-02-13 11:29:33 +01:00
|
|
|
else if(keyWasPressedOnce(tic_key_f9)) startVideoRecord();
|
2018-02-13 10:44:16 +01:00
|
|
|
#endif
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-13 11:38:11 +01:00
|
|
|
return;
|
2018-02-13 10:44:16 +01:00
|
|
|
}
|
2018-02-01 17:11:59 +01:00
|
|
|
|
|
|
|
if(alt)
|
|
|
|
{
|
2018-02-13 11:29:33 +01:00
|
|
|
if(keyWasPressedOnce(tic_key_grave)) setStudioMode(TIC_CONSOLE_MODE);
|
|
|
|
else if(keyWasPressedOnce(tic_key_1)) setStudioMode(TIC_CODE_MODE);
|
|
|
|
else if(keyWasPressedOnce(tic_key_2)) setStudioMode(TIC_SPRITE_MODE);
|
|
|
|
else if(keyWasPressedOnce(tic_key_3)) setStudioMode(TIC_MAP_MODE);
|
|
|
|
else if(keyWasPressedOnce(tic_key_4)) setStudioMode(TIC_SFX_MODE);
|
|
|
|
else if(keyWasPressedOnce(tic_key_5)) setStudioMode(TIC_MUSIC_MODE);
|
|
|
|
else if(keyWasPressedOnce(tic_key_return)) goFullscreen();
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
2018-02-01 17:34:42 +01:00
|
|
|
else if(ctrl)
|
|
|
|
{
|
2018-02-13 11:29:33 +01:00
|
|
|
if(keyWasPressedOnce(tic_key_pageup)) changeStudioMode(-1);
|
|
|
|
else if(keyWasPressedOnce(tic_key_pagedown)) changeStudioMode(1);
|
|
|
|
else if(keyWasPressedOnce(tic_key_q)) exitStudio();
|
|
|
|
else if(keyWasPressedOnce(tic_key_r)) runProject();
|
|
|
|
else if(keyWasPressedOnce(tic_key_return)) runProject();
|
|
|
|
else if(keyWasPressedOnce(tic_key_s)) saveProject();
|
2018-02-01 17:34:42 +01:00
|
|
|
}
|
2017-09-26 08:59:34 +02:00
|
|
|
else
|
|
|
|
{
|
2018-02-13 11:29:33 +01:00
|
|
|
if(keyWasPressedOnce(tic_key_f1)) setStudioMode(TIC_CODE_MODE);
|
|
|
|
else if(keyWasPressedOnce(tic_key_f2)) setStudioMode(TIC_SPRITE_MODE);
|
|
|
|
else if(keyWasPressedOnce(tic_key_f3)) setStudioMode(TIC_MAP_MODE);
|
|
|
|
else if(keyWasPressedOnce(tic_key_f4)) setStudioMode(TIC_SFX_MODE);
|
|
|
|
else if(keyWasPressedOnce(tic_key_f5)) setStudioMode(TIC_MUSIC_MODE);
|
|
|
|
else if(keyWasPressedOnce(tic_key_f7)) setCoverImage();
|
|
|
|
else if(keyWasPressedOnce(tic_key_f8)) takeScreenshot();
|
2017-09-26 08:59:34 +02:00
|
|
|
#if !defined(__EMSCRIPTEN__)
|
2018-02-13 11:29:33 +01:00
|
|
|
else if(keyWasPressedOnce(tic_key_f9)) startVideoRecord();
|
2017-09-26 08:59:34 +02:00
|
|
|
#endif
|
2018-02-13 11:29:33 +01:00
|
|
|
else if(keyWasPressedOnce(tic_key_f11)) goFullscreen();
|
|
|
|
else if(keyWasPressedOnce(tic_key_escape))
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Code* code = impl.editor[impl.bank.index.code].code;
|
2017-12-14 12:38:06 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.mode == TIC_CODE_MODE && code->mode != TEXT_EDIT_MODE)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2017-12-14 12:38:06 +01:00
|
|
|
code->escape(code);
|
2018-02-01 17:11:59 +01:00
|
|
|
return;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.mode == TIC_DIALOG_MODE)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.dialog->escape(impl.dialog);
|
2018-02-01 17:11:59 +01:00
|
|
|
return;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
setStudioMode(impl.mode == TIC_CONSOLE_MODE ? impl.prevMode : TIC_CONSOLE_MODE);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
#if defined(TIC80_PRO)
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
static void reloadConfirm(bool yes, void* data)
|
|
|
|
{
|
|
|
|
if(yes)
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.console->updateProject(impl.console);
|
2018-02-13 14:02:05 +01:00
|
|
|
}
|
2017-12-27 13:47:33 +01:00
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
#endif
|
2017-12-29 13:25:29 +01:00
|
|
|
|
2018-02-14 13:14:05 +01:00
|
|
|
static void updateStudioProject()
|
2018-02-13 14:02:05 +01:00
|
|
|
{
|
|
|
|
#if defined(TIC80_PRO)
|
2017-12-29 13:25:29 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.mode != TIC_START_MODE)
|
2018-02-13 14:02:05 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Console* console = impl.console;
|
2017-12-29 13:25:29 +01:00
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
u64 mdate = fsMDate(console->fs, console->romName);
|
2017-10-16 05:03:05 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.cart.mdate && mdate > impl.cart.mdate)
|
2018-02-13 14:02:05 +01:00
|
|
|
{
|
|
|
|
if(studioCartChanged())
|
|
|
|
{
|
|
|
|
static const char* Rows[] =
|
|
|
|
{
|
|
|
|
"",
|
|
|
|
"CART HAS CHANGED!",
|
|
|
|
"",
|
|
|
|
"DO YOU WANT",
|
|
|
|
"TO RELOAD IT?"
|
|
|
|
};
|
|
|
|
|
|
|
|
showDialog(Rows, COUNT_OF(Rows), reloadConfirm, NULL);
|
|
|
|
}
|
|
|
|
else console->updateProject(console);
|
|
|
|
}
|
|
|
|
}
|
2017-11-22 08:20:12 +01:00
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
#endif
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Code* code = impl.editor[impl.bank.index.code].code;
|
|
|
|
impl.console->codeLiveReload.reload(impl.console, code->src);
|
|
|
|
if(impl.console->codeLiveReload.active && code->update)
|
2018-02-13 14:02:05 +01:00
|
|
|
code->update(code);
|
|
|
|
}
|
2017-11-21 17:47:36 +01:00
|
|
|
|
2018-02-13 14:02:05 +01:00
|
|
|
}
|
2017-11-22 08:20:12 +01:00
|
|
|
|
2018-01-04 15:09:27 +01:00
|
|
|
static void drawRecordLabel(u32* frame, s32 sx, s32 sy, const u32* color)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2017-10-16 05:03:05 +02:00
|
|
|
static const u16 RecLabel[] =
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
0b0111001100110011,
|
|
|
|
0b1111101010100100,
|
|
|
|
0b1111101100110100,
|
|
|
|
0b1111101010100100,
|
|
|
|
0b0111001010110011,
|
|
|
|
};
|
2017-10-16 05:03:05 +02:00
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
for(s32 y = 0; y < 5; y++)
|
|
|
|
{
|
|
|
|
for(s32 x = 0; x < sizeof RecLabel[0]*BITS_IN_BYTE; x++)
|
|
|
|
{
|
|
|
|
if(RecLabel[y] & (1 << x))
|
2017-12-01 07:40:06 +01:00
|
|
|
memcpy(&frame[sx + 15 - x + ((y+sy) << TIC80_FULLWIDTH_BITS)], color, sizeof *color);
|
2017-10-16 05:03:05 +02:00
|
|
|
}
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-04 15:09:27 +01:00
|
|
|
static void drawDesyncLabel(u32* frame)
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(getConfig()->showSync && impl.missedFrame)
|
2018-01-04 15:09:27 +01:00
|
|
|
{
|
2018-01-22 10:59:34 +01:00
|
|
|
static const u16 DesyncLabel[] =
|
|
|
|
{
|
|
|
|
0b0110101010010011,
|
|
|
|
0b1000101011010100,
|
|
|
|
0b1110111010110100,
|
|
|
|
0b0010001010010100,
|
|
|
|
0b1100110010010011,
|
|
|
|
};
|
|
|
|
|
2018-01-04 15:09:27 +01:00
|
|
|
enum{sx = TIC80_WIDTH-24, sy = 8, Cols = sizeof DesyncLabel[0]*BITS_IN_BYTE, Rows = COUNT_OF(DesyncLabel)};
|
|
|
|
|
2018-03-12 07:40:48 +01:00
|
|
|
const u32* pal = tic_palette_blit(&impl.config->cart.bank0.palette);
|
2018-01-04 15:09:27 +01:00
|
|
|
const u32* color = &pal[tic_color_red];
|
|
|
|
|
|
|
|
for(s32 y = 0; y < Rows; y++)
|
|
|
|
{
|
|
|
|
for(s32 x = 0; x < Cols; x++)
|
|
|
|
{
|
|
|
|
if(DesyncLabel[y] & (1 << x))
|
|
|
|
memcpy(&frame[sx + Cols - 1 - x + ((y+sy) << TIC80_FULLWIDTH_BITS)], color, sizeof *color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-06 10:01:35 +01:00
|
|
|
static void recordFrame(u32* pixels)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.video.record)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.video.frame < impl.video.frames)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-01 17:34:42 +01:00
|
|
|
tic_rect rect = {0, 0, TIC80_FULLWIDTH, TIC80_FULLHEIGHT};
|
2018-02-15 11:36:15 +01:00
|
|
|
screen2buffer(impl.video.buffer + (TIC80_FULLWIDTH*TIC80_FULLHEIGHT) * impl.video.frame, pixels, rect);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.video.frame % TIC_FRAMERATE < TIC_FRAMERATE / 2)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-03-12 07:40:48 +01:00
|
|
|
const u32* pal = tic_palette_blit(&impl.config->cart.bank0.palette);
|
2018-01-04 15:09:27 +01:00
|
|
|
drawRecordLabel(pixels, TIC80_WIDTH-24, 8, &pal[tic_color_red]);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.video.frame++;
|
2017-10-16 05:03:05 +02:00
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
stopVideoRecord();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-08 13:03:59 +01:00
|
|
|
static void drawPopup()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.popup.counter > 0)
|
2017-12-08 13:03:59 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.popup.counter--;
|
2017-12-08 13:03:59 +01:00
|
|
|
|
|
|
|
s32 anim = 0;
|
|
|
|
|
|
|
|
enum{Dur = TIC_FRAMERATE/2};
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.popup.counter < Dur)
|
2018-08-03 22:22:58 +02:00
|
|
|
anim = -((Dur - impl.popup.counter) * (TIC_FONT_HEIGHT+1) / Dur);
|
2018-02-15 11:36:15 +01:00
|
|
|
else if(impl.popup.counter >= (POPUP_DUR - Dur))
|
2018-08-03 22:22:58 +02:00
|
|
|
anim = (((POPUP_DUR - Dur) - impl.popup.counter) * (TIC_FONT_HEIGHT+1) / Dur);
|
2017-12-08 13:03:59 +01:00
|
|
|
|
2018-08-03 22:22:58 +02:00
|
|
|
impl.studio.tic->api.rect(impl.studio.tic, 0, anim, TIC80_WIDTH, TIC_FONT_HEIGHT+1, (tic_color_red));
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.text(impl.studio.tic, impl.popup.message,
|
2018-08-03 22:22:58 +02:00
|
|
|
(s32)(TIC80_WIDTH - strlen(impl.popup.message)*TIC_FONT_WIDTH)/2,
|
2018-08-03 23:36:53 +02:00
|
|
|
anim + 1, (tic_color_white), false);
|
2017-12-08 13:03:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
static void renderStudio()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2017-11-20 09:29:52 +01:00
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
showTooltip("");
|
|
|
|
|
|
|
|
{
|
2017-12-10 10:12:09 +01:00
|
|
|
const tic_sfx* sfx = NULL;
|
|
|
|
const tic_music* music = NULL;
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
switch(impl.mode)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
|
|
|
case TIC_RUN_MODE:
|
2018-02-15 11:36:15 +01:00
|
|
|
sfx = &impl.studio.tic->ram.sfx;
|
|
|
|
music = &impl.studio.tic->ram.music;
|
2017-09-26 08:59:34 +02:00
|
|
|
break;
|
|
|
|
case TIC_START_MODE:
|
|
|
|
case TIC_DIALOG_MODE:
|
|
|
|
case TIC_MENU_MODE:
|
|
|
|
case TIC_SURF_MODE:
|
2018-03-12 07:40:48 +01:00
|
|
|
sfx = &impl.config->cart.bank0.sfx;
|
|
|
|
music = &impl.config->cart.bank0.music;
|
2017-09-26 08:59:34 +02:00
|
|
|
break;
|
|
|
|
default:
|
2018-02-15 11:36:15 +01:00
|
|
|
sfx = &impl.studio.tic->cart.banks[impl.bank.index.sfx].sfx;
|
|
|
|
music = &impl.studio.tic->cart.banks[impl.bank.index.music].music;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.tick_start(impl.studio.tic, sfx, music);
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
switch(impl.mode)
|
2017-09-26 08:59:34 +02:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
case TIC_START_MODE: impl.start->tick(impl.start); break;
|
|
|
|
case TIC_CONSOLE_MODE: impl.console->tick(impl.console); break;
|
|
|
|
case TIC_RUN_MODE: impl.run->tick(impl.run); break;
|
2017-12-14 12:38:06 +01:00
|
|
|
case TIC_CODE_MODE:
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Code* code = impl.editor[impl.bank.index.code].code;
|
2017-12-14 12:38:06 +01:00
|
|
|
code->tick(code);
|
|
|
|
}
|
|
|
|
break;
|
2017-12-14 15:08:04 +01:00
|
|
|
case TIC_SPRITE_MODE:
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Sprite* sprite = impl.editor[impl.bank.index.sprites].sprite;
|
2017-12-14 15:08:04 +01:00
|
|
|
sprite->tick(sprite);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TIC_MAP_MODE:
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Map* map = impl.editor[impl.bank.index.map].map;
|
2017-12-14 15:08:04 +01:00
|
|
|
map->tick(map);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TIC_SFX_MODE:
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Sfx* sfx = impl.editor[impl.bank.index.sfx].sfx;
|
2017-12-14 15:08:04 +01:00
|
|
|
sfx->tick(sfx);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TIC_MUSIC_MODE:
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Music* music = impl.editor[impl.bank.index.music].music;
|
2017-12-14 15:08:04 +01:00
|
|
|
music->tick(music);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
case TIC_WORLD_MODE: impl.world->tick(impl.world); break;
|
|
|
|
case TIC_DIALOG_MODE: impl.dialog->tick(impl.dialog); break;
|
|
|
|
case TIC_MENU_MODE: impl.menu->tick(impl.menu); break;
|
|
|
|
case TIC_SURF_MODE: impl.surf->tick(impl.surf); break;
|
2017-09-26 08:59:34 +02:00
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
2017-12-08 13:03:59 +01:00
|
|
|
drawPopup();
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2017-11-20 09:29:52 +01:00
|
|
|
if(getConfig()->noSound)
|
2018-02-06 20:15:56 +01:00
|
|
|
memset(tic->ram.registers, 0, sizeof tic->ram.registers);
|
2017-11-20 09:29:52 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->api.tick_end(impl.studio.tic);
|
2018-01-24 10:20:05 +01:00
|
|
|
}
|
|
|
|
|
2017-09-26 08:59:34 +02:00
|
|
|
static void updateSystemFont()
|
|
|
|
{
|
2018-08-03 22:22:58 +02:00
|
|
|
memset(impl.studio.tic->font.data, 0, sizeof(tic_font));
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
for(s32 i = 0; i < TIC_FONT_CHARS; i++)
|
|
|
|
for(s32 y = 0; y < TIC_SPRITESIZE; y++)
|
|
|
|
for(s32 x = 0; x < TIC_SPRITESIZE; x++)
|
2018-03-12 07:40:48 +01:00
|
|
|
if(tic_tool_peek4(&impl.config->cart.bank0.sprites.data[i], TIC_SPRITESIZE*(y+1) - x-1))
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tic->font.data[i*BITS_IN_BYTE+y] |= 1 << x;
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void studioConfigChanged()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Code* code = impl.editor[impl.bank.index.code].code;
|
2017-12-14 12:38:06 +01:00
|
|
|
if(code->update)
|
|
|
|
code->update(code);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
updateSystemFont();
|
2018-02-26 11:56:54 +01:00
|
|
|
|
|
|
|
getSystem()->updateConfig();
|
2017-09-26 08:59:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
u32 unzip(u8** dest, const u8* source, size_t size)
|
|
|
|
{
|
|
|
|
// TODO: remove this size
|
|
|
|
enum{DestSize = 10*1024*1024};
|
|
|
|
|
|
|
|
unsigned long destSize = DestSize;
|
|
|
|
|
2018-02-06 20:15:56 +01:00
|
|
|
u8* buffer = (u8*)malloc(destSize);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
if(buffer)
|
|
|
|
{
|
|
|
|
if(uncompress(buffer, &destSize, source, (unsigned long)size) == Z_OK)
|
|
|
|
{
|
2018-02-06 20:15:56 +01:00
|
|
|
*dest = (u8*)malloc(destSize+1);
|
2017-09-26 08:59:34 +02:00
|
|
|
memcpy(*dest, buffer, destSize);
|
|
|
|
(*dest)[destSize] = '\0';
|
|
|
|
}
|
|
|
|
|
2018-02-06 20:15:56 +01:00
|
|
|
free(buffer);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
|
|
|
return destSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-01-10 09:18:55 +01:00
|
|
|
static void initKeymap()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
FileSystem* fs = impl.fs;
|
2018-01-10 09:18:55 +01:00
|
|
|
|
2018-02-13 11:33:55 +01:00
|
|
|
s32 size = 0;
|
|
|
|
u8* data = (u8*)fsLoadFile(fs, KEYMAP_DAT_PATH, &size);
|
2018-01-10 09:18:55 +01:00
|
|
|
|
2018-02-13 11:33:55 +01:00
|
|
|
if(data)
|
|
|
|
{
|
|
|
|
if(size == KEYMAP_SIZE)
|
|
|
|
memcpy(getKeymap(), data, KEYMAP_SIZE);
|
2018-01-10 09:18:55 +01:00
|
|
|
|
2018-02-13 11:33:55 +01:00
|
|
|
free(data);
|
|
|
|
}
|
2018-01-10 09:18:55 +01:00
|
|
|
}
|
|
|
|
|
2018-01-24 12:55:13 +01:00
|
|
|
static void processMouseStates()
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
for(int i = 0; i < COUNT_OF(impl.mouse.state); i++)
|
|
|
|
impl.mouse.state[i].click = false;
|
2018-01-24 12:55:13 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2018-01-24 12:55:13 +01:00
|
|
|
|
2018-02-12 12:51:57 +01:00
|
|
|
tic->ram.vram.vars.cursor.sprite = tic_cursor_arrow;
|
|
|
|
tic->ram.vram.vars.cursor.system = true;
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
for(int i = 0; i < COUNT_OF(impl.mouse.state); i++)
|
2018-01-24 12:55:13 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
MouseState* state = &impl.mouse.state[i];
|
2018-01-24 12:55:13 +01:00
|
|
|
|
2018-02-01 16:32:31 +01:00
|
|
|
if(!state->down && (tic->ram.input.mouse.btns & (1 << i)))
|
2018-01-24 12:55:13 +01:00
|
|
|
{
|
|
|
|
state->down = true;
|
|
|
|
|
|
|
|
state->start.x = tic->ram.input.mouse.x;
|
|
|
|
state->start.y = tic->ram.input.mouse.y;
|
|
|
|
}
|
2018-02-01 16:32:31 +01:00
|
|
|
else if(state->down && !(tic->ram.input.mouse.btns & (1 << i)))
|
2018-01-24 12:55:13 +01:00
|
|
|
{
|
|
|
|
state->end.x = tic->ram.input.mouse.x;
|
|
|
|
state->end.y = tic->ram.input.mouse.y;
|
|
|
|
|
|
|
|
state->click = true;
|
|
|
|
state->down = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-19 11:13:44 +01:00
|
|
|
static void studioTick()
|
2018-01-24 10:20:05 +01:00
|
|
|
{
|
2018-02-01 17:11:59 +01:00
|
|
|
processShortcuts();
|
2018-01-24 12:55:13 +01:00
|
|
|
processMouseStates();
|
2018-01-24 15:56:55 +01:00
|
|
|
processGamepadMapping();
|
2018-01-24 12:55:13 +01:00
|
|
|
|
2018-01-24 10:20:05 +01:00
|
|
|
renderStudio();
|
2018-01-04 13:22:33 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
tic_mem* tic = impl.studio.tic;
|
2018-01-24 10:20:05 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
tic_scanline scanline = NULL;
|
2018-02-27 11:42:08 +01:00
|
|
|
tic_overline overline = NULL;
|
2018-01-24 10:20:05 +01:00
|
|
|
void* data = NULL;
|
2018-01-04 22:38:59 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
switch(impl.mode)
|
2018-01-22 10:59:34 +01:00
|
|
|
{
|
2018-01-24 10:20:05 +01:00
|
|
|
case TIC_RUN_MODE:
|
|
|
|
scanline = tic->api.scanline;
|
2018-02-27 11:42:08 +01:00
|
|
|
overline = tic->api.overline;
|
2018-01-24 10:20:05 +01:00
|
|
|
break;
|
|
|
|
case TIC_SPRITE_MODE:
|
2017-12-31 11:15:05 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Sprite* sprite = impl.editor[impl.bank.index.sprites].sprite;
|
2018-02-27 11:42:08 +01:00
|
|
|
overline = sprite->overline;
|
|
|
|
scanline = sprite->scanline;
|
2018-01-24 10:20:05 +01:00
|
|
|
data = sprite;
|
2018-02-27 12:55:25 +01:00
|
|
|
memcpy(&tic->ram.vram.palette, getBankPalette(), sizeof(tic_palette));
|
2017-12-31 11:15:05 +01:00
|
|
|
}
|
2018-01-24 10:20:05 +01:00
|
|
|
break;
|
|
|
|
case TIC_MAP_MODE:
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
Map* map = impl.editor[impl.bank.index.map].map;
|
2018-02-27 11:42:08 +01:00
|
|
|
overline = map->overline;
|
2018-02-27 12:55:25 +01:00
|
|
|
scanline = map->scanline;
|
2018-01-24 10:20:05 +01:00
|
|
|
data = map;
|
2018-02-27 12:55:25 +01:00
|
|
|
memcpy(&tic->ram.vram.palette, getBankPalette(), sizeof(tic_palette));
|
2018-01-24 10:20:05 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2018-03-12 07:40:48 +01:00
|
|
|
memcpy(&tic->ram.vram.palette, &impl.config->cart.bank0.palette, sizeof(tic_palette));
|
2018-01-24 10:20:05 +01:00
|
|
|
break;
|
2017-11-02 21:08:40 +01:00
|
|
|
}
|
2017-10-16 05:03:05 +02:00
|
|
|
|
2018-02-27 11:42:08 +01:00
|
|
|
tic->api.blit(tic, scanline, overline, data);
|
2017-09-26 08:59:34 +02:00
|
|
|
|
2018-02-19 11:13:44 +01:00
|
|
|
recordFrame(tic->screen);
|
|
|
|
drawDesyncLabel(tic->screen);
|
|
|
|
|
2018-01-24 10:20:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-14 13:14:05 +01:00
|
|
|
static void studioClose()
|
2018-01-24 10:20:05 +01:00
|
|
|
{
|
2018-02-26 11:56:54 +01:00
|
|
|
free((void*)getConfig()->crtShader);
|
|
|
|
|
2017-12-13 19:14:03 +01:00
|
|
|
{
|
2017-12-14 15:19:35 +01:00
|
|
|
for(s32 i = 0; i < TIC_EDITOR_BANKS; i++)
|
2017-12-14 15:08:04 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
free(impl.editor[i].code);
|
|
|
|
free(impl.editor[i].sprite);
|
|
|
|
free(impl.editor[i].map);
|
|
|
|
free(impl.editor[i].sfx);
|
|
|
|
free(impl.editor[i].music);
|
2017-12-14 15:08:04 +01:00
|
|
|
}
|
2017-12-14 12:38:06 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
free(impl.start);
|
|
|
|
free(impl.console);
|
|
|
|
free(impl.run);
|
|
|
|
free(impl.world);
|
|
|
|
free(impl.config);
|
|
|
|
free(impl.dialog);
|
|
|
|
free(impl.menu);
|
|
|
|
free(impl.surf);
|
2017-12-13 19:14:03 +01:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.tic80local)
|
|
|
|
tic80_delete((tic80*)impl.tic80local);
|
2018-02-07 16:21:24 +01:00
|
|
|
}
|
|
|
|
|
2018-02-14 13:14:05 +01:00
|
|
|
Studio* studioInit(s32 argc, char **argv, s32 samplerate, const char* folder, System* system)
|
|
|
|
{
|
|
|
|
setbuf(stdout, NULL);
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.argc = argc;
|
|
|
|
impl.argv = argv;
|
|
|
|
impl.samplerate = samplerate;
|
2018-02-14 13:14:05 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.system = system;
|
2018-02-14 13:14:05 +01:00
|
|
|
|
2018-02-27 21:17:35 +01:00
|
|
|
if(argc > 1 && fsExists(argv[1]))
|
|
|
|
{
|
|
|
|
char name[FILENAME_MAX];
|
|
|
|
fsBasename(argv[1], name);
|
|
|
|
impl.fs = createFileSystem(name);
|
|
|
|
}
|
|
|
|
else impl.fs = createFileSystem(folder);
|
2018-02-14 13:14:05 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.tic80local = (tic80_local*)tic80_create(impl.samplerate);
|
|
|
|
impl.studio.tic = impl.tic80local->memory;
|
2018-02-14 13:14:05 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
for(s32 i = 0; i < TIC_EDITOR_BANKS; i++)
|
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.editor[i].code = calloc(1, sizeof(Code));
|
|
|
|
impl.editor[i].sprite = calloc(1, sizeof(Sprite));
|
|
|
|
impl.editor[i].map = calloc(1, sizeof(Map));
|
|
|
|
impl.editor[i].sfx = calloc(1, sizeof(Sfx));
|
|
|
|
impl.editor[i].music = calloc(1, sizeof(Music));
|
2018-02-14 13:14:05 +01:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.start = calloc(1, sizeof(Start));
|
|
|
|
impl.console = calloc(1, sizeof(Console));
|
|
|
|
impl.run = calloc(1, sizeof(Run));
|
|
|
|
impl.world = calloc(1, sizeof(World));
|
|
|
|
impl.config = calloc(1, sizeof(Config));
|
|
|
|
impl.dialog = calloc(1, sizeof(Dialog));
|
|
|
|
impl.menu = calloc(1, sizeof(Menu));
|
|
|
|
impl.surf = calloc(1, sizeof(Surf));
|
2018-02-14 13:14:05 +01:00
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
fsMakeDir(impl.fs, TIC_LOCAL);
|
|
|
|
initConfig(impl.config, impl.studio.tic, impl.fs);
|
2018-02-14 13:14:05 +01:00
|
|
|
|
|
|
|
initKeymap();
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
initStart(impl.start, impl.studio.tic);
|
|
|
|
initConsole(impl.console, impl.studio.tic, impl.fs, impl.config, impl.argc, impl.argv);
|
2018-02-14 13:14:05 +01:00
|
|
|
initSurfMode();
|
|
|
|
|
|
|
|
initRunMode();
|
|
|
|
|
|
|
|
initModules();
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.console->skipStart)
|
2018-02-14 13:14:05 +01:00
|
|
|
{
|
|
|
|
setStudioMode(TIC_CONSOLE_MODE);
|
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
if(impl.console->goFullscreen)
|
2018-02-14 13:14:05 +01:00
|
|
|
{
|
|
|
|
goFullscreen();
|
|
|
|
}
|
|
|
|
|
2018-02-27 08:32:46 +01:00
|
|
|
if(impl.console->crtMonitor)
|
|
|
|
{
|
|
|
|
impl.config->data.crtMonitor = true;
|
|
|
|
}
|
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
impl.studio.tick = studioTick;
|
|
|
|
impl.studio.close = studioClose;
|
|
|
|
impl.studio.updateProject = updateStudioProject;
|
|
|
|
impl.studio.exit = exitStudio;
|
|
|
|
impl.studio.config = getConfig;
|
2018-08-09 19:24:15 +02:00
|
|
|
impl.studio.isGamepadMode = isGamepadMode;
|
2018-02-14 13:14:05 +01:00
|
|
|
|
2018-02-15 11:36:15 +01:00
|
|
|
return &impl.studio;
|
2018-02-14 13:14:05 +01:00
|
|
|
}
|
|
|
|
|
2018-02-14 12:52:10 +01:00
|
|
|
System* getSystem()
|
2018-02-07 16:21:24 +01:00
|
|
|
{
|
2018-02-15 11:36:15 +01:00
|
|
|
return impl.system;
|
2018-02-07 16:21:24 +01:00
|
|
|
}
|