opening TIC-80 sources
This commit is contained in:
1831
src/code.c
Normal file
1831
src/code.c
Normal file
File diff suppressed because it is too large
Load Diff
107
src/code.h
Normal file
107
src/code.h
Normal file
@@ -0,0 +1,107 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Code Code;
|
||||
typedef struct OutlineItem OutlineItem;
|
||||
|
||||
struct Code
|
||||
{
|
||||
tic_mem* tic;
|
||||
|
||||
char* data;
|
||||
|
||||
struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
char* position;
|
||||
char* selection;
|
||||
s32 tick;
|
||||
s32 column;
|
||||
};
|
||||
|
||||
char* mouseDownPosition;
|
||||
s32 delay;
|
||||
} cursor;
|
||||
|
||||
SDL_Rect rect;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 x;
|
||||
s32 y;
|
||||
|
||||
SDL_Point start;
|
||||
|
||||
bool active;
|
||||
bool gesture;
|
||||
|
||||
} scroll;
|
||||
|
||||
u8 colorBuffer[TIC_CODE_SIZE];
|
||||
|
||||
char status[STUDIO_TEXT_BUFFER_WIDTH+1];
|
||||
|
||||
u32 tickCounter;
|
||||
|
||||
struct History* history;
|
||||
struct History* cursorHistory;
|
||||
|
||||
enum
|
||||
{
|
||||
TEXT_EDIT_MODE,
|
||||
TEXT_FIND_MODE,
|
||||
TEXT_GOTO_MODE,
|
||||
TEXT_OUTLINE_MODE,
|
||||
} mode;
|
||||
|
||||
struct
|
||||
{
|
||||
char text[STUDIO_TEXT_BUFFER_WIDTH - sizeof "FIND:"];
|
||||
|
||||
char* prevPos;
|
||||
char* prevSel;
|
||||
} popup;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 line;
|
||||
} jump;
|
||||
|
||||
struct
|
||||
{
|
||||
OutlineItem* items;
|
||||
|
||||
s32 index;
|
||||
} outline;
|
||||
|
||||
void(*tick)(Code*);
|
||||
void(*escape)(Code*);
|
||||
void(*event)(Code*, StudioEvent);
|
||||
void(*update)(Code*);
|
||||
};
|
||||
|
||||
void initCode(Code*, tic_mem*);
|
||||
265
src/config.c
Normal file
265
src/config.c
Normal file
@@ -0,0 +1,265 @@
|
||||
// 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 "config.h"
|
||||
#include "fs.h"
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
static void readConfigVideoLength(Config* config, lua_State* lua)
|
||||
{
|
||||
lua_getglobal(lua, "GIF_LENGTH");
|
||||
|
||||
if(lua_isinteger(lua, -1))
|
||||
config->data.gifLength = (s32)lua_tointeger(lua, -1);
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
static void readConfigVideoScale(Config* config, lua_State* lua)
|
||||
{
|
||||
lua_getglobal(lua, "GIF_SCALE");
|
||||
|
||||
if(lua_isinteger(lua, -1))
|
||||
config->data.gifScale = (s32)lua_tointeger(lua, -1);
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
static void readConfigCheckNewVersion(Config* config, lua_State* lua)
|
||||
{
|
||||
lua_getglobal(lua, "CHECK_NEW_VERSION");
|
||||
|
||||
if(lua_isboolean(lua, -1))
|
||||
config->data.checkNewVersion = lua_toboolean(lua, -1);
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
static void readCursorTheme(Config* config, lua_State* lua)
|
||||
{
|
||||
lua_getfield(lua, -1, "CURSOR");
|
||||
|
||||
if(lua_type(lua, -1) == LUA_TTABLE)
|
||||
{
|
||||
{
|
||||
lua_getfield(lua, -1, "SPRITE");
|
||||
|
||||
if(lua_isinteger(lua, -1))
|
||||
{
|
||||
config->data.theme.cursor.sprite = (s32)lua_tointeger(lua, -1);
|
||||
}
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
{
|
||||
lua_getfield(lua, -1, "PIXEL_PERFECT");
|
||||
if(lua_isboolean(lua, -1))
|
||||
config->data.theme.cursor.pixelPerfect = lua_toboolean(lua, -1);
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
static void readPaletteMapTheme(Config* config, lua_State* lua)
|
||||
{
|
||||
lua_getfield(lua, -1, "PALMAP");
|
||||
|
||||
if(lua_isstring(lua, -1))
|
||||
{
|
||||
const char* val = lua_tostring(lua, -1);
|
||||
|
||||
s32 size = (s32)strlen(val);
|
||||
if(size == TIC_PALETTE_SIZE)
|
||||
for(s32 i = 0; i < size; i++)
|
||||
config->data.theme.palmap.data[i] = val[i] - (val[i] >= '0' && val[i] <= '9' ? '0' : 'a' - 10);
|
||||
}
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
static void readCodeTheme(Config* config, lua_State* lua)
|
||||
{
|
||||
lua_getfield(lua, -1, "CODE");
|
||||
|
||||
if(lua_type(lua, -1) == LUA_TTABLE)
|
||||
{
|
||||
static const char* Fields[] = {"BG", "STRING", "NUMBER", "KEYWORD", "API", "COMMENT", "SIGN", "VAR", "OTHER", "SELECT", "CURSOR"};
|
||||
|
||||
for(s32 i = 0; i < COUNT_OF(Fields); i++)
|
||||
{
|
||||
lua_getfield(lua, -1, Fields[i]);
|
||||
|
||||
if(lua_isinteger(lua, -1))
|
||||
((u8*)&config->data.theme.code)[i] = (u8)lua_tointeger(lua, -1);
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
static void readGamepadTheme(Config* config, lua_State* lua)
|
||||
{
|
||||
lua_getfield(lua, -1, "GAMEPAD");
|
||||
|
||||
if(lua_type(lua, -1) == LUA_TTABLE)
|
||||
{
|
||||
lua_getfield(lua, -1, "TOUCH");
|
||||
|
||||
if(lua_type(lua, -1) == LUA_TTABLE)
|
||||
{
|
||||
lua_getfield(lua, -1, "ALPHA");
|
||||
|
||||
if(lua_isinteger(lua, -1))
|
||||
config->data.theme.gamepad.touch.alpha = (u8)lua_tointeger(lua, -1);
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
static void readTheme(Config* config, lua_State* lua)
|
||||
{
|
||||
lua_getglobal(lua, "THEME");
|
||||
|
||||
if(lua_type(lua, -1) == LUA_TTABLE)
|
||||
{
|
||||
readPaletteMapTheme(config, lua);
|
||||
readCursorTheme(config, lua);
|
||||
readCodeTheme(config, lua);
|
||||
readGamepadTheme(config, lua);
|
||||
}
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
static void readConfig(Config* config)
|
||||
{
|
||||
lua_State* lua = luaL_newstate();
|
||||
|
||||
if(lua)
|
||||
{
|
||||
if(luaL_loadstring(lua, config->tic->config.code.data) == LUA_OK && lua_pcall(lua, 0, LUA_MULTRET, 0) == LUA_OK)
|
||||
{
|
||||
readConfigVideoLength(config, lua);
|
||||
readConfigVideoScale(config, lua);
|
||||
readConfigCheckNewVersion(config, lua);
|
||||
readTheme(config, lua);
|
||||
}
|
||||
|
||||
lua_close(lua);
|
||||
}
|
||||
}
|
||||
|
||||
static void update(Config* config, const u8* buffer, size_t size)
|
||||
{
|
||||
config->tic->api.load(&config->tic->config, buffer, size, true);
|
||||
|
||||
readConfig(config);
|
||||
studioConfigChanged();
|
||||
}
|
||||
|
||||
static void setDefault(Config* config)
|
||||
{
|
||||
{
|
||||
static const u8 DefaultBiosZip[] =
|
||||
{
|
||||
#include "../bin/assets/config.tic.dat"
|
||||
};
|
||||
|
||||
u8* embedBios = NULL;
|
||||
s32 size = unzip((u8**)&embedBios, DefaultBiosZip, sizeof DefaultBiosZip);
|
||||
|
||||
if(embedBios)
|
||||
{
|
||||
update(config, embedBios, size);
|
||||
|
||||
SDL_free(embedBios);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void saveConfig(Config* config, bool overwrite)
|
||||
{
|
||||
u8* buffer = SDL_malloc(sizeof(tic_cartridge));
|
||||
|
||||
if(buffer)
|
||||
{
|
||||
s32 size = config->tic->api.save(&config->tic->config, buffer);
|
||||
|
||||
fsSaveRootFile(config->fs, CONFIG_TIC_PATH, buffer, size, overwrite);
|
||||
|
||||
SDL_free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void reset(Config* config)
|
||||
{
|
||||
setDefault(config);
|
||||
saveConfig(config, true);
|
||||
}
|
||||
|
||||
static void save(Config* config)
|
||||
{
|
||||
SDL_memcpy(&config->tic->config, &config->tic->cart, sizeof(tic_cartridge));
|
||||
readConfig(config);
|
||||
saveConfig(config, true);
|
||||
|
||||
studioConfigChanged();
|
||||
}
|
||||
|
||||
void initConfig(Config* config, tic_mem* tic, FileSystem* fs)
|
||||
{
|
||||
*config = (Config)
|
||||
{
|
||||
.tic = tic,
|
||||
.save = save,
|
||||
.reset = reset,
|
||||
.fs = fs,
|
||||
};
|
||||
|
||||
setDefault(config);
|
||||
|
||||
s32 size = 0;
|
||||
u8* data = (u8*)fsLoadRootFile(fs, CONFIG_TIC_PATH, &size);
|
||||
|
||||
if(data)
|
||||
{
|
||||
update(config, data, size);
|
||||
|
||||
SDL_free(data);
|
||||
}
|
||||
else saveConfig(config, false);
|
||||
|
||||
tic->api.reset(tic);
|
||||
}
|
||||
40
src/config.h
Normal file
40
src/config.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Config Config;
|
||||
|
||||
struct Config
|
||||
{
|
||||
tic_mem* tic;
|
||||
struct FileSystem* fs;
|
||||
|
||||
StudioConfig data;
|
||||
|
||||
void(*save)(Config*);
|
||||
void(*reset)(Config*);
|
||||
};
|
||||
|
||||
void initConfig(Config* config, tic_mem* tic, struct FileSystem* fs);
|
||||
2481
src/console.c
Normal file
2481
src/console.c
Normal file
File diff suppressed because it is too large
Load Diff
92
src/console.h
Normal file
92
src/console.h
Normal file
@@ -0,0 +1,92 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CART_SAVE_OK,
|
||||
CART_SAVE_ERROR,
|
||||
CART_SAVE_MISSING_NAME,
|
||||
} CartSaveResult;
|
||||
|
||||
typedef struct HistoryItem HistoryItem;
|
||||
|
||||
struct HistoryItem
|
||||
{
|
||||
char* value;
|
||||
HistoryItem* prev;
|
||||
HistoryItem* next;
|
||||
};
|
||||
|
||||
typedef struct Console Console;
|
||||
|
||||
struct Console
|
||||
{
|
||||
struct Config* config;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 x;
|
||||
s32 y;
|
||||
s32 delay;
|
||||
} cursor;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 pos;
|
||||
s32 start;
|
||||
|
||||
bool active;
|
||||
} scroll;
|
||||
|
||||
char* buffer;
|
||||
u8* colorBuffer;
|
||||
|
||||
char inputBuffer[STUDIO_TEXT_BUFFER_WIDTH * STUDIO_TEXT_BUFFER_HEIGHT];
|
||||
size_t inputPosition;
|
||||
|
||||
tic_mem* tic;
|
||||
|
||||
struct FileSystem* fs;
|
||||
|
||||
char romName[FILENAME_MAX];
|
||||
char appPath[FILENAME_MAX];
|
||||
|
||||
HistoryItem* history;
|
||||
HistoryItem* historyHead;
|
||||
|
||||
u32 tickCounter;
|
||||
bool active;
|
||||
bool showGameMenu;
|
||||
|
||||
void(*load)(Console*, const char* name);
|
||||
void(*error)(Console*, const char*);
|
||||
void(*trace)(Console*, const char*, u8 color);
|
||||
void(*tick)(Console*);
|
||||
|
||||
CartSaveResult(*save)(Console*);
|
||||
};
|
||||
|
||||
void initConsole(Console*, tic_mem*, struct FileSystem* fs, struct Config* config, s32 argc, char **argv);
|
||||
26
src/defines.h
Normal file
26
src/defines.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
|
||||
#define STATIC_ASSERT(name, condition) typedef char static_assert_dummy_ ## name[(condition) ? 1 : -1]
|
||||
252
src/dialog.c
Normal file
252
src/dialog.c
Normal file
@@ -0,0 +1,252 @@
|
||||
// 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 "dialog.h"
|
||||
|
||||
static void drawButton(Dialog* dlg, const char* label, s32 x, s32 y, u8 color, u8 overColor, void(*callback)(Dialog* dlg), s32 id)
|
||||
{
|
||||
tic_mem* tic = dlg->tic;
|
||||
|
||||
enum {BtnWidth = 20, BtnHeight = 9};
|
||||
|
||||
SDL_Rect rect = {x, y, BtnWidth, BtnHeight};
|
||||
bool down = false;
|
||||
bool over = false;
|
||||
|
||||
if(checkMousePos(&rect))
|
||||
{
|
||||
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
over = true;
|
||||
|
||||
if(checkMouseDown(&rect, SDL_BUTTON_LEFT))
|
||||
{
|
||||
down = true;
|
||||
dlg->focus = id;
|
||||
}
|
||||
|
||||
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||||
callback(dlg);
|
||||
}
|
||||
|
||||
if(down)
|
||||
{
|
||||
tic->api.rect(tic, rect.x, rect.y+1, rect.w, rect.h, systemColor(tic_color_white));
|
||||
}
|
||||
else
|
||||
{
|
||||
tic->api.rect(tic, rect.x, rect.y+1, rect.w, rect.h, systemColor(tic_color_black));
|
||||
tic->api.rect(tic, rect.x, rect.y, rect.w, rect.h, systemColor(tic_color_white));
|
||||
}
|
||||
|
||||
s32 size = tic->api.text(tic, label, 0, -TIC_FONT_HEIGHT, 0);
|
||||
tic->api.text(tic, label, rect.x + (BtnWidth - size+1)/2, rect.y + (down?3:2), over ? overColor : color);
|
||||
|
||||
if(dlg->focus == id)
|
||||
{
|
||||
static const u8 Icon[] =
|
||||
{
|
||||
0b10000000,
|
||||
0b11000000,
|
||||
0b11100000,
|
||||
0b11000000,
|
||||
0b10000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
};
|
||||
|
||||
drawBitIcon(rect.x-5, rect.y+3, Icon, systemColor(tic_color_black));
|
||||
drawBitIcon(rect.x-5, rect.y+2, Icon, systemColor(tic_color_white));
|
||||
}
|
||||
}
|
||||
|
||||
static void onYes(Dialog* dlg)
|
||||
{
|
||||
dlg->callback(true, dlg->data);
|
||||
hideDialog();
|
||||
}
|
||||
|
||||
static void onNo(Dialog* dlg)
|
||||
{
|
||||
dlg->callback(false, dlg->data);
|
||||
hideDialog();
|
||||
}
|
||||
|
||||
static void processKeydown(Dialog* dlg, SDL_Keysym* keysum)
|
||||
{
|
||||
SDL_Scancode scancode = keysum->scancode;
|
||||
|
||||
switch(scancode)
|
||||
{
|
||||
case SDL_SCANCODE_LEFT:
|
||||
dlg->focus = (dlg->focus-1) % 2;
|
||||
playSystemSfx(2);
|
||||
break;
|
||||
case SDL_SCANCODE_RIGHT:
|
||||
case SDL_SCANCODE_TAB:
|
||||
dlg->focus = (dlg->focus+1) % 2;
|
||||
playSystemSfx(2);
|
||||
break;
|
||||
case SDL_SCANCODE_RETURN:
|
||||
case SDL_SCANCODE_SPACE:
|
||||
dlg->focus == 0 ? onYes(dlg) : onNo(dlg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void drawDialog(Dialog* dlg)
|
||||
{
|
||||
enum {Width = TIC80_WIDTH/2, Height = TIC80_HEIGHT/2-TOOLBAR_SIZE-1};
|
||||
|
||||
tic_mem* tic = dlg->tic;
|
||||
|
||||
SDL_Rect rect = {(TIC80_WIDTH - Width)/2, (TIC80_HEIGHT - Height)/2, Width, Height};
|
||||
|
||||
rect.x -= dlg->pos.x;
|
||||
rect.y -= dlg->pos.y;
|
||||
|
||||
SDL_Rect header = {rect.x, rect.y-(TOOLBAR_SIZE-2), rect.w, TOOLBAR_SIZE-1};
|
||||
|
||||
if(checkMousePos(&header))
|
||||
{
|
||||
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
|
||||
if(checkMouseDown(&header, SDL_BUTTON_LEFT))
|
||||
{
|
||||
if(!dlg->drag.active)
|
||||
{
|
||||
dlg->drag.start.x = getMouseX() + dlg->pos.x;
|
||||
dlg->drag.start.y = getMouseY() + dlg->pos.y;
|
||||
|
||||
dlg->drag.active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(dlg->drag.active)
|
||||
{
|
||||
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
|
||||
dlg->pos.x = dlg->drag.start.x - getMouseX();
|
||||
dlg->pos.y = dlg->drag.start.y - getMouseY();
|
||||
|
||||
SDL_Rect rect = {0, 0, TIC80_WIDTH, TIC80_HEIGHT};
|
||||
if(!checkMouseDown(&rect, SDL_BUTTON_LEFT))
|
||||
dlg->drag.active = false;
|
||||
}
|
||||
|
||||
tic->api.rect(tic, rect.x, rect.y, rect.w, rect.h, systemColor(tic_color_blue));
|
||||
tic->api.rect_border(tic, rect.x, rect.y, rect.w, rect.h, systemColor(tic_color_white));
|
||||
tic->api.line(tic, rect.x, rect.y+Height, rect.x+Width-1, rect.y+Height, systemColor(tic_color_black));
|
||||
tic->api.rect(tic, rect.x, rect.y-(TOOLBAR_SIZE-3), rect.w, TOOLBAR_SIZE-3, systemColor(tic_color_white));
|
||||
tic->api.line(tic, rect.x+1, rect.y-(TOOLBAR_SIZE-2), rect.x+Width-2, rect.y-(TOOLBAR_SIZE-2), systemColor(tic_color_white));
|
||||
|
||||
{
|
||||
static const char Label[] = "WARNING!";
|
||||
s32 size = tic->api.text(tic, Label, 0, -TIC_FONT_HEIGHT, 0);
|
||||
tic->api.text(tic, Label, rect.x + (Width - size)/2, rect.y-(TOOLBAR_SIZE-3), systemColor(tic_color_gray));
|
||||
}
|
||||
|
||||
{
|
||||
u8 chromakey = 14;
|
||||
tic->api.sprite_ex(tic, &tic->config.gfx, 2, rect.x+6, rect.y-4, 2, 2, &chromakey, 1, 1, tic_no_flip, tic_no_rotate);
|
||||
}
|
||||
|
||||
{
|
||||
for(s32 i = 0; i < dlg->rows; i++)
|
||||
{
|
||||
s32 size = tic->api.text(tic, dlg->text[i], 0, -TIC_FONT_HEIGHT, 0);
|
||||
|
||||
s32 x = rect.x + (Width - size)/2;
|
||||
s32 y = rect.y + (TIC_FONT_HEIGHT+1)*(i+1);
|
||||
tic->api.text(tic, dlg->text[i], x, y+1, systemColor(tic_color_black));
|
||||
tic->api.text(tic, dlg->text[i], x, y, systemColor(tic_color_white));
|
||||
}
|
||||
}
|
||||
|
||||
drawButton(dlg, "YES", rect.x + (Width/2 - 26), rect.y + 45, systemColor(tic_color_dark_red), systemColor(tic_color_red), onYes, 0);
|
||||
drawButton(dlg, "NO", rect.x + (Width/2 + 6), rect.y + 45, systemColor(tic_color_green), systemColor(tic_color_light_green), onNo, 1);
|
||||
}
|
||||
|
||||
static void tick(Dialog* dlg)
|
||||
{
|
||||
SDL_Event* event = NULL;
|
||||
while ((event = pollEvent()))
|
||||
{
|
||||
switch(event->type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
processKeydown(dlg, &event->key.keysym);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!dlg->init)
|
||||
{
|
||||
playSystemSfx(0);
|
||||
|
||||
dlg->init = true;
|
||||
}
|
||||
|
||||
SDL_memcpy(dlg->tic->ram.vram.screen.data, dlg->bg, sizeof dlg->tic->ram.vram.screen.data);
|
||||
|
||||
drawDialog(dlg);
|
||||
}
|
||||
|
||||
static void escape(Dialog* dlg)
|
||||
{
|
||||
dlg->callback(false, dlg->data);
|
||||
hideDialog();
|
||||
}
|
||||
|
||||
void initDialog(Dialog* dlg, tic_mem* tic, const char** text, s32 rows, DialogCallback callback, void* data)
|
||||
{
|
||||
*dlg = (Dialog)
|
||||
{
|
||||
.init = false,
|
||||
.tic = tic,
|
||||
.tick = tick,
|
||||
.escape = escape,
|
||||
.bg = dlg->bg,
|
||||
.callback = callback,
|
||||
.data = data,
|
||||
.text = text,
|
||||
.rows = rows,
|
||||
.focus = 0,
|
||||
.pos = {0, 0},
|
||||
.drag =
|
||||
{
|
||||
.start = {0, 0},
|
||||
.active = 0,
|
||||
},
|
||||
};
|
||||
|
||||
enum{Size = sizeof tic->ram.vram.screen.data};
|
||||
|
||||
if(!dlg->bg)
|
||||
dlg->bg = SDL_malloc(Size);
|
||||
|
||||
if(dlg->bg)
|
||||
SDL_memcpy(dlg->bg, tic->ram.vram.screen.data, Size);
|
||||
}
|
||||
54
src/dialog.h
Normal file
54
src/dialog.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Dialog Dialog;
|
||||
|
||||
struct Dialog
|
||||
{
|
||||
tic_mem* tic;
|
||||
|
||||
bool init;
|
||||
void* bg;
|
||||
DialogCallback callback;
|
||||
void* data;
|
||||
const char** text;
|
||||
s32 rows;
|
||||
|
||||
u32 focus;
|
||||
|
||||
SDL_Point pos;
|
||||
|
||||
struct
|
||||
{
|
||||
SDL_Point start;
|
||||
bool active;
|
||||
} drag;
|
||||
|
||||
void(*tick)(Dialog* Dialog);
|
||||
void(*escape)(Dialog* Dialog);
|
||||
};
|
||||
|
||||
void initDialog(Dialog* dialog, tic_mem* tic, const char** text, s32 rows, DialogCallback callback, void* data);
|
||||
346
src/ext/blip_buf.c
Normal file
346
src/ext/blip_buf.c
Normal file
@@ -0,0 +1,346 @@
|
||||
/* blip_buf $vers. http://www.slack.net/~ant/ */
|
||||
|
||||
#include "blip_buf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Library Copyright (C) 2003-2009 Shay Green. This library is free software;
|
||||
you can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
library is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#if defined (BLARGG_TEST) && BLARGG_TEST
|
||||
#include "blargg_test.h"
|
||||
#endif
|
||||
|
||||
/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000.
|
||||
Avoids constants that don't fit in 32 bits. */
|
||||
#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF
|
||||
typedef unsigned long fixed_t;
|
||||
enum { pre_shift = 32 };
|
||||
|
||||
#elif defined(ULLONG_MAX)
|
||||
typedef unsigned long long fixed_t;
|
||||
enum { pre_shift = 32 };
|
||||
|
||||
#else
|
||||
typedef unsigned fixed_t;
|
||||
enum { pre_shift = 0 };
|
||||
|
||||
#endif
|
||||
|
||||
enum { time_bits = pre_shift + 20 };
|
||||
|
||||
static fixed_t const time_unit = (fixed_t) 1 << time_bits;
|
||||
|
||||
enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */
|
||||
enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */
|
||||
|
||||
enum { half_width = 8 };
|
||||
enum { buf_extra = half_width*2 + end_frame_extra };
|
||||
enum { phase_bits = 5 };
|
||||
enum { phase_count = 1 << phase_bits };
|
||||
enum { delta_bits = 15 };
|
||||
enum { delta_unit = 1 << delta_bits };
|
||||
enum { frac_bits = time_bits - pre_shift };
|
||||
|
||||
/* We could eliminate avail and encode whole samples in offset, but that would
|
||||
limit the total buffered samples to blip_max_frame. That could only be
|
||||
increased by decreasing time_bits, which would reduce resample ratio accuracy.
|
||||
*/
|
||||
|
||||
/** Sample buffer that resamples to output rate and accumulates samples
|
||||
until they're read out */
|
||||
struct blip_t
|
||||
{
|
||||
fixed_t factor;
|
||||
fixed_t offset;
|
||||
int avail;
|
||||
int size;
|
||||
int integrator;
|
||||
};
|
||||
|
||||
typedef int buf_t;
|
||||
|
||||
/* probably not totally portable */
|
||||
#define SAMPLES( buf ) ((buf_t*) ((buf) + 1))
|
||||
|
||||
/* Arithmetic (sign-preserving) right shift */
|
||||
#define ARITH_SHIFT( n, shift ) \
|
||||
((n) >> (shift))
|
||||
|
||||
enum { max_sample = +32767 };
|
||||
enum { min_sample = -32768 };
|
||||
|
||||
#define CLAMP( n ) \
|
||||
{\
|
||||
if ( (short) n != n )\
|
||||
n = ARITH_SHIFT( n, 16 ) ^ max_sample;\
|
||||
}
|
||||
|
||||
static void check_assumptions( void )
|
||||
{
|
||||
int n;
|
||||
|
||||
#if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF
|
||||
#error "int must be at least 32 bits"
|
||||
#endif
|
||||
|
||||
assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */
|
||||
|
||||
n = max_sample * 2;
|
||||
CLAMP( n );
|
||||
assert( n == max_sample );
|
||||
|
||||
n = min_sample * 2;
|
||||
CLAMP( n );
|
||||
assert( n == min_sample );
|
||||
|
||||
assert( blip_max_ratio <= time_unit );
|
||||
assert( blip_max_frame <= (fixed_t) -1 >> time_bits );
|
||||
}
|
||||
|
||||
blip_t* blip_new( int size )
|
||||
{
|
||||
blip_t* m;
|
||||
assert( size >= 0 );
|
||||
|
||||
m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) );
|
||||
if ( m )
|
||||
{
|
||||
m->factor = time_unit / blip_max_ratio;
|
||||
m->size = size;
|
||||
blip_clear( m );
|
||||
check_assumptions();
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
void blip_delete( blip_t* m )
|
||||
{
|
||||
if ( m != NULL )
|
||||
{
|
||||
/* Clear fields in case user tries to use after freeing */
|
||||
memset( m, 0, sizeof *m );
|
||||
free( m );
|
||||
}
|
||||
}
|
||||
|
||||
void blip_set_rates( blip_t* m, double clock_rate, double sample_rate )
|
||||
{
|
||||
double factor = time_unit * sample_rate / clock_rate;
|
||||
m->factor = (fixed_t) factor;
|
||||
|
||||
/* Fails if clock_rate exceeds maximum, relative to sample_rate */
|
||||
assert( 0 <= factor - m->factor && factor - m->factor < 1 );
|
||||
|
||||
/* Avoid requiring math.h. Equivalent to
|
||||
m->factor = (int) ceil( factor ) */
|
||||
if ( m->factor < factor )
|
||||
m->factor++;
|
||||
|
||||
/* At this point, factor is most likely rounded up, but could still
|
||||
have been rounded down in the floating-point calculation. */
|
||||
}
|
||||
|
||||
void blip_clear( blip_t* m )
|
||||
{
|
||||
/* We could set offset to 0, factor/2, or factor-1. 0 is suitable if
|
||||
factor is rounded up. factor-1 is suitable if factor is rounded down.
|
||||
Since we don't know rounding direction, factor/2 accommodates either,
|
||||
with the slight loss of showing an error in half the time. Since for
|
||||
a 64-bit factor this is years, the halving isn't a problem. */
|
||||
|
||||
m->offset = m->factor / 2;
|
||||
m->avail = 0;
|
||||
m->integrator = 0;
|
||||
memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) );
|
||||
}
|
||||
|
||||
int blip_clocks_needed( const blip_t* m, int samples )
|
||||
{
|
||||
fixed_t needed;
|
||||
|
||||
/* Fails if buffer can't hold that many more samples */
|
||||
assert( samples >= 0 && m->avail + samples <= m->size );
|
||||
|
||||
needed = (fixed_t) samples * time_unit;
|
||||
if ( needed < m->offset )
|
||||
return 0;
|
||||
|
||||
return (int)((needed - m->offset + m->factor - 1) / m->factor);
|
||||
}
|
||||
|
||||
void blip_end_frame( blip_t* m, unsigned t )
|
||||
{
|
||||
fixed_t off = t * m->factor + m->offset;
|
||||
m->avail += off >> time_bits;
|
||||
m->offset = off & (time_unit - 1);
|
||||
|
||||
/* Fails if buffer size was exceeded */
|
||||
assert( m->avail <= m->size );
|
||||
}
|
||||
|
||||
int blip_samples_avail( const blip_t* m )
|
||||
{
|
||||
return m->avail;
|
||||
}
|
||||
|
||||
static void remove_samples( blip_t* m, int count )
|
||||
{
|
||||
buf_t* buf = SAMPLES( m );
|
||||
int remain = m->avail + buf_extra - count;
|
||||
m->avail -= count;
|
||||
|
||||
memmove( &buf [0], &buf [count], remain * sizeof buf [0] );
|
||||
memset( &buf [remain], 0, count * sizeof buf [0] );
|
||||
}
|
||||
|
||||
int blip_read_samples( blip_t* m, short out [], int count)
|
||||
{
|
||||
assert( count >= 0 );
|
||||
|
||||
if ( count > m->avail )
|
||||
count = m->avail;
|
||||
|
||||
if ( count )
|
||||
{
|
||||
int const step = 1;
|
||||
buf_t const* in = SAMPLES( m );
|
||||
buf_t const* end = in + count;
|
||||
int sum = m->integrator;
|
||||
do
|
||||
{
|
||||
/* Eliminate fraction */
|
||||
int s = ARITH_SHIFT( sum, delta_bits );
|
||||
|
||||
sum += *in++;
|
||||
|
||||
CLAMP( s );
|
||||
|
||||
int size = step;
|
||||
while(size--)
|
||||
*out++ = s;
|
||||
// out += step;
|
||||
|
||||
/* High-pass filter */
|
||||
sum -= s << (delta_bits - bass_shift);
|
||||
}
|
||||
while ( in != end );
|
||||
m->integrator = sum;
|
||||
|
||||
remove_samples( m, count );
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Things that didn't help performance on x86:
|
||||
__attribute__((aligned(128)))
|
||||
#define short int
|
||||
restrict
|
||||
*/
|
||||
|
||||
/* Sinc_Generator( 0.9, 0.55, 4.5 ) */
|
||||
static short const bl_step [phase_count + 1] [half_width] =
|
||||
{
|
||||
{ 43, -115, 350, -488, 1136, -914, 5861,21022},
|
||||
{ 44, -118, 348, -473, 1076, -799, 5274,21001},
|
||||
{ 45, -121, 344, -454, 1011, -677, 4706,20936},
|
||||
{ 46, -122, 336, -431, 942, -549, 4156,20829},
|
||||
{ 47, -123, 327, -404, 868, -418, 3629,20679},
|
||||
{ 47, -122, 316, -375, 792, -285, 3124,20488},
|
||||
{ 47, -120, 303, -344, 714, -151, 2644,20256},
|
||||
{ 46, -117, 289, -310, 634, -17, 2188,19985},
|
||||
{ 46, -114, 273, -275, 553, 117, 1758,19675},
|
||||
{ 44, -108, 255, -237, 471, 247, 1356,19327},
|
||||
{ 43, -103, 237, -199, 390, 373, 981,18944},
|
||||
{ 42, -98, 218, -160, 310, 495, 633,18527},
|
||||
{ 40, -91, 198, -121, 231, 611, 314,18078},
|
||||
{ 38, -84, 178, -81, 153, 722, 22,17599},
|
||||
{ 36, -76, 157, -43, 80, 824, -241,17092},
|
||||
{ 34, -68, 135, -3, 8, 919, -476,16558},
|
||||
{ 32, -61, 115, 34, -60, 1006, -683,16001},
|
||||
{ 29, -52, 94, 70, -123, 1083, -862,15422},
|
||||
{ 27, -44, 73, 106, -184, 1152,-1015,14824},
|
||||
{ 25, -36, 53, 139, -239, 1211,-1142,14210},
|
||||
{ 22, -27, 34, 170, -290, 1261,-1244,13582},
|
||||
{ 20, -20, 16, 199, -335, 1301,-1322,12942},
|
||||
{ 18, -12, -3, 226, -375, 1331,-1376,12293},
|
||||
{ 15, -4, -19, 250, -410, 1351,-1408,11638},
|
||||
{ 13, 3, -35, 272, -439, 1361,-1419,10979},
|
||||
{ 11, 9, -49, 292, -464, 1362,-1410,10319},
|
||||
{ 9, 16, -63, 309, -483, 1354,-1383, 9660},
|
||||
{ 7, 22, -75, 322, -496, 1337,-1339, 9005},
|
||||
{ 6, 26, -85, 333, -504, 1312,-1280, 8355},
|
||||
{ 4, 31, -94, 341, -507, 1278,-1205, 7713},
|
||||
{ 3, 35, -102, 347, -506, 1238,-1119, 7082},
|
||||
{ 1, 40, -110, 350, -499, 1190,-1021, 6464},
|
||||
{ 0, 43, -115, 350, -488, 1136, -914, 5861}
|
||||
};
|
||||
|
||||
/* Shifting by pre_shift allows calculation using unsigned int rather than
|
||||
possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient.
|
||||
And by having pre_shift 32, a 32-bit platform can easily do the shift by
|
||||
simply ignoring the low half. */
|
||||
|
||||
void blip_add_delta( blip_t* m, unsigned time, int delta )
|
||||
{
|
||||
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
|
||||
buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
|
||||
|
||||
int const phase_shift = frac_bits - phase_bits;
|
||||
int phase = fixed >> phase_shift & (phase_count - 1);
|
||||
short const* in = bl_step [phase];
|
||||
short const* rev = bl_step [phase_count - phase];
|
||||
|
||||
int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1);
|
||||
int delta2 = (delta * interp) >> delta_bits;
|
||||
delta -= delta2;
|
||||
|
||||
/* Fails if buffer size was exceeded */
|
||||
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
|
||||
|
||||
out [0] += in[0]*delta + in[half_width+0]*delta2;
|
||||
out [1] += in[1]*delta + in[half_width+1]*delta2;
|
||||
out [2] += in[2]*delta + in[half_width+2]*delta2;
|
||||
out [3] += in[3]*delta + in[half_width+3]*delta2;
|
||||
out [4] += in[4]*delta + in[half_width+4]*delta2;
|
||||
out [5] += in[5]*delta + in[half_width+5]*delta2;
|
||||
out [6] += in[6]*delta + in[half_width+6]*delta2;
|
||||
out [7] += in[7]*delta + in[half_width+7]*delta2;
|
||||
|
||||
in = rev;
|
||||
out [ 8] += in[7]*delta + in[7-half_width]*delta2;
|
||||
out [ 9] += in[6]*delta + in[6-half_width]*delta2;
|
||||
out [10] += in[5]*delta + in[5-half_width]*delta2;
|
||||
out [11] += in[4]*delta + in[4-half_width]*delta2;
|
||||
out [12] += in[3]*delta + in[3-half_width]*delta2;
|
||||
out [13] += in[2]*delta + in[2-half_width]*delta2;
|
||||
out [14] += in[1]*delta + in[1-half_width]*delta2;
|
||||
out [15] += in[0]*delta + in[0-half_width]*delta2;
|
||||
}
|
||||
|
||||
void blip_add_delta_fast( blip_t* m, unsigned time, int delta )
|
||||
{
|
||||
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
|
||||
buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
|
||||
|
||||
int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1);
|
||||
int delta2 = delta * interp;
|
||||
|
||||
/* Fails if buffer size was exceeded */
|
||||
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
|
||||
|
||||
out [7] += delta * delta_unit - delta2;
|
||||
out [8] += delta2;
|
||||
}
|
||||
72
src/ext/blip_buf.h
Normal file
72
src/ext/blip_buf.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/** \file
|
||||
Sample buffer that resamples from input clock rate to output sample rate */
|
||||
|
||||
/* blip_buf $vers */
|
||||
#ifndef BLIP_BUF_H
|
||||
#define BLIP_BUF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** First parameter of most functions is blip_t*, or const blip_t* if nothing
|
||||
is changed. */
|
||||
typedef struct blip_t blip_t;
|
||||
|
||||
/** Creates new buffer that can hold at most sample_count samples. Sets rates
|
||||
so that there are blip_max_ratio clocks per sample. Returns pointer to new
|
||||
buffer, or NULL if insufficient memory. */
|
||||
blip_t* blip_new( int sample_count );
|
||||
|
||||
/** Sets approximate input clock rate and output sample rate. For every
|
||||
clock_rate input clocks, approximately sample_rate samples are generated. */
|
||||
void blip_set_rates( blip_t*, double clock_rate, double sample_rate );
|
||||
|
||||
enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
|
||||
clock_rate must not be greater than sample_rate*blip_max_ratio. */
|
||||
blip_max_ratio = 1 << 20 };
|
||||
|
||||
/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
|
||||
void blip_clear( blip_t* );
|
||||
|
||||
/** Adds positive/negative delta into buffer at specified clock time. */
|
||||
void blip_add_delta( blip_t*, unsigned int clock_time, int delta );
|
||||
|
||||
/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
|
||||
void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta );
|
||||
|
||||
/** Length of time frame, in clocks, needed to make sample_count additional
|
||||
samples available. */
|
||||
int blip_clocks_needed( const blip_t*, int sample_count );
|
||||
|
||||
enum { /** Maximum number of samples that can be generated from one time frame. */
|
||||
blip_max_frame = 4000 };
|
||||
|
||||
/** Makes input clocks before clock_duration available for reading as output
|
||||
samples. Also begins new time frame at clock_duration, so that clock time 0 in
|
||||
the new time frame specifies the same clock as clock_duration in the old time
|
||||
frame specified. Deltas can have been added slightly past clock_duration (up to
|
||||
however many clocks there are in two output samples). */
|
||||
void blip_end_frame( blip_t*, unsigned int clock_duration );
|
||||
|
||||
/** Number of buffered samples available for reading. */
|
||||
int blip_samples_avail( const blip_t* );
|
||||
|
||||
/** Reads and removes at most 'count' samples and writes them to 'out'. If
|
||||
'stereo' is true, writes output to every other element of 'out', allowing easy
|
||||
interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed
|
||||
samples. Returns number of samples actually read. */
|
||||
int blip_read_samples( blip_t*, short out [], int count);
|
||||
|
||||
/** Frees buffer. No effect if NULL is passed. */
|
||||
void blip_delete( blip_t* );
|
||||
|
||||
|
||||
/* Deprecated */
|
||||
typedef blip_t blip_buffer_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
3590
src/ext/duktape/duk_config.h
Normal file
3590
src/ext/duktape/duk_config.h
Normal file
File diff suppressed because it is too large
Load Diff
1815
src/ext/duktape/duk_source_meta.json
Normal file
1815
src/ext/duktape/duk_source_meta.json
Normal file
File diff suppressed because it is too large
Load Diff
93368
src/ext/duktape/duktape.c
Normal file
93368
src/ext/duktape/duktape.c
Normal file
File diff suppressed because it is too large
Load Diff
1288
src/ext/duktape/duktape.h
Normal file
1288
src/ext/duktape/duktape.h
Normal file
File diff suppressed because it is too large
Load Diff
495
src/ext/file_dialog.c
Normal file
495
src/ext/file_dialog.c
Normal file
@@ -0,0 +1,495 @@
|
||||
// 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 "file_dialog.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
|
||||
#include <windows.h>
|
||||
#include <commdlg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define UTF8ToString(S) (wchar_t *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1)
|
||||
#define StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(wchar_t))
|
||||
|
||||
void file_dialog_load(file_dialog_load_callback callback, void* data)
|
||||
{
|
||||
OPENFILENAMEW ofn;
|
||||
SDL_zero(ofn);
|
||||
|
||||
wchar_t filename[MAX_PATH];
|
||||
memset(filename, 0, sizeof(filename));
|
||||
|
||||
ofn.lStructSize = sizeof(ofn);
|
||||
ofn.lpstrFile = filename;
|
||||
ofn.nMaxFile = sizeof(filename);
|
||||
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
|
||||
|
||||
if(GetOpenFileNameW(&ofn))
|
||||
{
|
||||
FILE* source = _wfopen(filename, L"rb");
|
||||
if (source)
|
||||
{
|
||||
fseek(source, 0, SEEK_END);
|
||||
s32 size = ftell(source);
|
||||
fseek(source, 0, SEEK_SET);
|
||||
|
||||
u8* buffer = malloc(size);
|
||||
|
||||
if(buffer)
|
||||
fread(buffer, size, 1, source);
|
||||
|
||||
fclose(source);
|
||||
|
||||
if(buffer)
|
||||
{
|
||||
const wchar_t* basename = wcsrchr(filename, L'\\');
|
||||
const wchar_t* name = basename ? basename + 1 : filename;
|
||||
|
||||
callback(StringToUTF8(name), buffer, size, data, 0);
|
||||
free(buffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callback(NULL, NULL, 0, data, 0);
|
||||
}
|
||||
|
||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
|
||||
{
|
||||
OPENFILENAMEW ofn;
|
||||
SDL_zero(ofn);
|
||||
|
||||
wchar_t filename[MAX_PATH];
|
||||
memset(filename, 0, sizeof(filename));
|
||||
wcscpy(filename, UTF8ToString(name));
|
||||
|
||||
ofn.lStructSize = sizeof(ofn);
|
||||
ofn.lpstrFile = filename;
|
||||
ofn.nMaxFile = sizeof(filename);
|
||||
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;
|
||||
|
||||
if(GetSaveFileNameW(&ofn))
|
||||
{
|
||||
FILE* file = _wfopen(filename, L"wb");
|
||||
|
||||
if(file)
|
||||
{
|
||||
fwrite(buffer, 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
#if !defined(__WINDOWS__)
|
||||
chmod(filename, mode);
|
||||
#endif
|
||||
callback(true, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
callback(false, data);
|
||||
}
|
||||
|
||||
#include <shlobj.h>
|
||||
|
||||
const char* folder_dialog(void* data)
|
||||
{
|
||||
|
||||
BROWSEINFOW bi = { 0 };
|
||||
bi.lpszTitle = L"Browse for folder...";
|
||||
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
|
||||
bi.lParam = (LPARAM) NULL;
|
||||
|
||||
LPITEMIDLIST pidl = SHBrowseForFolderW ( &bi );
|
||||
|
||||
if ( pidl != 0 )
|
||||
{
|
||||
wchar_t path[MAX_PATH];
|
||||
SHGetPathFromIDListW (pidl, path);
|
||||
|
||||
LPMALLOC imalloc = NULL;
|
||||
if ( SUCCEEDED( SHGetMalloc ( &imalloc )) )
|
||||
{
|
||||
imalloc->lpVtbl->Free (imalloc, pidl );
|
||||
imalloc->lpVtbl->Release (imalloc);
|
||||
}
|
||||
|
||||
{
|
||||
static char result[MAX_PATH];
|
||||
strcpy(result, StringToUTF8(path));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
|
||||
#include <emscripten.h>
|
||||
|
||||
void file_dialog_load(file_dialog_load_callback callback, void* data)
|
||||
{
|
||||
EM_ASM_
|
||||
({
|
||||
Module.showAddPopup(function(filename, rom)
|
||||
{
|
||||
if(filename == null || rom == null)
|
||||
{
|
||||
Runtime.dynCall('viiiii', $0, [0, 0, 0, $1, 0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var filePtr = Module._malloc(filename.length + 1);
|
||||
stringToUTF8(filename, filePtr, filename.length + 1);
|
||||
|
||||
var dataPtr = Module._malloc(rom.length);
|
||||
writeArrayToMemory(rom, dataPtr);
|
||||
|
||||
Runtime.dynCall('viiiii', $0, [filePtr, dataPtr, rom.length, $1, 0]);
|
||||
|
||||
Module._free(filePtr);
|
||||
Module._free(dataPtr);
|
||||
}
|
||||
});
|
||||
}, callback, data);
|
||||
}
|
||||
|
||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
|
||||
{
|
||||
EM_ASM_
|
||||
({
|
||||
var name = Pointer_stringify($0);
|
||||
var blob = new Blob([HEAPU8.subarray($1, $1 + $2)], {type: "application/octet-stream"});
|
||||
|
||||
Module.saveAs(blob, name);
|
||||
}, name, buffer, size);
|
||||
|
||||
callback(true, data);
|
||||
}
|
||||
|
||||
#elif defined(__LINUX__)
|
||||
|
||||
#if defined(__ARM_LINUX__)
|
||||
|
||||
void file_dialog_load(file_dialog_load_callback callback, void* data) {}
|
||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode) {}
|
||||
|
||||
#else
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
static void WaitForCleanup(void)
|
||||
{
|
||||
while (gtk_events_pending())
|
||||
gtk_main_iteration();
|
||||
}
|
||||
|
||||
void file_dialog_load(file_dialog_load_callback callback, void* data)
|
||||
{
|
||||
bool done = false;
|
||||
|
||||
if(gtk_init_check(NULL,NULL))
|
||||
{
|
||||
GtkWidget *dialog = gtk_file_chooser_dialog_new("Open File", NULL, GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Open", GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
|
||||
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT )
|
||||
{
|
||||
char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
||||
|
||||
FILE* source = fopen(filename, "rb");
|
||||
if (source)
|
||||
{
|
||||
fseek(source, 0, SEEK_END);
|
||||
s32 size = ftell(source);
|
||||
fseek(source, 0, SEEK_SET);
|
||||
|
||||
u8* buffer = malloc(size);
|
||||
|
||||
if(buffer && fread(buffer, size, 1, source));
|
||||
|
||||
fclose(source);
|
||||
|
||||
if(buffer)
|
||||
{
|
||||
const char* basename = strrchr(filename, '/');
|
||||
|
||||
mode_t mode = 0;
|
||||
{
|
||||
struct stat s;
|
||||
if(stat(filename, &s) == 0)
|
||||
mode = s.st_mode;
|
||||
}
|
||||
|
||||
callback(basename ? basename + 1 : filename, buffer, size, data, mode);
|
||||
free(buffer);
|
||||
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WaitForCleanup();
|
||||
gtk_widget_destroy(dialog);
|
||||
WaitForCleanup();
|
||||
}
|
||||
|
||||
if(!done)
|
||||
callback(NULL, NULL, 0, data, 0);
|
||||
}
|
||||
|
||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
|
||||
{
|
||||
bool done = false;
|
||||
|
||||
if(gtk_init_check(NULL,NULL))
|
||||
{
|
||||
GtkWidget *dialog = gtk_file_chooser_dialog_new("Save File", NULL, GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Save", GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
|
||||
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), name);
|
||||
|
||||
if(gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
||||
|
||||
FILE* file = fopen(filename, "wb");
|
||||
|
||||
if(file)
|
||||
{
|
||||
fwrite(buffer, 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
chmod(filename, mode);
|
||||
|
||||
callback(true, data);
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
WaitForCleanup();
|
||||
gtk_widget_destroy(dialog);
|
||||
WaitForCleanup();
|
||||
}
|
||||
|
||||
if(!done)
|
||||
callback(false, data);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#elif defined(__MACOSX__)
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
bool file_dialog_load_path(char* buffer);
|
||||
bool file_dialog_save_path(const char* name, char* buffer);
|
||||
|
||||
void file_dialog_load(file_dialog_load_callback callback, void* data)
|
||||
{
|
||||
bool done = false;
|
||||
|
||||
char filename[FILENAME_MAX];
|
||||
if(file_dialog_load_path(filename))
|
||||
{
|
||||
FILE* source = fopen(filename, "rb");
|
||||
if (source)
|
||||
{
|
||||
fseek(source, 0, SEEK_END);
|
||||
s32 size = ftell(source);
|
||||
fseek(source, 0, SEEK_SET);
|
||||
|
||||
u8* buffer = malloc(size);
|
||||
|
||||
if(buffer && fread(buffer, size, 1, source)){}
|
||||
|
||||
fclose(source);
|
||||
|
||||
if(buffer)
|
||||
{
|
||||
const char* basename = strrchr(filename, '/');
|
||||
|
||||
mode_t mode = 0;
|
||||
{
|
||||
struct stat s;
|
||||
if(stat(filename, &s) == 0)
|
||||
mode = s.st_mode;
|
||||
}
|
||||
|
||||
callback(basename ? basename + 1 : filename, buffer, size, data, mode);
|
||||
free(buffer);
|
||||
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!done)
|
||||
callback(NULL, NULL, 0, data, 0);
|
||||
}
|
||||
|
||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
|
||||
{
|
||||
bool done = false;
|
||||
|
||||
char filename[FILENAME_MAX];
|
||||
if(file_dialog_save_path(name, filename))
|
||||
{
|
||||
FILE* file = fopen(filename, "wb");
|
||||
|
||||
if(file)
|
||||
{
|
||||
fwrite(buffer, 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
chmod(filename, mode);
|
||||
|
||||
callback(true, data);
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!done)
|
||||
callback(false, data);
|
||||
}
|
||||
|
||||
#elif defined(__ANDROID__)
|
||||
|
||||
#include <jni.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
void file_dialog_load(file_dialog_load_callback callback, void* data)
|
||||
{
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
jobject activity = (jobject)SDL_AndroidGetActivity();
|
||||
jclass clazz = (*env)->GetObjectClass(env, activity);
|
||||
|
||||
jmethodID method_id = (*env)->GetMethodID(env, clazz, "loadFile", "()Ljava/lang/String;");
|
||||
jstring jPath = (jstring) (*env)->CallObjectMethod(env, activity, method_id);
|
||||
|
||||
const char *filename = (*env)->GetStringUTFChars(env, jPath, NULL);
|
||||
|
||||
(*env)->DeleteLocalRef(env, activity);
|
||||
(*env)->DeleteLocalRef(env, clazz);
|
||||
|
||||
bool done = false;
|
||||
|
||||
if(filename && strlen(filename))
|
||||
{
|
||||
FILE* source = fopen(filename, "rb");
|
||||
if (source)
|
||||
{
|
||||
fseek(source, 0, SEEK_END);
|
||||
s32 size = ftell(source);
|
||||
fseek(source, 0, SEEK_SET);
|
||||
|
||||
u8* buffer = malloc(size);
|
||||
|
||||
if(buffer && fread(buffer, size, 1, source)){}
|
||||
|
||||
fclose(source);
|
||||
|
||||
if(buffer)
|
||||
{
|
||||
const char* basename = strrchr(filename, '/');
|
||||
|
||||
mode_t mode = 0;
|
||||
{
|
||||
struct stat s;
|
||||
if(stat(filename, &s) == 0)
|
||||
mode = s.st_mode;
|
||||
}
|
||||
|
||||
callback(basename ? basename + 1 : filename, buffer, size, data, mode);
|
||||
free(buffer);
|
||||
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!done)
|
||||
callback(NULL, NULL, 0, data, 0);
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, jPath, filename);
|
||||
}
|
||||
|
||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
|
||||
{
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
jobject activity = (jobject)SDL_AndroidGetActivity();
|
||||
jclass clazz = (*env)->GetObjectClass(env, activity);
|
||||
|
||||
jstring jName = (*env)->NewStringUTF(env, name);
|
||||
jmethodID method_id = (*env)->GetMethodID(env, clazz, "saveFile", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
jstring jPath = (jstring) (*env)->CallObjectMethod(env, activity, method_id, jName);
|
||||
|
||||
const char *filename = (*env)->GetStringUTFChars(env, jPath, NULL);
|
||||
|
||||
(*env)->DeleteLocalRef(env, jName);
|
||||
(*env)->DeleteLocalRef(env, activity);
|
||||
(*env)->DeleteLocalRef(env, clazz);
|
||||
|
||||
bool done = false;
|
||||
|
||||
if(filename && strlen(filename))
|
||||
{
|
||||
FILE* file = fopen(filename, "wb");
|
||||
|
||||
if(file)
|
||||
{
|
||||
fwrite(buffer, 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
chmod(filename, mode);
|
||||
|
||||
callback(true, data);
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!done)
|
||||
callback(false, data);
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, jPath, filename);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void file_dialog_load(file_dialog_load_callback callback, void* data) {}
|
||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode) {}
|
||||
|
||||
#endif
|
||||
145
src/ext/file_dialog.cpp
Normal file
145
src/ext/file_dialog.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
// 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 "file_dialog.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#if defined(__WINRT__)
|
||||
|
||||
#include <ppltasks.h>
|
||||
#include <Robuffer.h>
|
||||
#include <wrl/client.h>
|
||||
#include <collection.h>
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Concurrency;
|
||||
using namespace Windows::Storage;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::Storage::Pickers;
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
#define UTF8ToString(S) (wchar_t *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1)
|
||||
#define StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(wchar_t))
|
||||
|
||||
byte* GetPointerToData(IBuffer^ buffer)
|
||||
{
|
||||
Object^ obj = buffer;
|
||||
ComPtr<IInspectable> insp(reinterpret_cast<IInspectable*>(obj));
|
||||
ComPtr<IBufferByteAccess> bufferByteAccess;
|
||||
insp.As(&bufferByteAccess);
|
||||
byte* data = nullptr;
|
||||
bufferByteAccess->Buffer(&data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
IBuffer^ IBufferFromArray(Array<unsigned char>^ data)
|
||||
{
|
||||
DataWriter^ dataWriter = ref new DataWriter();
|
||||
dataWriter->WriteBytes(data);
|
||||
return dataWriter->DetachBuffer();
|
||||
}
|
||||
|
||||
IBuffer^ IBufferFromPointer(LPBYTE pbData, DWORD cbData)
|
||||
{
|
||||
auto byteArray = new ArrayReference<unsigned char>(pbData, cbData);
|
||||
return IBufferFromArray(reinterpret_cast<Array<unsigned char>^>(byteArray));
|
||||
}
|
||||
|
||||
void file_dialog_load(file_dialog_load_callback callback, void* data)
|
||||
{
|
||||
FileOpenPicker^ openPicker = ref new FileOpenPicker();
|
||||
|
||||
openPicker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary;
|
||||
openPicker->FileTypeFilter->Append("*");
|
||||
|
||||
create_task(openPicker->PickSingleFileAsync()).then([callback, data](StorageFile^ file)
|
||||
{
|
||||
if (file)
|
||||
{
|
||||
create_task(file->OpenAsync(FileAccessMode::Read)).then([callback, data, file](IRandomAccessStream^ stream)
|
||||
{
|
||||
Buffer^ buffer = ref new Buffer((int)stream->Size);
|
||||
|
||||
create_task(stream->ReadAsync(buffer, buffer->Capacity, InputStreamOptions::Partial)).then([callback, data, file](IBuffer^ buffer)
|
||||
{
|
||||
auto ptr = GetPointerToData(buffer);
|
||||
auto size = buffer->Length;
|
||||
|
||||
std::wstring fooW(file->Name->Begin());
|
||||
callback(StringToUTF8(fooW.c_str()), ptr, size, data, 0);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(NULL, NULL, 0, data, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode)
|
||||
{
|
||||
FileSavePicker^ savePicker = ref new FileSavePicker();
|
||||
|
||||
savePicker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary;
|
||||
|
||||
savePicker->SuggestedFileName = ref new Platform::String(UTF8ToString(name));
|
||||
|
||||
auto allExtensions = ref new Platform::Collections::Vector<String^>();
|
||||
allExtensions->Append(".tic");
|
||||
allExtensions->Append(".html");
|
||||
allExtensions->Append(".gif");
|
||||
|
||||
savePicker->FileTypeChoices->Insert("TIC files", allExtensions);
|
||||
|
||||
create_task(savePicker->PickSaveFileAsync()).then([callback, data, buffer, size](StorageFile^ file)
|
||||
{
|
||||
if (file)
|
||||
{
|
||||
create_task(file->OpenAsync(FileAccessMode::ReadWrite)).then([callback, data, file, buffer, size](IRandomAccessStream^ stream)
|
||||
{
|
||||
auto ibuffer = IBufferFromPointer(const_cast<u8*>(buffer), (DWORD)size);
|
||||
|
||||
if (ibuffer)
|
||||
{
|
||||
create_task(FileIO::WriteBufferAsync(file, ibuffer)).then([callback, data, file]()
|
||||
{
|
||||
callback(true, data);
|
||||
});
|
||||
}
|
||||
else callback(false, data);
|
||||
});
|
||||
}
|
||||
else callback(false, data);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
const char* folder_dialog(void* data)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
41
src/ext/file_dialog.h
Normal file
41
src/ext/file_dialog.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tic80_types.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void(*file_dialog_load_callback)(const char* name, const u8* buffer, s32 size, void* data, u32 mode);
|
||||
typedef void(*file_dialog_save_callback)(bool result, void* data);
|
||||
|
||||
void file_dialog_load(file_dialog_load_callback callback, void* data);
|
||||
void file_dialog_save(file_dialog_save_callback callback, const char* name, const u8* buffer, size_t size, void* data, u32 mode);
|
||||
const char* folder_dialog(void* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
76
src/ext/file_dialog.m
Normal file
76
src/ext/file_dialog.m
Normal file
@@ -0,0 +1,76 @@
|
||||
// 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 "file_dialog.h"
|
||||
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
bool file_dialog_load_path(char* buffer)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
|
||||
NSOpenPanel *dialog = [NSOpenPanel openPanel];
|
||||
[dialog setAllowsMultipleSelection:NO];
|
||||
|
||||
bool success = false;
|
||||
if ( [dialog runModal] == NSModalResponseOK )
|
||||
{
|
||||
NSURL *url = [dialog URL];
|
||||
const char *utf8Path = [[url path] UTF8String];
|
||||
|
||||
strcpy( buffer, utf8Path);
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
||||
[pool release];
|
||||
[keyWindow makeKeyAndOrderFront:nil];
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool file_dialog_save_path(const char* name, char* buffer)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
|
||||
NSSavePanel *dialog = [NSSavePanel savePanel];
|
||||
[dialog setExtensionHidden:NO];
|
||||
|
||||
NSString *nameString = [NSString stringWithUTF8String: name];
|
||||
[dialog setNameFieldStringValue:nameString];
|
||||
|
||||
bool success = false;
|
||||
if ( [dialog runModal] == NSModalResponseOK )
|
||||
{
|
||||
NSURL *url = [dialog URL];
|
||||
const char *utf8Path = [[url path] UTF8String];
|
||||
|
||||
strcpy( buffer, utf8Path);
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
||||
[pool release];
|
||||
[keyWindow makeKeyAndOrderFront:nil];
|
||||
|
||||
return success;
|
||||
}
|
||||
421
src/ext/gif.c
Normal file
421
src/ext/gif.c
Normal file
@@ -0,0 +1,421 @@
|
||||
// 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gif.h"
|
||||
#include "gif_lib.h"
|
||||
|
||||
static gif_image* readGif(GifFileType *gif)
|
||||
{
|
||||
gif_image* image = NULL;
|
||||
|
||||
s32 error = 0;
|
||||
|
||||
if(gif)
|
||||
{
|
||||
if(gif->SHeight > 0 && gif->SWidth > 0)
|
||||
{
|
||||
s32 size = gif->SWidth * gif->SHeight * sizeof(GifPixelType);
|
||||
GifPixelType* screen = (GifPixelType*)malloc(size);
|
||||
|
||||
if(screen)
|
||||
{
|
||||
memset(screen, gif->SBackGroundColor, size);
|
||||
|
||||
GifRecordType record = UNDEFINED_RECORD_TYPE;
|
||||
|
||||
do
|
||||
{
|
||||
if(DGifGetRecordType(gif, &record) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (record)
|
||||
{
|
||||
case IMAGE_DESC_RECORD_TYPE:
|
||||
{
|
||||
if(DGifGetImageDesc(gif) == GIF_ERROR)
|
||||
error = gif->Error;
|
||||
|
||||
s32 row = gif->Image.Top;
|
||||
s32 col = gif->Image.Left;
|
||||
s32 width = gif->Image.Width;
|
||||
s32 height = gif->Image.Height;
|
||||
|
||||
if (gif->Image.Left + gif->Image.Width > gif->SWidth ||
|
||||
gif->Image.Top + gif->Image.Height > gif->SHeight)
|
||||
error = E_GIF_ERR_OPEN_FAILED;
|
||||
|
||||
if (gif->Image.Interlace)
|
||||
{
|
||||
s32 InterlacedOffset[] = { 0, 4, 2, 1 };
|
||||
s32 InterlacedJumps[] = { 8, 8, 4, 2 };
|
||||
|
||||
for (s32 i = 0; i < 4; i++)
|
||||
for (s32 j = row + InterlacedOffset[i]; j < row + height; j += InterlacedJumps[i])
|
||||
{
|
||||
if(DGifGetLine(gif, screen + j * gif->SWidth + col, width) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (s32 i = 0; i < height; i++, row++)
|
||||
{
|
||||
if(DGifGetLine(gif, screen + row * gif->SWidth + col, width) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EXTENSION_RECORD_TYPE:
|
||||
{
|
||||
s32 extCode = 0;
|
||||
GifByteType* extension = NULL;
|
||||
|
||||
if (DGifGetExtension(gif, &extCode, &extension) == GIF_ERROR)
|
||||
error = gif->Error;
|
||||
else
|
||||
{
|
||||
while (extension != NULL)
|
||||
{
|
||||
if(DGifGetExtensionNext(gif, &extension) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TERMINATE_RECORD_TYPE:
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if(error != E_GIF_SUCCEEDED)
|
||||
break;
|
||||
}
|
||||
while(record != TERMINATE_RECORD_TYPE);
|
||||
|
||||
if(error == E_GIF_SUCCEEDED)
|
||||
{
|
||||
|
||||
image = (gif_image*)malloc(sizeof(gif_image));
|
||||
|
||||
if(image)
|
||||
{
|
||||
memset(image, 0, sizeof(gif_image));
|
||||
image->buffer = screen;
|
||||
image->width = gif->SWidth;
|
||||
image->height = gif->SHeight;
|
||||
|
||||
ColorMapObject* colorMap = gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
|
||||
|
||||
image->colors = colorMap->ColorCount;
|
||||
|
||||
s32 size = image->colors * sizeof(gif_color);
|
||||
image->palette = malloc(size);
|
||||
|
||||
memcpy(image->palette, colorMap->Colors, size);
|
||||
}
|
||||
}
|
||||
else free(screen);
|
||||
}
|
||||
}
|
||||
|
||||
DGifCloseFile(gif, &error);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const void* data;
|
||||
s32 pos;
|
||||
} GifBuffer;
|
||||
|
||||
static int readBuffer(GifFileType* gif, GifByteType* data, int size)
|
||||
{
|
||||
GifBuffer* buffer = (GifBuffer*)gif->UserData;
|
||||
|
||||
memcpy(data, (const u8*)buffer->data + buffer->pos, size);
|
||||
buffer->pos += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
gif_image* gif_read_data(const void* data, s32 size)
|
||||
{
|
||||
GifBuffer buffer = {data, 0};
|
||||
GifFileType *gif = DGifOpen(&buffer, readBuffer, NULL);
|
||||
|
||||
return readGif(gif);
|
||||
}
|
||||
|
||||
static bool writeGif(GifFileType* gif, s32 width, s32 height, const u8* data, const gif_color* palette, u8 bpp)
|
||||
{
|
||||
bool result = false;
|
||||
s32 error = 0;
|
||||
|
||||
if(gif)
|
||||
{
|
||||
s32 colors = 1 << bpp;
|
||||
ColorMapObject* colorMap = GifMakeMapObject(colors, NULL);
|
||||
|
||||
memcpy(colorMap->Colors, palette, colors * sizeof(GifColorType));
|
||||
|
||||
if(EGifPutScreenDesc(gif, width, height, bpp, 0, colorMap) != GIF_ERROR)
|
||||
{
|
||||
if(EGifPutImageDesc(gif, 0, 0, width, height, false, NULL) != GIF_ERROR)
|
||||
{
|
||||
GifByteType* ptr = (GifByteType*)data;
|
||||
for (s32 i = 0; i < height; i++, ptr += width)
|
||||
{
|
||||
if (EGifPutLine(gif, ptr, width) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result = error == E_GIF_SUCCEEDED;
|
||||
}
|
||||
}
|
||||
|
||||
EGifCloseFile(gif, &error);
|
||||
GifFreeMapObject(colorMap);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int writeBuffer(GifFileType* gif, const GifByteType* data, int size)
|
||||
{
|
||||
GifBuffer* buffer = (GifBuffer*)gif->UserData;
|
||||
|
||||
memcpy((u8*)buffer->data + buffer->pos, data, size);
|
||||
buffer->pos += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
bool gif_write_data(const void* buffer, s32* size, s32 width, s32 height, const u8* data, const gif_color* palette, u8 bpp)
|
||||
{
|
||||
s32 error = 0;
|
||||
GifBuffer output = {buffer, 0};
|
||||
GifFileType* gif = EGifOpen(&output, writeBuffer, &error);
|
||||
|
||||
bool result = writeGif(gif, width, height, data, palette, bpp);
|
||||
|
||||
*size = output.pos;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void gif_close(gif_image* image)
|
||||
{
|
||||
if(image)
|
||||
{
|
||||
if(image->buffer) free(image->buffer);
|
||||
if(image->palette) free(image->palette);
|
||||
|
||||
free(image);
|
||||
}
|
||||
}
|
||||
|
||||
static bool AddLoop(GifFileType *gif)
|
||||
{
|
||||
{
|
||||
const char *nsle = "NETSCAPE2.0";
|
||||
const char subblock[] = {
|
||||
1, // always 1
|
||||
0, // little-endian loop counter:
|
||||
0 // 0 for infinite loop.
|
||||
};
|
||||
|
||||
EGifPutExtensionLeader(gif, APPLICATION_EXT_FUNC_CODE);
|
||||
EGifPutExtensionBlock(gif, 11, nsle);
|
||||
EGifPutExtensionBlock(gif, 3, subblock);
|
||||
EGifPutExtensionTrailer(gif);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const u8* toColor(const u8* ptr, gif_color* color)
|
||||
{
|
||||
color->b = *ptr++;
|
||||
color->g = *ptr++;
|
||||
color->r = *ptr++;
|
||||
ptr++;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool gif_write_animation(const void* buffer, s32* size, s32 width, s32 height, const u8* data, s32 frames, s32 fps, s32 scale)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
s32 swidth = width*scale, sheight = height*scale;
|
||||
s32 frameSize = width * height;
|
||||
|
||||
enum{Bpp = 8, PalSize = 1 << Bpp, PalStructSize = PalSize * sizeof(gif_color)};
|
||||
|
||||
s32 error = 0;
|
||||
GifBuffer output = {buffer, 0};
|
||||
GifFileType* gif = EGifOpen(&output, writeBuffer, &error);
|
||||
|
||||
if(gif)
|
||||
{
|
||||
EGifSetGifVersion(gif, true);
|
||||
|
||||
if(EGifPutScreenDesc(gif, swidth, sheight, Bpp, 0, NULL) != GIF_ERROR)
|
||||
{
|
||||
if(AddLoop(gif))
|
||||
{
|
||||
gif_color* palette = (gif_color*)malloc(PalStructSize);
|
||||
u8* screen = malloc(frameSize);
|
||||
u8* line = malloc(swidth);
|
||||
|
||||
for(s32 f = 0; f < frames; f++)
|
||||
{
|
||||
enum {DelayUnits = 100, MinDelay = 2};
|
||||
|
||||
s32 frame = (f * fps * MinDelay * 2 + 1) / (2 * DelayUnits);
|
||||
|
||||
if(frame >= frames)
|
||||
break;
|
||||
|
||||
s32 colors = 0;
|
||||
const u8* ptr = data + frameSize*frame*sizeof(u32);
|
||||
|
||||
{
|
||||
memset(palette, 0, PalStructSize);
|
||||
memset(screen, 0, frameSize);
|
||||
|
||||
for(s32 i = 0; i < frameSize; i++)
|
||||
{
|
||||
if(colors >= PalSize) break;
|
||||
|
||||
gif_color color;
|
||||
toColor(ptr + i*sizeof(u32), &color);
|
||||
|
||||
bool found = false;
|
||||
for(s32 c = 0; c < colors; c++)
|
||||
{
|
||||
if(memcmp(&palette[c], &color, sizeof(gif_color)) == 0)
|
||||
{
|
||||
found = true;
|
||||
screen[i] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
// TODO: check for last color in palette and try to find closest color
|
||||
screen[i] = colors;
|
||||
memcpy(&palette[colors], &color, sizeof(gif_color));
|
||||
colors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
GraphicsControlBlock gcb =
|
||||
{
|
||||
.DisposalMode = DISPOSE_DO_NOT,
|
||||
.UserInputFlag = false,
|
||||
.DelayTime = MinDelay,
|
||||
.TransparentColor = -1,
|
||||
};
|
||||
|
||||
u8 ext[4];
|
||||
EGifGCBToExtension(&gcb, ext);
|
||||
EGifPutExtension(gif, GRAPHICS_EXT_FUNC_CODE, sizeof ext, ext);
|
||||
}
|
||||
|
||||
ColorMapObject* colorMap = GifMakeMapObject(PalSize, NULL);
|
||||
memset(colorMap->Colors, 0, PalStructSize);
|
||||
memcpy(colorMap->Colors, palette, colors * sizeof(GifColorType));
|
||||
|
||||
if(EGifPutImageDesc(gif, 0, 0, swidth, sheight, false, colorMap) != GIF_ERROR)
|
||||
{
|
||||
for(s32 y = 0; y < height; y++)
|
||||
{
|
||||
for(s32 x = 0, pos = y*width; x < width; x++, pos++)
|
||||
{
|
||||
u8 color = screen[pos];
|
||||
for(s32 s = 0, pos = x*scale; s < scale; s++, pos++)
|
||||
line[pos] = color;
|
||||
}
|
||||
|
||||
for(s32 s = 0; s < scale; s++)
|
||||
{
|
||||
if (EGifPutLine(gif, line, swidth) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(error != E_GIF_SUCCEEDED) break;
|
||||
}
|
||||
|
||||
*size = output.pos;
|
||||
|
||||
result = error == E_GIF_SUCCEEDED;
|
||||
}
|
||||
|
||||
GifFreeMapObject(colorMap);
|
||||
|
||||
if(!result)
|
||||
break;
|
||||
}
|
||||
|
||||
free(line);
|
||||
free(screen);
|
||||
free(palette);
|
||||
}
|
||||
}
|
||||
|
||||
EGifCloseFile(gif, &error);
|
||||
|
||||
*size = output.pos;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
49
src/ext/gif.h
Normal file
49
src/ext/gif.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tic80_types.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 r;
|
||||
u8 g;
|
||||
u8 b;
|
||||
}gif_color;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8* buffer;
|
||||
|
||||
gif_color* palette;
|
||||
|
||||
s32 width;
|
||||
s32 height;
|
||||
|
||||
s32 colors;
|
||||
} gif_image;
|
||||
|
||||
gif_image* gif_read_data(const void* buffer, s32 size);
|
||||
bool gif_write_data(const void* buffer, s32* size, s32 width, s32 height, const u8* data, const gif_color* palette, u8 bpp);
|
||||
bool gif_write_animation(const void* buffer, s32* size, s32 width, s32 height, const u8* data, s32 frames, s32 fps, s32 scale);
|
||||
void gif_close(gif_image* image);
|
||||
287
src/ext/md5.c
Normal file
287
src/ext/md5.c
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
|
||||
* MD5 Message-Digest Algorithm (RFC 1321).
|
||||
*
|
||||
* Homepage:
|
||||
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
|
||||
*
|
||||
* Author:
|
||||
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
|
||||
*
|
||||
* This software was written by Alexander Peslyak in 2001. No copyright is
|
||||
* claimed, and the software is hereby placed in the public domain.
|
||||
* In case this attempt to disclaim copyright and place the software in the
|
||||
* public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* (This is a heavily cut-down "BSD license".)
|
||||
*
|
||||
* This differs from Colin Plumb's older public domain implementation in that
|
||||
* no exactly 32-bit integer data type is required (any 32-bit or wider
|
||||
* unsigned integer data type will do), there's no compile-time endianness
|
||||
* configuration, and the function prototypes match OpenSSL's. No code from
|
||||
* Colin Plumb's implementation has been reused; this comment merely compares
|
||||
* the properties of the two independent implementations.
|
||||
*
|
||||
* The primary goals of this implementation are portability and ease of use.
|
||||
* It is meant to be fast, but not as fast as possible. Some known
|
||||
* optimizations are not included to reduce source code size and avoid
|
||||
* compile-time configuration.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "md5.h"
|
||||
|
||||
/*
|
||||
* The basic MD5 functions.
|
||||
*
|
||||
* F and G are optimized compared to their RFC 1321 definitions for
|
||||
* architectures that lack an AND-NOT instruction, just like in Colin Plumb's
|
||||
* implementation.
|
||||
*/
|
||||
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
|
||||
#define H(x, y, z) (((x) ^ (y)) ^ (z))
|
||||
#define H2(x, y, z) ((x) ^ ((y) ^ (z)))
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
|
||||
/*
|
||||
* The MD5 transformation for all four rounds.
|
||||
*/
|
||||
#define STEP(f, a, b, c, d, x, t, s) \
|
||||
(a) += f((b), (c), (d)) + (x) + (t); \
|
||||
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
|
||||
(a) += (b);
|
||||
|
||||
/*
|
||||
* SET reads 4 input bytes in little-endian byte order and stores them in a
|
||||
* properly aligned word in host byte order.
|
||||
*
|
||||
* The check for little-endian architectures that tolerate unaligned memory
|
||||
* accesses is just an optimization. Nothing will break if it fails to detect
|
||||
* a suitable architecture.
|
||||
*
|
||||
* Unfortunately, this optimization may be a C strict aliasing rules violation
|
||||
* if the caller's data buffer has effective type that cannot be aliased by
|
||||
* MD5_u32plus. In practice, this problem may occur if these MD5 routines are
|
||||
* inlined into a calling function, or with future and dangerously advanced
|
||||
* link-time optimizations. For the time being, keeping these MD5 routines in
|
||||
* their own translation unit avoids the problem.
|
||||
*/
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
|
||||
#define SET(n) \
|
||||
(*(MD5_u32plus *)&ptr[(n) * 4])
|
||||
#define GET(n) \
|
||||
SET(n)
|
||||
#else
|
||||
#define SET(n) \
|
||||
(ctx->block[(n)] = \
|
||||
(MD5_u32plus)ptr[(n) * 4] | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
|
||||
((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
|
||||
#define GET(n) \
|
||||
(ctx->block[(n)])
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This processes one or more 64-byte data blocks, but does NOT update the bit
|
||||
* counters. There are no alignment requirements.
|
||||
*/
|
||||
static const void *body(MD5_CTX *ctx, const void *data, unsigned long size)
|
||||
{
|
||||
const unsigned char *ptr;
|
||||
MD5_u32plus a, b, c, d;
|
||||
MD5_u32plus saved_a, saved_b, saved_c, saved_d;
|
||||
|
||||
ptr = (const unsigned char *)data;
|
||||
|
||||
a = ctx->a;
|
||||
b = ctx->b;
|
||||
c = ctx->c;
|
||||
d = ctx->d;
|
||||
|
||||
do {
|
||||
saved_a = a;
|
||||
saved_b = b;
|
||||
saved_c = c;
|
||||
saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
|
||||
STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
|
||||
STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
|
||||
STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
|
||||
STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
|
||||
STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
|
||||
STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
|
||||
STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
|
||||
STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
|
||||
STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
|
||||
STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
|
||||
STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
|
||||
STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
|
||||
STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
|
||||
STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
|
||||
STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
|
||||
|
||||
/* Round 2 */
|
||||
STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
|
||||
STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
|
||||
STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
|
||||
STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
|
||||
STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
|
||||
STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
|
||||
STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
|
||||
STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
|
||||
STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
|
||||
STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
|
||||
STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
|
||||
STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
|
||||
STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
|
||||
STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
|
||||
STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
|
||||
STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
|
||||
|
||||
/* Round 3 */
|
||||
STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
|
||||
STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
|
||||
STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
|
||||
STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
|
||||
STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
|
||||
STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
|
||||
STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
|
||||
STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
|
||||
STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
|
||||
STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
|
||||
STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
|
||||
STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
|
||||
STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
|
||||
STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
|
||||
STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
|
||||
STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
|
||||
|
||||
/* Round 4 */
|
||||
STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
|
||||
STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
|
||||
STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
|
||||
STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
|
||||
STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
|
||||
STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
|
||||
STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
|
||||
STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
|
||||
STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
|
||||
STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
|
||||
STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
|
||||
STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
|
||||
STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
|
||||
STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
|
||||
STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
|
||||
STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
c += saved_c;
|
||||
d += saved_d;
|
||||
|
||||
ptr += 64;
|
||||
} while (size -= 64);
|
||||
|
||||
ctx->a = a;
|
||||
ctx->b = b;
|
||||
ctx->c = c;
|
||||
ctx->d = d;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void MD5_Init(MD5_CTX *ctx)
|
||||
{
|
||||
ctx->a = 0x67452301;
|
||||
ctx->b = 0xefcdab89;
|
||||
ctx->c = 0x98badcfe;
|
||||
ctx->d = 0x10325476;
|
||||
|
||||
ctx->lo = 0;
|
||||
ctx->hi = 0;
|
||||
}
|
||||
|
||||
void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size)
|
||||
{
|
||||
MD5_u32plus saved_lo;
|
||||
unsigned long used, available;
|
||||
|
||||
saved_lo = ctx->lo;
|
||||
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
|
||||
ctx->hi++;
|
||||
ctx->hi += size >> 29;
|
||||
|
||||
used = saved_lo & 0x3f;
|
||||
|
||||
if (used) {
|
||||
available = 64 - used;
|
||||
|
||||
if (size < available) {
|
||||
memcpy(&ctx->buffer[used], data, size);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&ctx->buffer[used], data, available);
|
||||
data = (const unsigned char *)data + available;
|
||||
size -= available;
|
||||
body(ctx, ctx->buffer, 64);
|
||||
}
|
||||
|
||||
if (size >= 64) {
|
||||
data = body(ctx, data, size & ~(unsigned long)0x3f);
|
||||
size &= 0x3f;
|
||||
}
|
||||
|
||||
memcpy(ctx->buffer, data, size);
|
||||
}
|
||||
|
||||
#define OUT(dst, src) \
|
||||
(dst)[0] = (unsigned char)(src); \
|
||||
(dst)[1] = (unsigned char)((src) >> 8); \
|
||||
(dst)[2] = (unsigned char)((src) >> 16); \
|
||||
(dst)[3] = (unsigned char)((src) >> 24);
|
||||
|
||||
void MD5_Final(unsigned char *result, MD5_CTX *ctx)
|
||||
{
|
||||
unsigned long used, available;
|
||||
|
||||
used = ctx->lo & 0x3f;
|
||||
|
||||
ctx->buffer[used++] = 0x80;
|
||||
|
||||
available = 64 - used;
|
||||
|
||||
if (available < 8) {
|
||||
memset(&ctx->buffer[used], 0, available);
|
||||
body(ctx, ctx->buffer, 64);
|
||||
used = 0;
|
||||
available = 64;
|
||||
}
|
||||
|
||||
memset(&ctx->buffer[used], 0, available - 8);
|
||||
|
||||
ctx->lo <<= 3;
|
||||
OUT(&ctx->buffer[56], ctx->lo)
|
||||
OUT(&ctx->buffer[60], ctx->hi)
|
||||
|
||||
body(ctx, ctx->buffer, 64);
|
||||
|
||||
OUT(&result[0], ctx->a)
|
||||
OUT(&result[4], ctx->b)
|
||||
OUT(&result[8], ctx->c)
|
||||
OUT(&result[12], ctx->d)
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
43
src/ext/md5.h
Normal file
43
src/ext/md5.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
|
||||
* MD5 Message-Digest Algorithm (RFC 1321).
|
||||
*
|
||||
* Homepage:
|
||||
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
|
||||
*
|
||||
* Author:
|
||||
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
|
||||
*
|
||||
* This software was written by Alexander Peslyak in 2001. No copyright is
|
||||
* claimed, and the software is hereby placed in the public domain.
|
||||
* In case this attempt to disclaim copyright and place the software in the
|
||||
* public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* See md5.c for more information.
|
||||
*/
|
||||
|
||||
#ifndef _MD5_H
|
||||
#define _MD5_H
|
||||
|
||||
/* Any 32-bit or wider unsigned integer data type will do */
|
||||
typedef unsigned int MD5_u32plus;
|
||||
|
||||
typedef struct {
|
||||
MD5_u32plus lo, hi;
|
||||
MD5_u32plus a, b, c, d;
|
||||
unsigned char buffer[64];
|
||||
MD5_u32plus block[16];
|
||||
} MD5_CTX;
|
||||
|
||||
extern void MD5_Init(MD5_CTX *ctx);
|
||||
extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
|
||||
extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
|
||||
|
||||
#endif
|
||||
14398
src/ext/moonscript.h
Normal file
14398
src/ext/moonscript.h
Normal file
File diff suppressed because it is too large
Load Diff
443
src/ext/net/SDL_net.h
Normal file
443
src/ext/net/SDL_net.h
Normal file
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
SDL_net: An example cross-platform network library for use with SDL
|
||||
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
|
||||
Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef _SDL_NET_H
|
||||
#define _SDL_NET_H
|
||||
|
||||
#ifdef WITHOUT_SDL
|
||||
#include <stdint.h>
|
||||
typedef uint8_t Uint8;
|
||||
typedef uint16_t Uint16;
|
||||
typedef uint32_t Uint32;
|
||||
|
||||
typedef struct SDLNet_version {
|
||||
Uint8 major;
|
||||
Uint8 minor;
|
||||
Uint8 patch;
|
||||
} SDLNet_version;
|
||||
|
||||
#else /* WITHOUT_SDL */
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_endian.h"
|
||||
#include "SDL_version.h"
|
||||
|
||||
typedef SDL_version SDLNet_version;
|
||||
|
||||
#endif /* WITHOUT_SDL */
|
||||
|
||||
#include "begin_code.h"
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
|
||||
*/
|
||||
#define SDL_NET_MAJOR_VERSION 2
|
||||
#define SDL_NET_MINOR_VERSION 0
|
||||
#define SDL_NET_PATCHLEVEL 1
|
||||
|
||||
/* This macro can be used to fill a version structure with the compile-time
|
||||
* version of the SDL_net library.
|
||||
*/
|
||||
#define SDL_NET_VERSION(X) \
|
||||
{ \
|
||||
(X)->major = SDL_NET_MAJOR_VERSION; \
|
||||
(X)->minor = SDL_NET_MINOR_VERSION; \
|
||||
(X)->patch = SDL_NET_PATCHLEVEL; \
|
||||
}
|
||||
|
||||
/* This function gets the version of the dynamically linked SDL_net library.
|
||||
it should NOT be used to fill a version structure, instead you should
|
||||
use the SDL_NET_VERSION() macro.
|
||||
*/
|
||||
extern DECLSPEC const SDLNet_version * SDLCALL SDLNet_Linked_Version(void);
|
||||
|
||||
/* Initialize/Cleanup the network API
|
||||
SDL must be initialized before calls to functions in this library,
|
||||
because this library uses utility functions from the SDL library.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDLNet_Init(void);
|
||||
extern DECLSPEC void SDLCALL SDLNet_Quit(void);
|
||||
|
||||
/***********************************************************************/
|
||||
/* IPv4 hostname resolution API */
|
||||
/***********************************************************************/
|
||||
|
||||
typedef struct {
|
||||
Uint32 host; /* 32-bit IPv4 host address */
|
||||
Uint16 port; /* 16-bit protocol port */
|
||||
} IPaddress;
|
||||
|
||||
/* Resolve a host name and port to an IP address in network form.
|
||||
If the function succeeds, it will return 0.
|
||||
If the host couldn't be resolved, the host portion of the returned
|
||||
address will be INADDR_NONE, and the function will return -1.
|
||||
If 'host' is NULL, the resolved host will be set to INADDR_ANY.
|
||||
*/
|
||||
#ifndef INADDR_ANY
|
||||
#define INADDR_ANY 0x00000000
|
||||
#endif
|
||||
#ifndef INADDR_NONE
|
||||
#define INADDR_NONE 0xFFFFFFFF
|
||||
#endif
|
||||
#ifndef INADDR_LOOPBACK
|
||||
#define INADDR_LOOPBACK 0x7f000001
|
||||
#endif
|
||||
#ifndef INADDR_BROADCAST
|
||||
#define INADDR_BROADCAST 0xFFFFFFFF
|
||||
#endif
|
||||
extern DECLSPEC int SDLCALL SDLNet_ResolveHost(IPaddress *address, const char *host, Uint16 port);
|
||||
|
||||
/* Resolve an ip address to a host name in canonical form.
|
||||
If the ip couldn't be resolved, this function returns NULL,
|
||||
otherwise a pointer to a static buffer containing the hostname
|
||||
is returned. Note that this function is not thread-safe.
|
||||
*/
|
||||
extern DECLSPEC const char * SDLCALL SDLNet_ResolveIP(const IPaddress *ip);
|
||||
|
||||
/* Get the addresses of network interfaces on this system.
|
||||
This returns the number of addresses saved in 'addresses'
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDLNet_GetLocalAddresses(IPaddress *addresses, int maxcount);
|
||||
|
||||
/***********************************************************************/
|
||||
/* TCP network API */
|
||||
/***********************************************************************/
|
||||
|
||||
typedef struct _TCPsocket *TCPsocket;
|
||||
|
||||
/* Open a TCP network socket
|
||||
If ip.host is INADDR_NONE or INADDR_ANY, this creates a local server
|
||||
socket on the given port, otherwise a TCP connection to the remote
|
||||
host and port is attempted. The address passed in should already be
|
||||
swapped to network byte order (addresses returned from
|
||||
SDLNet_ResolveHost() are already in the correct form).
|
||||
The newly created socket is returned, or NULL if there was an error.
|
||||
*/
|
||||
extern DECLSPEC TCPsocket SDLCALL SDLNet_TCP_Open(IPaddress *ip);
|
||||
|
||||
/* Accept an incoming connection on the given server socket.
|
||||
The newly created socket is returned, or NULL if there was an error.
|
||||
*/
|
||||
extern DECLSPEC TCPsocket SDLCALL SDLNet_TCP_Accept(TCPsocket server);
|
||||
|
||||
/* Get the IP address of the remote system associated with the socket.
|
||||
If the socket is a server socket, this function returns NULL.
|
||||
*/
|
||||
extern DECLSPEC IPaddress * SDLCALL SDLNet_TCP_GetPeerAddress(TCPsocket sock);
|
||||
|
||||
/* Send 'len' bytes of 'data' over the non-server socket 'sock'
|
||||
This function returns the actual amount of data sent. If the return value
|
||||
is less than the amount of data sent, then either the remote connection was
|
||||
closed, or an unknown socket error occurred.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDLNet_TCP_Send(TCPsocket sock, const void *data,
|
||||
int len);
|
||||
|
||||
/* Receive up to 'maxlen' bytes of data over the non-server socket 'sock',
|
||||
and store them in the buffer pointed to by 'data'.
|
||||
This function returns the actual amount of data received. If the return
|
||||
value is less than or equal to zero, then either the remote connection was
|
||||
closed, or an unknown socket error occurred.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDLNet_TCP_Recv(TCPsocket sock, void *data, int maxlen);
|
||||
|
||||
/* Close a TCP network socket */
|
||||
extern DECLSPEC void SDLCALL SDLNet_TCP_Close(TCPsocket sock);
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
/* UDP network API */
|
||||
/***********************************************************************/
|
||||
|
||||
/* The maximum channels on a a UDP socket */
|
||||
#define SDLNET_MAX_UDPCHANNELS 32
|
||||
/* The maximum addresses bound to a single UDP socket channel */
|
||||
#define SDLNET_MAX_UDPADDRESSES 4
|
||||
|
||||
typedef struct _UDPsocket *UDPsocket;
|
||||
typedef struct {
|
||||
int channel; /* The src/dst channel of the packet */
|
||||
Uint8 *data; /* The packet data */
|
||||
int len; /* The length of the packet data */
|
||||
int maxlen; /* The size of the data buffer */
|
||||
int status; /* packet status after sending */
|
||||
IPaddress address; /* The source/dest address of an incoming/outgoing packet */
|
||||
} UDPpacket;
|
||||
|
||||
/* Allocate/resize/free a single UDP packet 'size' bytes long.
|
||||
The new packet is returned, or NULL if the function ran out of memory.
|
||||
*/
|
||||
extern DECLSPEC UDPpacket * SDLCALL SDLNet_AllocPacket(int size);
|
||||
extern DECLSPEC int SDLCALL SDLNet_ResizePacket(UDPpacket *packet, int newsize);
|
||||
extern DECLSPEC void SDLCALL SDLNet_FreePacket(UDPpacket *packet);
|
||||
|
||||
/* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets,
|
||||
each 'size' bytes long.
|
||||
A pointer to the first packet in the array is returned, or NULL if the
|
||||
function ran out of memory.
|
||||
*/
|
||||
extern DECLSPEC UDPpacket ** SDLCALL SDLNet_AllocPacketV(int howmany, int size);
|
||||
extern DECLSPEC void SDLCALL SDLNet_FreePacketV(UDPpacket **packetV);
|
||||
|
||||
|
||||
/* Open a UDP network socket
|
||||
If 'port' is non-zero, the UDP socket is bound to a local port.
|
||||
The 'port' should be given in native byte order, but is used
|
||||
internally in network (big endian) byte order, in addresses, etc.
|
||||
This allows other systems to send to this socket via a known port.
|
||||
*/
|
||||
extern DECLSPEC UDPsocket SDLCALL SDLNet_UDP_Open(Uint16 port);
|
||||
|
||||
/* Set the percentage of simulated packet loss for packets sent on the socket.
|
||||
*/
|
||||
extern DECLSPEC void SDLCALL SDLNet_UDP_SetPacketLoss(UDPsocket sock, int percent);
|
||||
|
||||
/* Bind the address 'address' to the requested channel on the UDP socket.
|
||||
If the channel is -1, then the first unbound channel that has not yet
|
||||
been bound to the maximum number of addresses will be bound with
|
||||
the given address as it's primary address.
|
||||
If the channel is already bound, this new address will be added to the
|
||||
list of valid source addresses for packets arriving on the channel.
|
||||
If the channel is not already bound, then the address becomes the primary
|
||||
address, to which all outbound packets on the channel are sent.
|
||||
This function returns the channel which was bound, or -1 on error.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDLNet_UDP_Bind(UDPsocket sock, int channel, const IPaddress *address);
|
||||
|
||||
/* Unbind all addresses from the given channel */
|
||||
extern DECLSPEC void SDLCALL SDLNet_UDP_Unbind(UDPsocket sock, int channel);
|
||||
|
||||
/* Get the primary IP address of the remote system associated with the
|
||||
socket and channel. If the channel is -1, then the primary IP port
|
||||
of the UDP socket is returned -- this is only meaningful for sockets
|
||||
opened with a specific port.
|
||||
If the channel is not bound and not -1, this function returns NULL.
|
||||
*/
|
||||
extern DECLSPEC IPaddress * SDLCALL SDLNet_UDP_GetPeerAddress(UDPsocket sock, int channel);
|
||||
|
||||
/* Send a vector of packets to the the channels specified within the packet.
|
||||
If the channel specified in the packet is -1, the packet will be sent to
|
||||
the address in the 'src' member of the packet.
|
||||
Each packet will be updated with the status of the packet after it has
|
||||
been sent, -1 if the packet send failed.
|
||||
This function returns the number of packets sent.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDLNet_UDP_SendV(UDPsocket sock, UDPpacket **packets, int npackets);
|
||||
|
||||
/* Send a single packet to the specified channel.
|
||||
If the channel specified in the packet is -1, the packet will be sent to
|
||||
the address in the 'src' member of the packet.
|
||||
The packet will be updated with the status of the packet after it has
|
||||
been sent.
|
||||
This function returns 1 if the packet was sent, or 0 on error.
|
||||
|
||||
NOTE:
|
||||
The maximum size of the packet is limited by the MTU (Maximum Transfer Unit)
|
||||
of the transport medium. It can be as low as 250 bytes for some PPP links,
|
||||
and as high as 1500 bytes for ethernet.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDLNet_UDP_Send(UDPsocket sock, int channel, UDPpacket *packet);
|
||||
|
||||
/* Receive a vector of pending packets from the UDP socket.
|
||||
The returned packets contain the source address and the channel they arrived
|
||||
on. If they did not arrive on a bound channel, the the channel will be set
|
||||
to -1.
|
||||
The channels are checked in highest to lowest order, so if an address is
|
||||
bound to multiple channels, the highest channel with the source address
|
||||
bound will be returned.
|
||||
This function returns the number of packets read from the network, or -1
|
||||
on error. This function does not block, so can return 0 packets pending.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDLNet_UDP_RecvV(UDPsocket sock, UDPpacket **packets);
|
||||
|
||||
/* Receive a single packet from the UDP socket.
|
||||
The returned packet contains the source address and the channel it arrived
|
||||
on. If it did not arrive on a bound channel, the the channel will be set
|
||||
to -1.
|
||||
The channels are checked in highest to lowest order, so if an address is
|
||||
bound to multiple channels, the highest channel with the source address
|
||||
bound will be returned.
|
||||
This function returns the number of packets read from the network, or -1
|
||||
on error. This function does not block, so can return 0 packets pending.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDLNet_UDP_Recv(UDPsocket sock, UDPpacket *packet);
|
||||
|
||||
/* Close a UDP network socket */
|
||||
extern DECLSPEC void SDLCALL SDLNet_UDP_Close(UDPsocket sock);
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
/* Hooks for checking sockets for available data */
|
||||
/***********************************************************************/
|
||||
|
||||
typedef struct _SDLNet_SocketSet *SDLNet_SocketSet;
|
||||
|
||||
/* Any network socket can be safely cast to this socket type */
|
||||
typedef struct _SDLNet_GenericSocket {
|
||||
int ready;
|
||||
} *SDLNet_GenericSocket;
|
||||
|
||||
/* Allocate a socket set for use with SDLNet_CheckSockets()
|
||||
This returns a socket set for up to 'maxsockets' sockets, or NULL if
|
||||
the function ran out of memory.
|
||||
*/
|
||||
extern DECLSPEC SDLNet_SocketSet SDLCALL SDLNet_AllocSocketSet(int maxsockets);
|
||||
|
||||
/* Add a socket to a set of sockets to be checked for available data */
|
||||
extern DECLSPEC int SDLCALL SDLNet_AddSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock);
|
||||
SDL_FORCE_INLINE int SDLNet_TCP_AddSocket(SDLNet_SocketSet set, TCPsocket sock)
|
||||
{
|
||||
return SDLNet_AddSocket(set, (SDLNet_GenericSocket)sock);
|
||||
}
|
||||
SDL_FORCE_INLINE int SDLNet_UDP_AddSocket(SDLNet_SocketSet set, UDPsocket sock)
|
||||
{
|
||||
return SDLNet_AddSocket(set, (SDLNet_GenericSocket)sock);
|
||||
}
|
||||
|
||||
|
||||
/* Remove a socket from a set of sockets to be checked for available data */
|
||||
extern DECLSPEC int SDLCALL SDLNet_DelSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock);
|
||||
SDL_FORCE_INLINE int SDLNet_TCP_DelSocket(SDLNet_SocketSet set, TCPsocket sock)
|
||||
{
|
||||
return SDLNet_DelSocket(set, (SDLNet_GenericSocket)sock);
|
||||
}
|
||||
SDL_FORCE_INLINE int SDLNet_UDP_DelSocket(SDLNet_SocketSet set, UDPsocket sock)
|
||||
{
|
||||
return SDLNet_DelSocket(set, (SDLNet_GenericSocket)sock);
|
||||
}
|
||||
|
||||
/* This function checks to see if data is available for reading on the
|
||||
given set of sockets. If 'timeout' is 0, it performs a quick poll,
|
||||
otherwise the function returns when either data is available for
|
||||
reading, or the timeout in milliseconds has elapsed, which ever occurs
|
||||
first. This function returns the number of sockets ready for reading,
|
||||
or -1 if there was an error with the select() system call.
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDLNet_CheckSockets(SDLNet_SocketSet set, Uint32 timeout);
|
||||
|
||||
/* After calling SDLNet_CheckSockets(), you can use this function on a
|
||||
socket that was in the socket set, to find out if data is available
|
||||
for reading.
|
||||
*/
|
||||
#define SDLNet_SocketReady(sock) _SDLNet_SocketReady((SDLNet_GenericSocket)(sock))
|
||||
SDL_FORCE_INLINE int _SDLNet_SocketReady(SDLNet_GenericSocket sock)
|
||||
{
|
||||
return (sock != NULL) && (sock->ready);
|
||||
}
|
||||
|
||||
/* Free a set of sockets allocated by SDL_NetAllocSocketSet() */
|
||||
extern DECLSPEC void SDLCALL SDLNet_FreeSocketSet(SDLNet_SocketSet set);
|
||||
|
||||
/***********************************************************************/
|
||||
/* Error reporting functions */
|
||||
/***********************************************************************/
|
||||
|
||||
extern DECLSPEC void SDLCALL SDLNet_SetError(const char *fmt, ...);
|
||||
extern DECLSPEC const char * SDLCALL SDLNet_GetError(void);
|
||||
|
||||
/***********************************************************************/
|
||||
/* Inline functions to read/write network data */
|
||||
/***********************************************************************/
|
||||
|
||||
/* Warning, some systems have data access alignment restrictions */
|
||||
#if defined(sparc) || defined(mips) || defined(__arm__)
|
||||
#define SDL_DATA_ALIGNED 1
|
||||
#endif
|
||||
#ifndef SDL_DATA_ALIGNED
|
||||
#define SDL_DATA_ALIGNED 0
|
||||
#endif
|
||||
|
||||
/* Write a 16/32-bit value to network packet buffer */
|
||||
#define SDLNet_Write16(value, areap) _SDLNet_Write16(value, areap)
|
||||
#define SDLNet_Write32(value, areap) _SDLNet_Write32(value, areap)
|
||||
|
||||
/* Read a 16/32-bit value from network packet buffer */
|
||||
#define SDLNet_Read16(areap) _SDLNet_Read16(areap)
|
||||
#define SDLNet_Read32(areap) _SDLNet_Read32(areap)
|
||||
|
||||
#if !defined(WITHOUT_SDL) && !SDL_DATA_ALIGNED
|
||||
|
||||
SDL_FORCE_INLINE void _SDLNet_Write16(Uint16 value, void *areap)
|
||||
{
|
||||
*(Uint16 *)areap = SDL_SwapBE16(value);
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE void _SDLNet_Write32(Uint32 value, void *areap)
|
||||
{
|
||||
*(Uint32 *)areap = SDL_SwapBE32(value);
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE Uint16 _SDLNet_Read16(const void *areap)
|
||||
{
|
||||
return SDL_SwapBE16(*(const Uint16 *)areap);
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE Uint32 _SDLNet_Read32(const void *areap)
|
||||
{
|
||||
return SDL_SwapBE32(*(const Uint32 *)areap);
|
||||
}
|
||||
|
||||
#else /* !defined(WITHOUT_SDL) && !SDL_DATA_ALIGNED */
|
||||
|
||||
SDL_FORCE_INLINE void _SDLNet_Write16(Uint16 value, void *areap)
|
||||
{
|
||||
Uint8 *area = (Uint8*)areap;
|
||||
area[0] = (value >> 8) & 0xFF;
|
||||
area[1] = value & 0xFF;
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE void _SDLNet_Write32(Uint32 value, void *areap)
|
||||
{
|
||||
Uint8 *area = (Uint8*)areap;
|
||||
area[0] = (value >> 24) & 0xFF;
|
||||
area[1] = (value >> 16) & 0xFF;
|
||||
area[2] = (value >> 8) & 0xFF;
|
||||
area[3] = value & 0xFF;
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE Uint16 _SDLNet_Read16(void *areap)
|
||||
{
|
||||
Uint8 *area = (Uint8*)areap;
|
||||
return ((Uint16)area[0]) << 8 | ((Uint16)area[1]);
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE Uint32 _SDLNet_Read32(const void *areap)
|
||||
{
|
||||
const Uint8 *area = (const Uint8*)areap;
|
||||
return ((Uint32)area[0]) << 24 | ((Uint32)area[1]) << 16 | ((Uint32)area[2]) << 8 | ((Uint32)area[3]);
|
||||
}
|
||||
|
||||
#endif /* !defined(WITHOUT_SDL) && !SDL_DATA_ALIGNED */
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include "close_code.h"
|
||||
|
||||
#endif /* _SDL_NET_H */
|
||||
297
src/ext/net/SDLnet.c
Normal file
297
src/ext/net/SDLnet.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
SDL_net: An example cross-platform network library for use with SDL
|
||||
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
|
||||
Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#include "SDLnetsys.h"
|
||||
#include "SDL_net.h"
|
||||
|
||||
#ifdef WITHOUT_SDL
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
const SDLNet_version *SDLNet_Linked_Version(void)
|
||||
{
|
||||
static SDLNet_version linked_version;
|
||||
SDL_NET_VERSION(&linked_version);
|
||||
return(&linked_version);
|
||||
}
|
||||
|
||||
/* Since the UNIX/Win32/BeOS code is so different from MacOS,
|
||||
we'll just have two completely different sections here.
|
||||
*/
|
||||
static int SDLNet_started = 0;
|
||||
|
||||
#ifndef __USE_W32_SOCKETS
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#ifndef __USE_W32_SOCKETS
|
||||
|
||||
int SDLNet_GetLastError(void)
|
||||
{
|
||||
return errno;
|
||||
}
|
||||
|
||||
void SDLNet_SetLastError(int err)
|
||||
{
|
||||
errno = err;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static char errorbuf[1024];
|
||||
|
||||
void SDLCALL SDLNet_SetError(const char *fmt, ...)
|
||||
{
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
SDL_vsnprintf(errorbuf, sizeof(errorbuf), fmt, argp);
|
||||
va_end(argp);
|
||||
#ifndef WITHOUT_SDL
|
||||
SDL_SetError("%s", errorbuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char * SDLCALL SDLNet_GetError(void)
|
||||
{
|
||||
#ifdef WITHOUT_SDL
|
||||
return errorbuf;
|
||||
#else
|
||||
return SDL_GetError();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Initialize/Cleanup the network API */
|
||||
int SDLNet_Init(void)
|
||||
{
|
||||
if ( !SDLNet_started ) {
|
||||
#ifdef __USE_W32_SOCKETS
|
||||
/* Start up the windows networking */
|
||||
WORD version_wanted = MAKEWORD(1,1);
|
||||
WSADATA wsaData;
|
||||
|
||||
if ( WSAStartup(version_wanted, &wsaData) != 0 ) {
|
||||
SDLNet_SetError("Couldn't initialize Winsock 1.1\n");
|
||||
return(-1);
|
||||
}
|
||||
#else
|
||||
/* SIGPIPE is generated when a remote socket is closed */
|
||||
void (*handler)(int);
|
||||
handler = signal(SIGPIPE, SIG_IGN);
|
||||
if ( handler != SIG_DFL ) {
|
||||
signal(SIGPIPE, handler);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
++SDLNet_started;
|
||||
return(0);
|
||||
}
|
||||
void SDLNet_Quit(void)
|
||||
{
|
||||
if ( SDLNet_started == 0 ) {
|
||||
return;
|
||||
}
|
||||
if ( --SDLNet_started == 0 ) {
|
||||
#ifdef __USE_W32_SOCKETS
|
||||
/* Clean up windows networking */
|
||||
if ( WSACleanup() == SOCKET_ERROR ) {
|
||||
if ( WSAGetLastError() == WSAEINPROGRESS ) {
|
||||
#ifndef _WIN32_WCE
|
||||
//WSACancelBlockingCall();
|
||||
#endif
|
||||
WSACleanup();
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Restore the SIGPIPE handler */
|
||||
void (*handler)(int);
|
||||
handler = signal(SIGPIPE, SIG_DFL);
|
||||
if ( handler != SIG_IGN ) {
|
||||
signal(SIGPIPE, handler);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Resolve a host name and port to an IP address in network form */
|
||||
int SDLNet_ResolveHost(IPaddress *address, const char *host, Uint16 port)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
/* Perform the actual host resolution */
|
||||
if ( host == NULL ) {
|
||||
address->host = INADDR_ANY;
|
||||
} else {
|
||||
address->host = inet_addr(host);
|
||||
if ( address->host == INADDR_NONE ) {
|
||||
struct hostent *hp;
|
||||
|
||||
hp = gethostbyname(host);
|
||||
if ( hp ) {
|
||||
SDL_memcpy(&address->host,hp->h_addr,hp->h_length);
|
||||
} else {
|
||||
retval = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
address->port = SDLNet_Read16(&port);
|
||||
|
||||
/* Return the status */
|
||||
return(retval);
|
||||
}
|
||||
|
||||
/* Resolve an ip address to a host name in canonical form.
|
||||
If the ip couldn't be resolved, this function returns NULL,
|
||||
otherwise a pointer to a static buffer containing the hostname
|
||||
is returned. Note that this function is not thread-safe.
|
||||
*/
|
||||
/* Written by Miguel Angel Blanch.
|
||||
* Main Programmer of Arianne RPG.
|
||||
* http://come.to/arianne_rpg
|
||||
*/
|
||||
const char *SDLNet_ResolveIP(const IPaddress *ip)
|
||||
{
|
||||
struct hostent *hp;
|
||||
struct in_addr in;
|
||||
|
||||
hp = gethostbyaddr((const char *)&ip->host, sizeof(ip->host), AF_INET);
|
||||
if ( hp != NULL ) {
|
||||
return hp->h_name;
|
||||
}
|
||||
|
||||
in.s_addr = ip->host;
|
||||
return inet_ntoa(in);
|
||||
}
|
||||
|
||||
int SDLNet_GetLocalAddresses(IPaddress *addresses, int maxcount)
|
||||
{
|
||||
int count = 0;
|
||||
#ifdef SIOCGIFCONF
|
||||
/* Defined on Mac OS X */
|
||||
#ifndef _SIZEOF_ADDR_IFREQ
|
||||
#define _SIZEOF_ADDR_IFREQ sizeof
|
||||
#endif
|
||||
SOCKET sock;
|
||||
struct ifconf conf;
|
||||
char data[4096];
|
||||
struct ifreq *ifr;
|
||||
struct sockaddr_in *sock_addr;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if ( sock == INVALID_SOCKET ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
conf.ifc_len = sizeof(data);
|
||||
conf.ifc_buf = (caddr_t) data;
|
||||
if ( ioctl(sock, SIOCGIFCONF, &conf) < 0 ) {
|
||||
closesocket(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ifr = (struct ifreq*)data;
|
||||
while ((char*)ifr < data+conf.ifc_len) {
|
||||
if (ifr->ifr_addr.sa_family == AF_INET) {
|
||||
if (count < maxcount) {
|
||||
sock_addr = (struct sockaddr_in*)&ifr->ifr_addr;
|
||||
addresses[count].host = sock_addr->sin_addr.s_addr;
|
||||
addresses[count].port = sock_addr->sin_port;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
ifr = (struct ifreq*)((char*)ifr + _SIZEOF_ADDR_IFREQ(*ifr));
|
||||
}
|
||||
closesocket(sock);
|
||||
#elif defined(__WIN32__)
|
||||
//PIP_ADAPTER_INFO pAdapterInfo;
|
||||
//PIP_ADAPTER_INFO pAdapter;
|
||||
//PIP_ADDR_STRING pAddress;
|
||||
//DWORD dwRetVal = 0;
|
||||
//ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
|
||||
|
||||
//pAdapterInfo = (IP_ADAPTER_INFO *) SDL_malloc(sizeof (IP_ADAPTER_INFO));
|
||||
//if (pAdapterInfo == NULL) {
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
//if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == ERROR_BUFFER_OVERFLOW) {
|
||||
// pAdapterInfo = (IP_ADAPTER_INFO *) SDL_realloc(pAdapterInfo, ulOutBufLen);
|
||||
// if (pAdapterInfo == NULL) {
|
||||
// return 0;
|
||||
// }
|
||||
// dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
|
||||
//}
|
||||
|
||||
//if (dwRetVal == NO_ERROR) {
|
||||
// for (pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) {
|
||||
// for (pAddress = &pAdapter->IpAddressList; pAddress; pAddress = pAddress->Next) {
|
||||
// if (count < maxcount) {
|
||||
// addresses[count].host = inet_addr(pAddress->IpAddress.String);
|
||||
// addresses[count].port = 0;
|
||||
// }
|
||||
// ++count;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//SDL_free(pAdapterInfo);
|
||||
#endif
|
||||
return count;
|
||||
}
|
||||
|
||||
#if !defined(WITHOUT_SDL) && !SDL_DATA_ALIGNED /* function versions for binary compatibility */
|
||||
|
||||
#undef SDLNet_Write16
|
||||
#undef SDLNet_Write32
|
||||
#undef SDLNet_Read16
|
||||
#undef SDLNet_Read32
|
||||
|
||||
/* Write a 16/32 bit value to network packet buffer */
|
||||
extern DECLSPEC void SDLCALL SDLNet_Write16(Uint16 value, void *area);
|
||||
extern DECLSPEC void SDLCALL SDLNet_Write32(Uint32 value, void *area);
|
||||
|
||||
/* Read a 16/32 bit value from network packet buffer */
|
||||
extern DECLSPEC Uint16 SDLCALL SDLNet_Read16(void *area);
|
||||
extern DECLSPEC Uint32 SDLCALL SDLNet_Read32(const void *area);
|
||||
|
||||
void SDLNet_Write16(Uint16 value, void *areap)
|
||||
{
|
||||
(*(Uint16 *)(areap) = SDL_SwapBE16(value));
|
||||
}
|
||||
|
||||
void SDLNet_Write32(Uint32 value, void *areap)
|
||||
{
|
||||
*(Uint32 *)(areap) = SDL_SwapBE32(value);
|
||||
}
|
||||
|
||||
Uint16 SDLNet_Read16(void *areap)
|
||||
{
|
||||
return (SDL_SwapBE16(*(Uint16 *)(areap)));
|
||||
}
|
||||
|
||||
Uint32 SDLNet_Read32(const void *areap)
|
||||
{
|
||||
return (SDL_SwapBE32(*(Uint32 *)(areap)));
|
||||
}
|
||||
|
||||
#endif /* !defined(WITHOUT_SDL) && !SDL_DATA_ALIGNED */
|
||||
297
src/ext/net/SDLnetTCP.c
Normal file
297
src/ext/net/SDLnetTCP.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
SDL_net: An example cross-platform network library for use with SDL
|
||||
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#include "SDLnetsys.h"
|
||||
#include "SDL_net.h"
|
||||
|
||||
/* The network API for TCP sockets */
|
||||
|
||||
/* Since the UNIX/Win32/BeOS code is so different from MacOS,
|
||||
we'll just have two completely different sections here.
|
||||
*/
|
||||
|
||||
struct _TCPsocket {
|
||||
int ready;
|
||||
SOCKET channel;
|
||||
IPaddress remoteAddress;
|
||||
IPaddress localAddress;
|
||||
int sflag;
|
||||
};
|
||||
|
||||
/* Open a TCP network socket
|
||||
If 'remote' is NULL, this creates a local server socket on the given port,
|
||||
otherwise a TCP connection to the remote host and port is attempted.
|
||||
The newly created socket is returned, or NULL if there was an error.
|
||||
*/
|
||||
TCPsocket SDLNet_TCP_Open(IPaddress *ip)
|
||||
{
|
||||
TCPsocket sock;
|
||||
struct sockaddr_in sock_addr;
|
||||
|
||||
/* Allocate a TCP socket structure */
|
||||
sock = (TCPsocket)SDL_malloc(sizeof(*sock));
|
||||
if ( sock == NULL ) {
|
||||
SDLNet_SetError("Out of memory");
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/* Open the socket */
|
||||
sock->channel = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if ( sock->channel == INVALID_SOCKET ) {
|
||||
SDLNet_SetError("Couldn't create socket");
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/* Connect to remote, or bind locally, as appropriate */
|
||||
if ( (ip->host != INADDR_NONE) && (ip->host != INADDR_ANY) ) {
|
||||
|
||||
// ######### Connecting to remote
|
||||
|
||||
SDL_memset(&sock_addr, 0, sizeof(sock_addr));
|
||||
sock_addr.sin_family = AF_INET;
|
||||
sock_addr.sin_addr.s_addr = ip->host;
|
||||
sock_addr.sin_port = ip->port;
|
||||
|
||||
/* Connect to the remote host */
|
||||
if ( connect(sock->channel, (struct sockaddr *)&sock_addr,
|
||||
sizeof(sock_addr)) == SOCKET_ERROR ) {
|
||||
SDLNet_SetError("Couldn't connect to remote host");
|
||||
goto error_return;
|
||||
}
|
||||
sock->sflag = 0;
|
||||
} else {
|
||||
|
||||
// ########## Binding locally
|
||||
|
||||
SDL_memset(&sock_addr, 0, sizeof(sock_addr));
|
||||
sock_addr.sin_family = AF_INET;
|
||||
sock_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
sock_addr.sin_port = ip->port;
|
||||
|
||||
/*
|
||||
* Windows gets bad mojo with SO_REUSEADDR:
|
||||
* http://www.devolution.com/pipermail/sdl/2005-September/070491.html
|
||||
* --ryan.
|
||||
*/
|
||||
#ifndef WIN32
|
||||
/* allow local address reuse */
|
||||
{ int yes = 1;
|
||||
setsockopt(sock->channel, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Bind the socket for listening */
|
||||
if ( bind(sock->channel, (struct sockaddr *)&sock_addr,
|
||||
sizeof(sock_addr)) == SOCKET_ERROR ) {
|
||||
SDLNet_SetError("Couldn't bind to local port");
|
||||
goto error_return;
|
||||
}
|
||||
if ( listen(sock->channel, 5) == SOCKET_ERROR ) {
|
||||
SDLNet_SetError("Couldn't listen to local port");
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/* Set the socket to non-blocking mode for accept() */
|
||||
#if defined(__BEOS__) && defined(SO_NONBLOCK)
|
||||
/* On BeOS r5 there is O_NONBLOCK but it's for files only */
|
||||
{
|
||||
long b = 1;
|
||||
setsockopt(sock->channel, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
|
||||
}
|
||||
#elif defined(O_NONBLOCK)
|
||||
{
|
||||
fcntl(sock->channel, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
#elif defined(WIN32)
|
||||
{
|
||||
unsigned long mode = 1;
|
||||
ioctlsocket (sock->channel, FIONBIO, &mode);
|
||||
}
|
||||
#elif defined(__OS2__)
|
||||
{
|
||||
int dontblock = 1;
|
||||
ioctl(sock->channel, FIONBIO, &dontblock);
|
||||
}
|
||||
#else
|
||||
#warning How do we set non-blocking mode on other operating systems?
|
||||
#endif
|
||||
sock->sflag = 1;
|
||||
}
|
||||
sock->ready = 0;
|
||||
|
||||
#ifdef TCP_NODELAY
|
||||
/* Set the nodelay TCP option for real-time games */
|
||||
{ int yes = 1;
|
||||
setsockopt(sock->channel, IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(yes));
|
||||
}
|
||||
#else
|
||||
#warning Building without TCP_NODELAY
|
||||
#endif /* TCP_NODELAY */
|
||||
|
||||
/* Fill in the channel host address */
|
||||
sock->remoteAddress.host = sock_addr.sin_addr.s_addr;
|
||||
sock->remoteAddress.port = sock_addr.sin_port;
|
||||
|
||||
/* The socket is ready */
|
||||
return(sock);
|
||||
|
||||
error_return:
|
||||
SDLNet_TCP_Close(sock);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/* Accept an incoming connection on the given server socket.
|
||||
The newly created socket is returned, or NULL if there was an error.
|
||||
*/
|
||||
TCPsocket SDLNet_TCP_Accept(TCPsocket server)
|
||||
{
|
||||
TCPsocket sock;
|
||||
struct sockaddr_in sock_addr;
|
||||
socklen_t sock_alen;
|
||||
|
||||
/* Only server sockets can accept */
|
||||
if ( ! server->sflag ) {
|
||||
SDLNet_SetError("Only server sockets can accept()");
|
||||
return(NULL);
|
||||
}
|
||||
server->ready = 0;
|
||||
|
||||
/* Allocate a TCP socket structure */
|
||||
sock = (TCPsocket)SDL_malloc(sizeof(*sock));
|
||||
if ( sock == NULL ) {
|
||||
SDLNet_SetError("Out of memory");
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/* Accept a new TCP connection on a server socket */
|
||||
sock_alen = sizeof(sock_addr);
|
||||
sock->channel = accept(server->channel, (struct sockaddr *)&sock_addr,
|
||||
&sock_alen);
|
||||
if ( sock->channel == INVALID_SOCKET ) {
|
||||
SDLNet_SetError("accept() failed");
|
||||
goto error_return;
|
||||
}
|
||||
#ifdef WIN32
|
||||
{
|
||||
/* passing a zero value, socket mode set to block on */
|
||||
unsigned long mode = 0;
|
||||
ioctlsocket (sock->channel, FIONBIO, &mode);
|
||||
}
|
||||
#elif defined(O_NONBLOCK)
|
||||
{
|
||||
int flags = fcntl(sock->channel, F_GETFL, 0);
|
||||
fcntl(sock->channel, F_SETFL, flags & ~O_NONBLOCK);
|
||||
}
|
||||
#endif /* WIN32 */
|
||||
sock->remoteAddress.host = sock_addr.sin_addr.s_addr;
|
||||
sock->remoteAddress.port = sock_addr.sin_port;
|
||||
|
||||
sock->sflag = 0;
|
||||
sock->ready = 0;
|
||||
|
||||
/* The socket is ready */
|
||||
return(sock);
|
||||
|
||||
error_return:
|
||||
SDLNet_TCP_Close(sock);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/* Get the IP address of the remote system associated with the socket.
|
||||
If the socket is a server socket, this function returns NULL.
|
||||
*/
|
||||
IPaddress *SDLNet_TCP_GetPeerAddress(TCPsocket sock)
|
||||
{
|
||||
if ( sock->sflag ) {
|
||||
return(NULL);
|
||||
}
|
||||
return(&sock->remoteAddress);
|
||||
}
|
||||
|
||||
/* Send 'len' bytes of 'data' over the non-server socket 'sock'
|
||||
This function returns the actual amount of data sent. If the return value
|
||||
is less than the amount of data sent, then either the remote connection was
|
||||
closed, or an unknown socket error occurred.
|
||||
*/
|
||||
int SDLNet_TCP_Send(TCPsocket sock, const void *datap, int len)
|
||||
{
|
||||
const Uint8 *data = (const Uint8 *)datap; /* For pointer arithmetic */
|
||||
int sent, left;
|
||||
|
||||
/* Server sockets are for accepting connections only */
|
||||
if ( sock->sflag ) {
|
||||
SDLNet_SetError("Server sockets cannot send");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/* Keep sending data until it's sent or an error occurs */
|
||||
left = len;
|
||||
sent = 0;
|
||||
SDLNet_SetLastError(0);
|
||||
do {
|
||||
len = send(sock->channel, (const char *) data, left, 0);
|
||||
if ( len > 0 ) {
|
||||
sent += len;
|
||||
left -= len;
|
||||
data += len;
|
||||
}
|
||||
} while ( (left > 0) && ((len > 0) || (SDLNet_GetLastError() == EINTR)) );
|
||||
|
||||
return(sent);
|
||||
}
|
||||
|
||||
/* Receive up to 'maxlen' bytes of data over the non-server socket 'sock',
|
||||
and store them in the buffer pointed to by 'data'.
|
||||
This function returns the actual amount of data received. If the return
|
||||
value is less than or equal to zero, then either the remote connection was
|
||||
closed, or an unknown socket error occurred.
|
||||
*/
|
||||
int SDLNet_TCP_Recv(TCPsocket sock, void *data, int maxlen)
|
||||
{
|
||||
int len;
|
||||
|
||||
/* Server sockets are for accepting connections only */
|
||||
if ( sock->sflag ) {
|
||||
SDLNet_SetError("Server sockets cannot receive");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
SDLNet_SetLastError(0);
|
||||
do {
|
||||
len = recv(sock->channel, (char *) data, maxlen, 0);
|
||||
} while ( SDLNet_GetLastError() == EINTR );
|
||||
|
||||
sock->ready = 0;
|
||||
return(len);
|
||||
}
|
||||
|
||||
/* Close a TCP network socket */
|
||||
void SDLNet_TCP_Close(TCPsocket sock)
|
||||
{
|
||||
if ( sock != NULL ) {
|
||||
if ( sock->channel != INVALID_SOCKET ) {
|
||||
closesocket(sock->channel);
|
||||
}
|
||||
SDL_free(sock);
|
||||
}
|
||||
}
|
||||
524
src/ext/net/SDLnetUDP.c
Normal file
524
src/ext/net/SDLnetUDP.c
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
SDL_net: An example cross-platform network library for use with SDL
|
||||
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#include "SDLnetsys.h"
|
||||
#include "SDL_net.h"
|
||||
|
||||
#ifdef __WIN32__
|
||||
#define srandom srand
|
||||
#define random rand
|
||||
#endif
|
||||
|
||||
struct UDP_channel {
|
||||
int numbound;
|
||||
IPaddress address[SDLNET_MAX_UDPADDRESSES];
|
||||
};
|
||||
|
||||
struct _UDPsocket {
|
||||
int ready;
|
||||
SOCKET channel;
|
||||
IPaddress address;
|
||||
|
||||
struct UDP_channel binding[SDLNET_MAX_UDPCHANNELS];
|
||||
|
||||
/* For debugging purposes */
|
||||
int packetloss;
|
||||
};
|
||||
|
||||
/* Allocate/free a single UDP packet 'size' bytes long.
|
||||
The new packet is returned, or NULL if the function ran out of memory.
|
||||
*/
|
||||
extern UDPpacket *SDLNet_AllocPacket(int size)
|
||||
{
|
||||
UDPpacket *packet;
|
||||
int error;
|
||||
|
||||
|
||||
error = 1;
|
||||
packet = (UDPpacket *)SDL_malloc(sizeof(*packet));
|
||||
if ( packet != NULL ) {
|
||||
packet->maxlen = size;
|
||||
packet->data = (Uint8 *)SDL_malloc(size);
|
||||
if ( packet->data != NULL ) {
|
||||
error = 0;
|
||||
}
|
||||
}
|
||||
if ( error ) {
|
||||
SDLNet_SetError("Out of memory");
|
||||
SDLNet_FreePacket(packet);
|
||||
packet = NULL;
|
||||
}
|
||||
return(packet);
|
||||
}
|
||||
int SDLNet_ResizePacket(UDPpacket *packet, int newsize)
|
||||
{
|
||||
Uint8 *newdata;
|
||||
|
||||
newdata = (Uint8 *)SDL_malloc(newsize);
|
||||
if ( newdata != NULL ) {
|
||||
SDL_free(packet->data);
|
||||
packet->data = newdata;
|
||||
packet->maxlen = newsize;
|
||||
}
|
||||
return(packet->maxlen);
|
||||
}
|
||||
extern void SDLNet_FreePacket(UDPpacket *packet)
|
||||
{
|
||||
if ( packet ) {
|
||||
SDL_free(packet->data);
|
||||
SDL_free(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets,
|
||||
each 'size' bytes long.
|
||||
A pointer to the packet array is returned, or NULL if the function ran out
|
||||
of memory.
|
||||
*/
|
||||
UDPpacket **SDLNet_AllocPacketV(int howmany, int size)
|
||||
{
|
||||
UDPpacket **packetV;
|
||||
|
||||
packetV = (UDPpacket **)SDL_malloc((howmany+1)*sizeof(*packetV));
|
||||
if ( packetV != NULL ) {
|
||||
int i;
|
||||
for ( i=0; i<howmany; ++i ) {
|
||||
packetV[i] = SDLNet_AllocPacket(size);
|
||||
if ( packetV[i] == NULL ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
packetV[i] = NULL;
|
||||
|
||||
if ( i != howmany ) {
|
||||
SDLNet_SetError("Out of memory");
|
||||
SDLNet_FreePacketV(packetV);
|
||||
packetV = NULL;
|
||||
}
|
||||
}
|
||||
return(packetV);
|
||||
}
|
||||
void SDLNet_FreePacketV(UDPpacket **packetV)
|
||||
{
|
||||
if ( packetV ) {
|
||||
int i;
|
||||
for ( i=0; packetV[i]; ++i ) {
|
||||
SDLNet_FreePacket(packetV[i]);
|
||||
}
|
||||
SDL_free(packetV);
|
||||
}
|
||||
}
|
||||
|
||||
/* Since the UNIX/Win32/BeOS code is so different from MacOS,
|
||||
we'll just have two completely different sections here.
|
||||
*/
|
||||
|
||||
/* Open a UDP network socket
|
||||
If 'port' is non-zero, the UDP socket is bound to a fixed local port.
|
||||
*/
|
||||
UDPsocket SDLNet_UDP_Open(Uint16 port)
|
||||
{
|
||||
UDPsocket sock;
|
||||
struct sockaddr_in sock_addr;
|
||||
socklen_t sock_len;
|
||||
|
||||
/* Allocate a UDP socket structure */
|
||||
sock = (UDPsocket)SDL_malloc(sizeof(*sock));
|
||||
if ( sock == NULL ) {
|
||||
SDLNet_SetError("Out of memory");
|
||||
goto error_return;
|
||||
}
|
||||
SDL_memset(sock, 0, sizeof(*sock));
|
||||
SDL_memset(&sock_addr, 0, sizeof(sock_addr));
|
||||
|
||||
/* Open the socket */
|
||||
sock->channel = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if ( sock->channel == INVALID_SOCKET )
|
||||
{
|
||||
SDLNet_SetError("Couldn't create socket");
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/* Bind locally, if appropriate */
|
||||
sock_addr.sin_family = AF_INET;
|
||||
sock_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
sock_addr.sin_port = SDLNet_Read16(&port);
|
||||
|
||||
/* Bind the socket for listening */
|
||||
if ( bind(sock->channel, (struct sockaddr *)&sock_addr,
|
||||
sizeof(sock_addr)) == SOCKET_ERROR ) {
|
||||
SDLNet_SetError("Couldn't bind to local port");
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/* Get the bound address and port */
|
||||
sock_len = sizeof(sock_addr);
|
||||
if ( getsockname(sock->channel, (struct sockaddr *)&sock_addr, &sock_len) < 0 ) {
|
||||
SDLNet_SetError("Couldn't get socket address");
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/* Fill in the channel host address */
|
||||
sock->address.host = sock_addr.sin_addr.s_addr;
|
||||
sock->address.port = sock_addr.sin_port;
|
||||
|
||||
#ifdef SO_BROADCAST
|
||||
/* Allow LAN broadcasts with the socket */
|
||||
{ int yes = 1;
|
||||
setsockopt(sock->channel, SOL_SOCKET, SO_BROADCAST, (char*)&yes, sizeof(yes));
|
||||
}
|
||||
#endif
|
||||
#ifdef IP_ADD_MEMBERSHIP
|
||||
/* Receive LAN multicast packets on 224.0.0.1
|
||||
This automatically works on Mac OS X, Linux and BSD, but needs
|
||||
this code on Windows.
|
||||
*/
|
||||
/* A good description of multicast can be found here:
|
||||
http://www.docs.hp.com/en/B2355-90136/ch05s05.html
|
||||
*/
|
||||
/* FIXME: Add support for joining arbitrary groups to the API */
|
||||
{
|
||||
struct ip_mreq g;
|
||||
|
||||
g.imr_multiaddr.s_addr = inet_addr("224.0.0.1");
|
||||
g.imr_interface.s_addr = INADDR_ANY;
|
||||
setsockopt(sock->channel, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
||||
(char*)&g, sizeof(g));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The socket is ready */
|
||||
|
||||
return(sock);
|
||||
|
||||
error_return:
|
||||
SDLNet_UDP_Close(sock);
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
void SDLNet_UDP_SetPacketLoss(UDPsocket sock, int percent)
|
||||
{
|
||||
/* FIXME: We may want this behavior to be reproducible
|
||||
but there isn't a portable reentrant random
|
||||
number generator with good randomness.
|
||||
*/
|
||||
srandom(time(NULL));
|
||||
|
||||
if (percent < 0) {
|
||||
percent = 0;
|
||||
} else if (percent > 100) {
|
||||
percent = 100;
|
||||
}
|
||||
sock->packetloss = percent;
|
||||
}
|
||||
|
||||
/* Verify that the channel is in the valid range */
|
||||
static int ValidChannel(int channel)
|
||||
{
|
||||
if ( (channel < 0) || (channel >= SDLNET_MAX_UDPCHANNELS) ) {
|
||||
SDLNet_SetError("Invalid channel");
|
||||
return(0);
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
/* Bind the address 'address' to the requested channel on the UDP socket.
|
||||
If the channel is -1, then the first unbound channel that has not yet
|
||||
been bound to the maximum number of addresses will be bound with
|
||||
the given address as it's primary address.
|
||||
If the channel is already bound, this new address will be added to the
|
||||
list of valid source addresses for packets arriving on the channel.
|
||||
If the channel is not already bound, then the address becomes the primary
|
||||
address, to which all outbound packets on the channel are sent.
|
||||
This function returns the channel which was bound, or -1 on error.
|
||||
*/
|
||||
int SDLNet_UDP_Bind(UDPsocket sock, int channel, const IPaddress *address)
|
||||
{
|
||||
struct UDP_channel *binding;
|
||||
|
||||
if ( sock == NULL ) {
|
||||
SDLNet_SetError("Passed a NULL socket");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if ( channel == -1 ) {
|
||||
for ( channel=0; channel < SDLNET_MAX_UDPCHANNELS; ++channel ) {
|
||||
binding = &sock->binding[channel];
|
||||
if ( binding->numbound < SDLNET_MAX_UDPADDRESSES ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ( ! ValidChannel(channel) ) {
|
||||
return(-1);
|
||||
}
|
||||
binding = &sock->binding[channel];
|
||||
}
|
||||
if ( binding->numbound == SDLNET_MAX_UDPADDRESSES ) {
|
||||
SDLNet_SetError("No room for new addresses");
|
||||
return(-1);
|
||||
}
|
||||
binding->address[binding->numbound++] = *address;
|
||||
return(channel);
|
||||
}
|
||||
|
||||
/* Unbind all addresses from the given channel */
|
||||
void SDLNet_UDP_Unbind(UDPsocket sock, int channel)
|
||||
{
|
||||
if ( (channel >= 0) && (channel < SDLNET_MAX_UDPCHANNELS) ) {
|
||||
sock->binding[channel].numbound = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the primary IP address of the remote system associated with the
|
||||
socket and channel.
|
||||
If the channel is not bound, this function returns NULL.
|
||||
*/
|
||||
IPaddress *SDLNet_UDP_GetPeerAddress(UDPsocket sock, int channel)
|
||||
{
|
||||
IPaddress *address;
|
||||
|
||||
address = NULL;
|
||||
switch (channel) {
|
||||
case -1:
|
||||
/* Return the actual address of the socket */
|
||||
address = &sock->address;
|
||||
break;
|
||||
default:
|
||||
/* Return the address of the bound channel */
|
||||
if ( ValidChannel(channel) &&
|
||||
(sock->binding[channel].numbound > 0) ) {
|
||||
address = &sock->binding[channel].address[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
return(address);
|
||||
}
|
||||
|
||||
/* Send a vector of packets to the the channels specified within the packet.
|
||||
If the channel specified in the packet is -1, the packet will be sent to
|
||||
the address in the 'src' member of the packet.
|
||||
Each packet will be updated with the status of the packet after it has
|
||||
been sent, -1 if the packet send failed.
|
||||
This function returns the number of packets sent.
|
||||
*/
|
||||
int SDLNet_UDP_SendV(UDPsocket sock, UDPpacket **packets, int npackets)
|
||||
{
|
||||
int numsent, i, j;
|
||||
struct UDP_channel *binding;
|
||||
int status;
|
||||
int sock_len;
|
||||
struct sockaddr_in sock_addr;
|
||||
|
||||
if ( sock == NULL ) {
|
||||
SDLNet_SetError("Passed a NULL socket");
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Set up the variables to send packets */
|
||||
sock_len = sizeof(sock_addr);
|
||||
|
||||
numsent = 0;
|
||||
for ( i=0; i<npackets; ++i )
|
||||
{
|
||||
/* Simulate packet loss, if desired */
|
||||
if (sock->packetloss) {
|
||||
if ((random()%100) <= sock->packetloss) {
|
||||
packets[i]->status = packets[i]->len;
|
||||
++numsent;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* if channel is < 0, then use channel specified in sock */
|
||||
|
||||
if ( packets[i]->channel < 0 )
|
||||
{
|
||||
sock_addr.sin_addr.s_addr = packets[i]->address.host;
|
||||
sock_addr.sin_port = packets[i]->address.port;
|
||||
sock_addr.sin_family = AF_INET;
|
||||
status = sendto(sock->channel,
|
||||
packets[i]->data, packets[i]->len, 0,
|
||||
(struct sockaddr *)&sock_addr,sock_len);
|
||||
if ( status >= 0 )
|
||||
{
|
||||
packets[i]->status = status;
|
||||
++numsent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Send to each of the bound addresses on the channel */
|
||||
#ifdef DEBUG_NET
|
||||
printf("SDLNet_UDP_SendV sending packet to channel = %d\n", packets[i]->channel );
|
||||
#endif
|
||||
|
||||
binding = &sock->binding[packets[i]->channel];
|
||||
|
||||
for ( j=binding->numbound-1; j>=0; --j )
|
||||
{
|
||||
sock_addr.sin_addr.s_addr = binding->address[j].host;
|
||||
sock_addr.sin_port = binding->address[j].port;
|
||||
sock_addr.sin_family = AF_INET;
|
||||
status = sendto(sock->channel,
|
||||
packets[i]->data, packets[i]->len, 0,
|
||||
(struct sockaddr *)&sock_addr,sock_len);
|
||||
if ( status >= 0 )
|
||||
{
|
||||
packets[i]->status = status;
|
||||
++numsent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(numsent);
|
||||
}
|
||||
|
||||
int SDLNet_UDP_Send(UDPsocket sock, int channel, UDPpacket *packet)
|
||||
{
|
||||
/* This is silly, but... */
|
||||
packet->channel = channel;
|
||||
return(SDLNet_UDP_SendV(sock, &packet, 1));
|
||||
}
|
||||
|
||||
/* Returns true if a socket is has data available for reading right now */
|
||||
static int SocketReady(SOCKET sock)
|
||||
{
|
||||
int retval = 0;
|
||||
struct timeval tv;
|
||||
fd_set mask;
|
||||
|
||||
/* Check the file descriptors for available data */
|
||||
do {
|
||||
SDLNet_SetLastError(0);
|
||||
|
||||
/* Set up the mask of file descriptors */
|
||||
FD_ZERO(&mask);
|
||||
FD_SET(sock, &mask);
|
||||
|
||||
/* Set up the timeout */
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
/* Look! */
|
||||
retval = select(sock+1, &mask, NULL, NULL, &tv);
|
||||
} while ( SDLNet_GetLastError() == EINTR );
|
||||
|
||||
return(retval == 1);
|
||||
}
|
||||
|
||||
/* Receive a vector of pending packets from the UDP socket.
|
||||
The returned packets contain the source address and the channel they arrived
|
||||
on. If they did not arrive on a bound channel, the the channel will be set
|
||||
to -1.
|
||||
This function returns the number of packets read from the network, or -1
|
||||
on error. This function does not block, so can return 0 packets pending.
|
||||
*/
|
||||
extern int SDLNet_UDP_RecvV(UDPsocket sock, UDPpacket **packets)
|
||||
{
|
||||
int numrecv, i, j;
|
||||
struct UDP_channel *binding;
|
||||
socklen_t sock_len;
|
||||
struct sockaddr_in sock_addr;
|
||||
|
||||
if ( sock == NULL ) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
numrecv = 0;
|
||||
while ( packets[numrecv] && SocketReady(sock->channel) )
|
||||
{
|
||||
UDPpacket *packet;
|
||||
|
||||
packet = packets[numrecv];
|
||||
|
||||
sock_len = sizeof(sock_addr);
|
||||
packet->status = recvfrom(sock->channel,
|
||||
packet->data, packet->maxlen, 0,
|
||||
(struct sockaddr *)&sock_addr,
|
||||
&sock_len);
|
||||
if ( packet->status >= 0 ) {
|
||||
packet->len = packet->status;
|
||||
packet->address.host = sock_addr.sin_addr.s_addr;
|
||||
packet->address.port = sock_addr.sin_port;
|
||||
packet->channel = -1;
|
||||
|
||||
for (i=(SDLNET_MAX_UDPCHANNELS-1); i>=0; --i )
|
||||
{
|
||||
binding = &sock->binding[i];
|
||||
|
||||
for ( j=binding->numbound-1; j>=0; --j )
|
||||
{
|
||||
if ( (packet->address.host == binding->address[j].host) &&
|
||||
(packet->address.port == binding->address[j].port) )
|
||||
{
|
||||
packet->channel = i;
|
||||
goto foundit; /* break twice */
|
||||
}
|
||||
}
|
||||
}
|
||||
foundit:
|
||||
++numrecv;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
packet->len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
sock->ready = 0;
|
||||
|
||||
return(numrecv);
|
||||
}
|
||||
|
||||
/* Receive a single packet from the UDP socket.
|
||||
The returned packet contains the source address and the channel it arrived
|
||||
on. If it did not arrive on a bound channel, the the channel will be set
|
||||
to -1.
|
||||
This function returns the number of packets read from the network, or -1
|
||||
on error. This function does not block, so can return 0 packets pending.
|
||||
*/
|
||||
int SDLNet_UDP_Recv(UDPsocket sock, UDPpacket *packet)
|
||||
{
|
||||
UDPpacket *packets[2];
|
||||
|
||||
/* Receive a packet array of 1 */
|
||||
packets[0] = packet;
|
||||
packets[1] = NULL;
|
||||
return(SDLNet_UDP_RecvV(sock, packets));
|
||||
}
|
||||
|
||||
/* Close a UDP network socket */
|
||||
extern void SDLNet_UDP_Close(UDPsocket sock)
|
||||
{
|
||||
if ( sock != NULL ) {
|
||||
if ( sock->channel != INVALID_SOCKET ) {
|
||||
closesocket(sock->channel);
|
||||
}
|
||||
SDL_free(sock);
|
||||
}
|
||||
}
|
||||
|
||||
163
src/ext/net/SDLnetselect.c
Normal file
163
src/ext/net/SDLnetselect.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
SDL_net: An example cross-platform network library for use with SDL
|
||||
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#include "SDLnetsys.h"
|
||||
#include "SDL_net.h"
|
||||
|
||||
/* The select() API for network sockets */
|
||||
|
||||
struct SDLNet_Socket {
|
||||
int ready;
|
||||
SOCKET channel;
|
||||
};
|
||||
|
||||
struct _SDLNet_SocketSet {
|
||||
int numsockets;
|
||||
int maxsockets;
|
||||
struct SDLNet_Socket **sockets;
|
||||
};
|
||||
|
||||
/* Allocate a socket set for use with SDLNet_CheckSockets()
|
||||
This returns a socket set for up to 'maxsockets' sockets, or NULL if
|
||||
the function ran out of memory.
|
||||
*/
|
||||
SDLNet_SocketSet SDLNet_AllocSocketSet(int maxsockets)
|
||||
{
|
||||
struct _SDLNet_SocketSet *set;
|
||||
int i;
|
||||
|
||||
set = (struct _SDLNet_SocketSet *)SDL_malloc(sizeof(*set));
|
||||
if ( set != NULL ) {
|
||||
set->numsockets = 0;
|
||||
set->maxsockets = maxsockets;
|
||||
set->sockets = (struct SDLNet_Socket **)SDL_malloc
|
||||
(maxsockets*sizeof(*set->sockets));
|
||||
if ( set->sockets != NULL ) {
|
||||
for ( i=0; i<maxsockets; ++i ) {
|
||||
set->sockets[i] = NULL;
|
||||
}
|
||||
} else {
|
||||
SDL_free(set);
|
||||
set = NULL;
|
||||
}
|
||||
}
|
||||
return(set);
|
||||
}
|
||||
|
||||
/* Add a socket to a set of sockets to be checked for available data */
|
||||
int SDLNet_AddSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock)
|
||||
{
|
||||
if ( sock != NULL ) {
|
||||
if ( set->numsockets == set->maxsockets ) {
|
||||
SDLNet_SetError("socketset is full");
|
||||
return(-1);
|
||||
}
|
||||
set->sockets[set->numsockets++] = (struct SDLNet_Socket *)sock;
|
||||
}
|
||||
return(set->numsockets);
|
||||
}
|
||||
|
||||
/* Remove a socket from a set of sockets to be checked for available data */
|
||||
int SDLNet_DelSocket(SDLNet_SocketSet set, SDLNet_GenericSocket sock)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( sock != NULL ) {
|
||||
for ( i=0; i<set->numsockets; ++i ) {
|
||||
if ( set->sockets[i] == (struct SDLNet_Socket *)sock ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i == set->numsockets ) {
|
||||
SDLNet_SetError("socket not found in socketset");
|
||||
return(-1);
|
||||
}
|
||||
--set->numsockets;
|
||||
for ( ; i<set->numsockets; ++i ) {
|
||||
set->sockets[i] = set->sockets[i+1];
|
||||
}
|
||||
}
|
||||
return(set->numsockets);
|
||||
}
|
||||
|
||||
/* This function checks to see if data is available for reading on the
|
||||
given set of sockets. If 'timeout' is 0, it performs a quick poll,
|
||||
otherwise the function returns when either data is available for
|
||||
reading, or the timeout in milliseconds has elapsed, which ever occurs
|
||||
first. This function returns the number of sockets ready for reading,
|
||||
or -1 if there was an error with the select() system call.
|
||||
*/
|
||||
int SDLNet_CheckSockets(SDLNet_SocketSet set, Uint32 timeout)
|
||||
{
|
||||
int i;
|
||||
SOCKET maxfd;
|
||||
int retval;
|
||||
struct timeval tv;
|
||||
fd_set mask;
|
||||
|
||||
/* Find the largest file descriptor */
|
||||
maxfd = 0;
|
||||
for ( i=set->numsockets-1; i>=0; --i ) {
|
||||
if ( set->sockets[i]->channel > maxfd ) {
|
||||
maxfd = set->sockets[i]->channel;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the file descriptors for available data */
|
||||
do {
|
||||
SDLNet_SetLastError(0);
|
||||
|
||||
/* Set up the mask of file descriptors */
|
||||
FD_ZERO(&mask);
|
||||
for ( i=set->numsockets-1; i>=0; --i ) {
|
||||
FD_SET(set->sockets[i]->channel, &mask);
|
||||
}
|
||||
|
||||
/* Set up the timeout */
|
||||
tv.tv_sec = timeout/1000;
|
||||
tv.tv_usec = (timeout%1000)*1000;
|
||||
|
||||
/* Look! */
|
||||
retval = select(maxfd+1, &mask, NULL, NULL, &tv);
|
||||
} while ( SDLNet_GetLastError() == EINTR );
|
||||
|
||||
/* Mark all file descriptors ready that have data available */
|
||||
if ( retval > 0 ) {
|
||||
for ( i=set->numsockets-1; i>=0; --i ) {
|
||||
if ( FD_ISSET(set->sockets[i]->channel, &mask) ) {
|
||||
set->sockets[i]->ready = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
|
||||
/* Free a set of sockets allocated by SDL_NetAllocSocketSet() */
|
||||
extern void SDLNet_FreeSocketSet(SDLNet_SocketSet set)
|
||||
{
|
||||
if ( set ) {
|
||||
SDL_free(set->sockets);
|
||||
SDL_free(set);
|
||||
}
|
||||
}
|
||||
|
||||
88
src/ext/net/SDLnetsys.h
Normal file
88
src/ext/net/SDLnetsys.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
SDL_net: An example cross-platform network library for use with SDL
|
||||
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/* Include normal system headers */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifndef _WIN32_WCE
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
/* Include system network headers */
|
||||
#if defined(__WIN32__) || defined(WIN32) || defined(__WINRT__)
|
||||
#define __USE_W32_SOCKETS
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#else /* UNIX */
|
||||
#include <sys/types.h>
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#ifndef __BEOS__
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#endif /* WIN32 */
|
||||
|
||||
/* FIXME: What platforms need this? */
|
||||
#if 0
|
||||
typedef Uint32 socklen_t;
|
||||
#endif
|
||||
|
||||
/* System-dependent definitions */
|
||||
#ifndef __USE_W32_SOCKETS
|
||||
#ifdef __OS2__
|
||||
#define closesocket soclose
|
||||
#else /* !__OS2__ */
|
||||
#define closesocket close
|
||||
#endif /* __OS2__ */
|
||||
#define SOCKET int
|
||||
#define INVALID_SOCKET -1
|
||||
#define SOCKET_ERROR -1
|
||||
#endif /* __USE_W32_SOCKETS */
|
||||
|
||||
#ifdef __USE_W32_SOCKETS
|
||||
#define SDLNet_GetLastError WSAGetLastError
|
||||
#define SDLNet_SetLastError WSASetLastError
|
||||
#ifndef EINTR
|
||||
#define EINTR WSAEINTR
|
||||
#endif
|
||||
#else
|
||||
int SDLNet_GetLastError(void);
|
||||
void SDLNet_SetLastError(int err);
|
||||
#endif
|
||||
|
||||
765
src/fs.c
Normal file
765
src/fs.c
Normal file
@@ -0,0 +1,765 @@
|
||||
// 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 "fs.h"
|
||||
#include "net.h"
|
||||
#include "ext/file_dialog.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if !defined(__WINRT__) && !defined(__WINDOWS__)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(__WINRT__) || defined(__WINDOWS__)
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#define PUBLIC_DIR TIC_HOST "/play"
|
||||
#define PUBLIC_DIR_SLASH PUBLIC_DIR "/"
|
||||
|
||||
static const char* PublicDir = PUBLIC_DIR;
|
||||
|
||||
struct FileSystem
|
||||
{
|
||||
char dir[FILENAME_MAX];
|
||||
char work[FILENAME_MAX];
|
||||
|
||||
Net* net;
|
||||
};
|
||||
|
||||
static const char* getFilePath(FileSystem* fs, const char* name)
|
||||
{
|
||||
static char path[FILENAME_MAX] = {0};
|
||||
|
||||
strcpy(path, fs->dir);
|
||||
|
||||
if(strlen(fs->work))
|
||||
{
|
||||
strcat(path, fs->work);
|
||||
strcat(path, "/");
|
||||
}
|
||||
|
||||
strcat(path, name);
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
char* ptr = path;
|
||||
while (*ptr)
|
||||
{
|
||||
if (*ptr == '/') *ptr = '\\';
|
||||
ptr++;
|
||||
}
|
||||
#endif
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
|
||||
static bool isRoot(FileSystem* fs)
|
||||
{
|
||||
return strlen(fs->work) == 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool isPublicRoot(FileSystem* fs)
|
||||
{
|
||||
return strcmp(fs->work, PublicDir) == 0;
|
||||
}
|
||||
|
||||
static bool isPublic(FileSystem* fs)
|
||||
{
|
||||
return strcmp(fs->work, PublicDir) == 0 || memcmp(fs->work, PUBLIC_DIR_SLASH, sizeof PUBLIC_DIR_SLASH - 1) == 0;
|
||||
}
|
||||
|
||||
bool fsIsInPublicDir(FileSystem* fs)
|
||||
{
|
||||
return isPublic(fs);
|
||||
}
|
||||
|
||||
#if defined(__WINDOWS__) || defined(__WINRT__)
|
||||
|
||||
#define UTF8ToString(S) (wchar_t *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1)
|
||||
#define StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(wchar_t))
|
||||
|
||||
#define TIC_DIR _WDIR
|
||||
#define tic_dirent _wdirent
|
||||
#define tic_stat_struct _stat
|
||||
|
||||
#define tic_opendir _wopendir
|
||||
#define tic_readdir _wreaddir
|
||||
#define tic_closedir _wclosedir
|
||||
#define tic_rmdir _wrmdir
|
||||
#define tic_stat _wstat
|
||||
#define tic_remove _wremove
|
||||
#define tic_fopen _wfopen
|
||||
#define tic_mkdir(name) _wmkdir(name)
|
||||
#define tic_system _wsystem
|
||||
|
||||
#else
|
||||
|
||||
#define UTF8ToString(S) (S)
|
||||
#define StringToUTF8(S) (S)
|
||||
|
||||
#define TIC_DIR DIR
|
||||
#define tic_dirent dirent
|
||||
#define tic_stat_struct stat
|
||||
|
||||
#define tic_opendir opendir
|
||||
#define tic_readdir readdir
|
||||
#define tic_closedir closedir
|
||||
#define tic_rmdir rmdir
|
||||
#define tic_stat stat
|
||||
#define tic_remove remove
|
||||
#define tic_fopen fopen
|
||||
#define tic_mkdir(name) mkdir(name, 0700)
|
||||
#define tic_system system
|
||||
|
||||
#endif
|
||||
|
||||
void fsEnumFiles(FileSystem* fs, ListCallback callback, void* data)
|
||||
{
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
|
||||
if(isRoot(fs) && !callback(PublicDir, NULL, 0, data, true))return;
|
||||
|
||||
if(isPublic(fs))
|
||||
{
|
||||
netDirRequest(fs->net, fs->work + sizeof(TIC_HOST), callback, data);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TIC_DIR *dir = NULL;
|
||||
struct tic_dirent* ent = NULL;
|
||||
|
||||
const char* path = getFilePath(fs, "");
|
||||
|
||||
if ((dir = tic_opendir(UTF8ToString(path))) != NULL)
|
||||
{
|
||||
while ((ent = tic_readdir(dir)) != NULL)
|
||||
{
|
||||
if (ent->d_type == DT_DIR && *ent->d_name != '.')
|
||||
{
|
||||
if (!callback(StringToUTF8(ent->d_name), NULL, 0, data, true))break;
|
||||
}
|
||||
}
|
||||
|
||||
tic_closedir(dir);
|
||||
}
|
||||
|
||||
if ((dir = tic_opendir(UTF8ToString(path))) != NULL)
|
||||
{
|
||||
while ((ent = tic_readdir(dir)) != NULL)
|
||||
{
|
||||
if (ent->d_type == DT_REG)
|
||||
{
|
||||
if (!callback(StringToUTF8(ent->d_name), NULL, 0, data, false))break;
|
||||
}
|
||||
}
|
||||
|
||||
tic_closedir(dir);
|
||||
}
|
||||
}
|
||||
|
||||
bool fsDeleteDir(FileSystem* fs, const char* name)
|
||||
{
|
||||
#if defined(__WINRT__) || defined(__WINDOWS__)
|
||||
const char* path = getFilePath(fs, name);
|
||||
bool result = tic_rmdir(UTF8ToString(path));
|
||||
#else
|
||||
bool result = rmdir(getFilePath(fs, name));
|
||||
#endif
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
EM_ASM(FS.syncfs(function(){}));
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fsDeleteFile(FileSystem* fs, const char* name)
|
||||
{
|
||||
const char* path = getFilePath(fs, name);
|
||||
bool result = tic_remove(UTF8ToString(path));
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
EM_ASM(FS.syncfs(function(){}));
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FileSystem* fs;
|
||||
AddCallback callback;
|
||||
void* data;
|
||||
} AddFileData;
|
||||
|
||||
static void onAddFile(const char* name, const u8* buffer, s32 size, void* data, u32 mode)
|
||||
{
|
||||
AddFileData* addFileData = (AddFileData*)data;
|
||||
FileSystem* fs = addFileData->fs;
|
||||
|
||||
if(name)
|
||||
{
|
||||
const char* destname = getFilePath(fs, name);
|
||||
|
||||
FILE* file = tic_fopen(UTF8ToString(destname), UTF8ToString("rb"));
|
||||
if(file)
|
||||
{
|
||||
fclose(file);
|
||||
|
||||
addFileData->callback(name, FS_FILE_EXISTS, addFileData->data);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* path = getFilePath(fs, name);
|
||||
FILE* dest = tic_fopen(UTF8ToString(path), UTF8ToString("wb"));
|
||||
|
||||
if (dest)
|
||||
{
|
||||
fwrite(buffer, 1, size, dest);
|
||||
fclose(dest);
|
||||
|
||||
#if !defined(__WINRT__) && !defined(__WINDOWS__)
|
||||
if(mode)
|
||||
chmod(path, mode);
|
||||
#endif
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
EM_ASM(FS.syncfs(function(){}));
|
||||
#endif
|
||||
|
||||
addFileData->callback(name, FS_FILE_ADDED, addFileData->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addFileData->callback(name, FS_FILE_NOT_ADDED, addFileData->data);
|
||||
}
|
||||
|
||||
SDL_free(addFileData);
|
||||
}
|
||||
|
||||
void fsAddFile(FileSystem* fs, AddCallback callback, void* data)
|
||||
{
|
||||
AddFileData* addFileData = (AddFileData*)SDL_malloc(sizeof(AddFileData));
|
||||
|
||||
*addFileData = (AddFileData) { fs, callback, data };
|
||||
|
||||
file_dialog_load(&onAddFile, addFileData);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GetCallback callback;
|
||||
void* data;
|
||||
void* buffer;
|
||||
} GetFileData;
|
||||
|
||||
static void onGetFile(bool result, void* data)
|
||||
{
|
||||
GetFileData* command = (GetFileData*)data;
|
||||
|
||||
command->callback(result ? FS_FILE_DOWNLOADED : FS_FILE_NOT_DOWNLOADED, command->data);
|
||||
|
||||
SDL_free(command->buffer);
|
||||
SDL_free(command);
|
||||
}
|
||||
|
||||
static u32 fsGetMode(FileSystem* fs, const char* name)
|
||||
{
|
||||
|
||||
#if defined(__WINRT__) || defined(__WINDOWS__)
|
||||
return 0;
|
||||
#else
|
||||
const char* path = getFilePath(fs, name);
|
||||
mode_t mode = 0;
|
||||
struct stat s;
|
||||
if(stat(path, &s) == 0)
|
||||
mode = s.st_mode;
|
||||
|
||||
return mode;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void fsHomeDir(FileSystem* fs)
|
||||
{
|
||||
memset(fs->work, 0, sizeof fs->work);
|
||||
}
|
||||
|
||||
void fsDirBack(FileSystem* fs)
|
||||
{
|
||||
if(isPublicRoot(fs))
|
||||
{
|
||||
fsHomeDir(fs);
|
||||
return;
|
||||
}
|
||||
|
||||
char* start = fs->work;
|
||||
char* ptr = start + strlen(fs->work);
|
||||
|
||||
while(ptr > start && *ptr != '/') ptr--;
|
||||
|
||||
*ptr = '\0';
|
||||
}
|
||||
|
||||
const char* fsGetDir(FileSystem* fs)
|
||||
{
|
||||
return fs->work;
|
||||
}
|
||||
|
||||
bool fsChangeDir(FileSystem* fs, const char* dir)
|
||||
{
|
||||
if(fsIsDir(fs, dir))
|
||||
{
|
||||
if(strlen(fs->work))
|
||||
strcat(fs->work, "/");
|
||||
|
||||
strcat(fs->work, dir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
bool found;
|
||||
|
||||
} EnumPublicDirsData;
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
static bool onEnumPublicDirs(const char* name, const char* info, s32 id, void* data, bool dir)
|
||||
{
|
||||
EnumPublicDirsData* enumPublicDirsData = (EnumPublicDirsData*)data;
|
||||
|
||||
if(strcmp(name, enumPublicDirsData->name) == 0)
|
||||
{
|
||||
enumPublicDirsData->found = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool fsIsDir(FileSystem* fs, const char* name)
|
||||
{
|
||||
if(*name == '.') return false;
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
if(isRoot(fs) && strcmp(name, PublicDir) == 0)
|
||||
return true;
|
||||
|
||||
if(isPublicRoot(fs))
|
||||
{
|
||||
EnumPublicDirsData enumPublicDirsData =
|
||||
{
|
||||
.name = name,
|
||||
.found = false,
|
||||
};
|
||||
|
||||
fsEnumFiles(fs, onEnumPublicDirs, &enumPublicDirsData);
|
||||
|
||||
return enumPublicDirsData.found;
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* path = getFilePath(fs, name);
|
||||
struct tic_stat_struct s;
|
||||
return tic_stat(UTF8ToString(path), &s) == 0 && S_ISDIR(s.st_mode);
|
||||
}
|
||||
|
||||
void fsGetFileData(GetCallback callback, const char* name, void* buffer, size_t size, u32 mode, void* data)
|
||||
{
|
||||
GetFileData* command = (GetFileData*)SDL_malloc(sizeof(GetFileData));
|
||||
*command = (GetFileData) {callback, data, buffer};
|
||||
|
||||
file_dialog_save(onGetFile, name, buffer, size, command, mode);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
OpenCallback callback;
|
||||
void* data;
|
||||
} OpenFileData;
|
||||
|
||||
static void onOpenFileData(const char* name, const u8* buffer, s32 size, void* data, u32 mode)
|
||||
{
|
||||
OpenFileData* command = (OpenFileData*)data;
|
||||
|
||||
command->callback(name, buffer, size, command->data);
|
||||
|
||||
SDL_free(command);
|
||||
}
|
||||
|
||||
void fsOpenFileData(OpenCallback callback, void* data)
|
||||
{
|
||||
OpenFileData* command = (OpenFileData*)SDL_malloc(sizeof(OpenFileData));
|
||||
|
||||
*command = (OpenFileData){callback, data};
|
||||
|
||||
file_dialog_load(onOpenFileData, command);
|
||||
}
|
||||
|
||||
void fsGetFile(FileSystem* fs, GetCallback callback, const char* name, void* data)
|
||||
{
|
||||
s32 size = 0;
|
||||
void* buffer = fsLoadFile(fs, name, &size);
|
||||
|
||||
if(buffer)
|
||||
{
|
||||
GetFileData* command = (GetFileData*)SDL_malloc(sizeof(GetFileData));
|
||||
*command = (GetFileData) {callback, data, buffer};
|
||||
|
||||
s32 mode = fsGetMode(fs, name);
|
||||
file_dialog_save(onGetFile, name, buffer, size, command, mode);
|
||||
}
|
||||
else callback(FS_FILE_NOT_DOWNLOADED, data);
|
||||
}
|
||||
|
||||
bool fsWriteFile(const char* name, const void* buffer, s32 size)
|
||||
{
|
||||
FILE* file = tic_fopen(UTF8ToString(name), UTF8ToString("wb"));
|
||||
|
||||
if(file)
|
||||
{
|
||||
fwrite(buffer, 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
EM_ASM(FS.syncfs(function(){}));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fsCopyFile(const char* src, const char* dst)
|
||||
{
|
||||
bool done = false;
|
||||
|
||||
void* buffer = NULL;
|
||||
s32 size = 0;
|
||||
|
||||
{
|
||||
FILE* file = tic_fopen(UTF8ToString(src), UTF8ToString("rb"));
|
||||
if(file)
|
||||
{
|
||||
fseek(file, 0, SEEK_END);
|
||||
size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
if((buffer = SDL_malloc(size)) && fread(buffer, size, 1, file)) {}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
if(buffer)
|
||||
{
|
||||
FILE* file = tic_fopen(UTF8ToString(dst), UTF8ToString("wb"));
|
||||
|
||||
if(file)
|
||||
{
|
||||
fwrite(buffer, 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
done = true;
|
||||
}
|
||||
|
||||
SDL_free(buffer);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
void* fsReadFile(const char* path, s32* size)
|
||||
{
|
||||
FILE* file = tic_fopen(UTF8ToString(path), UTF8ToString("rb"));
|
||||
void* buffer = NULL;
|
||||
|
||||
if(file)
|
||||
{
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
*size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
if((buffer = SDL_malloc(*size)) && fread(buffer, *size, 1, file)) {}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void makeDir(const char* name)
|
||||
{
|
||||
tic_mkdir(UTF8ToString(name));
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
EM_ASM(FS.syncfs(function(){}));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool fsExistsFile(FileSystem* fs, const char* name)
|
||||
{
|
||||
const char* path = getFilePath(fs, name);
|
||||
FILE* file = tic_fopen(UTF8ToString(path), UTF8ToString("rb"));
|
||||
|
||||
if(file)
|
||||
{
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fsSaveFile(FileSystem* fs, const char* name, const void* data, size_t size, bool overwrite)
|
||||
{
|
||||
if(!overwrite)
|
||||
{
|
||||
if(fsExistsFile(fs, name))
|
||||
return false;
|
||||
}
|
||||
|
||||
return fsWriteFile(getFilePath(fs, name), data, size);
|
||||
}
|
||||
|
||||
bool fsSaveRootFile(FileSystem* fs, const char* name, const void* data, size_t size, bool overwrite)
|
||||
{
|
||||
char path[FILENAME_MAX];
|
||||
strcpy(path, fs->work);
|
||||
fsHomeDir(fs);
|
||||
|
||||
bool ret = fsSaveFile(fs, name, data, size, overwrite);
|
||||
|
||||
strcpy(fs->work, path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
char hash[FILENAME_MAX];
|
||||
|
||||
} LoadPublicCartData;
|
||||
|
||||
static bool onLoadPublicCart(const char* name, const char* info, s32 id, void* data, bool dir)
|
||||
{
|
||||
LoadPublicCartData* loadPublicCartData = (LoadPublicCartData*)data;
|
||||
|
||||
if(strcmp(name, loadPublicCartData->name) == 0 && info && strlen(info))
|
||||
{
|
||||
strcpy(loadPublicCartData->hash, info);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* fsLoadFile(FileSystem* fs, const char* name, s32* size)
|
||||
{
|
||||
if(isPublic(fs))
|
||||
{
|
||||
LoadPublicCartData loadPublicCartData =
|
||||
{
|
||||
.name = name,
|
||||
.hash = {0},
|
||||
};
|
||||
|
||||
fsEnumFiles(fs, onLoadPublicCart, &loadPublicCartData);
|
||||
|
||||
if(strlen(loadPublicCartData.hash))
|
||||
{
|
||||
char cachePath[FILENAME_MAX] = {0};
|
||||
sprintf(cachePath, TIC_CACHE "%s.tic", loadPublicCartData.hash);
|
||||
|
||||
{
|
||||
void* data = fsLoadRootFile(fs, cachePath, size);
|
||||
if(data) return data;
|
||||
}
|
||||
|
||||
char path[FILENAME_MAX] = {0};
|
||||
sprintf(path, "/cart/%s/cart.tic", loadPublicCartData.hash);
|
||||
void* data = netGetRequest(fs->net, path, size);
|
||||
|
||||
if(data)
|
||||
fsSaveRootFile(fs, cachePath, data, *size, false);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FILE* file = tic_fopen(UTF8ToString(getFilePath(fs, name)), UTF8ToString("rb"));
|
||||
void* ptr = NULL;
|
||||
|
||||
if(file)
|
||||
{
|
||||
fseek(file, 0, SEEK_END);
|
||||
*size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
u8* buffer = SDL_malloc(*size);
|
||||
|
||||
if(buffer && fread(buffer, *size, 1, file)) ptr = buffer;
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* fsLoadRootFile(FileSystem* fs, const char* name, s32* size)
|
||||
{
|
||||
char path[FILENAME_MAX];
|
||||
strcpy(path, fs->work);
|
||||
fsHomeDir(fs);
|
||||
|
||||
void* ret = fsLoadFile(fs, name, size);
|
||||
|
||||
strcpy(fs->work, path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fsMakeDir(FileSystem* fs, const char* name)
|
||||
{
|
||||
makeDir(getFilePath(fs, name));
|
||||
}
|
||||
|
||||
#if defined(__WINDOWS__) || defined(__LINUX__) || defined(__MACOSX__)
|
||||
|
||||
void fsOpenSystemPath(FileSystem* fs, const char* path)
|
||||
{
|
||||
char command[FILENAME_MAX];
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
|
||||
sprintf(command, "explorer \"%s\"", path);
|
||||
|
||||
#elif defined(__LINUX__)
|
||||
|
||||
sprintf(command, "xdg-open \"%s\"", path);
|
||||
|
||||
#elif defined(__MACOSX__)
|
||||
|
||||
sprintf(command, "open \"%s\"", path);
|
||||
|
||||
#endif
|
||||
|
||||
tic_system(UTF8ToString(command));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void fsOpenSystemPath(FileSystem* fs, const char* path) {}
|
||||
|
||||
#endif
|
||||
|
||||
void fsOpenWorkingFolder(FileSystem* fs)
|
||||
{
|
||||
const char* path = getFilePath(fs, "");
|
||||
|
||||
if(isPublic(fs))
|
||||
path = fs->dir;
|
||||
|
||||
fsOpenSystemPath(fs, path);
|
||||
}
|
||||
|
||||
void createFileSystem(void(*callback)(FileSystem*))
|
||||
{
|
||||
FileSystem* fs = (FileSystem*)SDL_malloc(sizeof(FileSystem));
|
||||
memset(fs, 0, sizeof(FileSystem));
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
|
||||
strcpy(fs->dir, "/" TIC_PACKAGE "/" TIC_NAME "/");
|
||||
|
||||
#elif defined(__ANDROID__)
|
||||
|
||||
strcpy(fs->dir, SDL_AndroidGetExternalStoragePath());
|
||||
const char AppFolder[] = "/" TIC_NAME "/";
|
||||
strcat(fs->dir, AppFolder);
|
||||
mkdir(fs->dir, 0700);
|
||||
|
||||
#else
|
||||
|
||||
char* path = SDL_GetPrefPath(TIC_PACKAGE, TIC_NAME);
|
||||
strcpy(fs->dir, path);
|
||||
SDL_free(path);
|
||||
|
||||
#endif
|
||||
|
||||
fs->net = createNet();
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
EM_ASM_
|
||||
(
|
||||
{
|
||||
var dir = "";
|
||||
Module.Pointer_stringify($0).split("/").forEach(function(val)
|
||||
{
|
||||
if(val.length)
|
||||
{
|
||||
dir += "/" + val;
|
||||
FS.mkdir(dir);
|
||||
}
|
||||
});
|
||||
|
||||
FS.mount(IDBFS, {}, dir);
|
||||
FS.syncfs(true, function(error)
|
||||
{
|
||||
if(error) console.log(error);
|
||||
else Runtime.dynCall('vi', $1, [$2]);
|
||||
});
|
||||
}, fs->dir, callback, fs
|
||||
);
|
||||
#else
|
||||
callback(fs);
|
||||
#endif
|
||||
}
|
||||
76
src/fs.h
Normal file
76
src/fs.h
Normal file
@@ -0,0 +1,76 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tic80_types.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct FileSystem FileSystem;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FS_FILE_NOT_ADDED,
|
||||
FS_FILE_ADDED,
|
||||
FS_FILE_EXISTS,
|
||||
} AddResult;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FS_FILE_NOT_DOWNLOADED,
|
||||
FS_FILE_DOWNLOADED,
|
||||
} GetResult;
|
||||
|
||||
typedef bool(*ListCallback)(const char* name, const char* info, s32 id, void* data, bool dir);
|
||||
typedef void(*AddCallback)(const char*, AddResult, void*);
|
||||
typedef void(*GetCallback)(GetResult, void*);
|
||||
typedef void(*OpenCallback)(const char* name, const void* buffer, size_t size, void* data);
|
||||
|
||||
typedef struct FileSystem FileSystem;
|
||||
|
||||
void createFileSystem(void(*callback)(FileSystem*));
|
||||
|
||||
void fsEnumFiles(FileSystem* fs, ListCallback callback, void* data);
|
||||
void fsAddFile(FileSystem* fs, AddCallback callback, void* data);
|
||||
void fsGetFile(FileSystem* fs, GetCallback callback, const char* name, void* data);
|
||||
bool fsDeleteFile(FileSystem* fs, const char* name);
|
||||
bool fsDeleteDir(FileSystem* fs, const char* name);
|
||||
bool fsSaveFile(FileSystem* fs, const char* name, const void* data, size_t size, bool overwrite);
|
||||
bool fsSaveRootFile(FileSystem* fs, const char* name, const void* data, size_t size, bool overwrite);
|
||||
void* fsLoadFile(FileSystem* fs, const char* name, s32* size);
|
||||
void* fsLoadRootFile(FileSystem* fs, const char* name, s32* size);
|
||||
void fsMakeDir(FileSystem* fs, const char* name);
|
||||
bool fsExistsFile(FileSystem* fs, const char* name);
|
||||
|
||||
void* fsReadFile(const char* path, s32* size);
|
||||
bool fsWriteFile(const char* path, const void* data, s32 size);
|
||||
bool fsCopyFile(const char* src, const char* dst);
|
||||
void fsGetFileData(GetCallback callback, const char* name, void* buffer, size_t size, u32 mode, void* data);
|
||||
void fsOpenFileData(OpenCallback callback, void* data);
|
||||
void fsOpenWorkingFolder(FileSystem* fs);
|
||||
void fsOpenSystemPath(FileSystem* fs, const char* path);
|
||||
bool fsIsDir(FileSystem* fs, const char* dir);
|
||||
bool fsIsInPublicDir(FileSystem* fs);
|
||||
bool fsChangeDir(FileSystem* fs, const char* dir);
|
||||
const char* fsGetDir(FileSystem* fs);
|
||||
void fsDirBack(FileSystem* fs);
|
||||
void fsHomeDir(FileSystem* fs);
|
||||
193
src/history.c
Normal file
193
src/history.c
Normal file
@@ -0,0 +1,193 @@
|
||||
// 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 "history.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8* buffer;
|
||||
u32 start;
|
||||
u32 end;
|
||||
} Data;
|
||||
|
||||
typedef struct Item Item;
|
||||
|
||||
struct Item
|
||||
{
|
||||
Item* next;
|
||||
Item* prev;
|
||||
|
||||
Data data;
|
||||
};
|
||||
|
||||
static void list_delete(Item* list, Item* from)
|
||||
{
|
||||
Item* it = from;
|
||||
|
||||
while(it)
|
||||
{
|
||||
Item* next = it->next;
|
||||
|
||||
if(it->data.buffer) free(it->data.buffer);
|
||||
if(it) free(it);
|
||||
|
||||
it = next;
|
||||
}
|
||||
}
|
||||
|
||||
static Item* list_insert(Item* list, Data* data)
|
||||
{
|
||||
Item* item = (Item*)malloc(sizeof(Item));
|
||||
item->next = NULL;
|
||||
item->prev = NULL;
|
||||
item->data = *data;
|
||||
|
||||
if(list)
|
||||
{
|
||||
list_delete(list, list->next);
|
||||
|
||||
list->next = item;
|
||||
item->prev = list;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static Item* list_first(Item* list)
|
||||
{
|
||||
Item* it = list;
|
||||
while(it->prev) it = it->prev;
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
struct History
|
||||
{
|
||||
Item* list;
|
||||
|
||||
u32 size;
|
||||
u8* state;
|
||||
|
||||
void* data;
|
||||
};
|
||||
|
||||
History* history_create(void* data, u32 size)
|
||||
{
|
||||
History* history = (History*)malloc(sizeof(History));
|
||||
history->data = data;
|
||||
|
||||
history->list = NULL;
|
||||
history->size = size;
|
||||
|
||||
history->state = malloc(size);
|
||||
memcpy(history->state, data, history->size);
|
||||
|
||||
// empty diff
|
||||
history->list = list_insert(history->list, &(Data){NULL, 0, 0});
|
||||
|
||||
return history;
|
||||
}
|
||||
|
||||
void history_delete(History* history)
|
||||
{
|
||||
if(history)
|
||||
{
|
||||
free(history->state);
|
||||
|
||||
list_delete(history->list, list_first(history->list));
|
||||
|
||||
free(history);
|
||||
}
|
||||
}
|
||||
|
||||
static void history_diff(History* history, Data* data)
|
||||
{
|
||||
for (u32 i = data->start, k = 0; i < data->end; ++i, ++k)
|
||||
history->state[i] ^= data->buffer[k];
|
||||
}
|
||||
|
||||
static u32 trim_left(u8* data, u32 size)
|
||||
{
|
||||
for(u32 i = 0; i < size; i++)
|
||||
if(data[i]) return i;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static u32 trim_right(u8* data, u32 size)
|
||||
{
|
||||
for(u32 i = 0; i < size; i++)
|
||||
if(data[size - i - 1]) return size - i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool history_add(History* history)
|
||||
{
|
||||
if (memcmp(history->state, history->data, history->size) == 0) return false;
|
||||
|
||||
history_diff(history, &(Data){history->data, 0, history->size});
|
||||
|
||||
{
|
||||
Data data;
|
||||
data.start = trim_left(history->state, history->size);
|
||||
data.end = trim_right(history->state, history->size);
|
||||
u32 size = data.end - data.start;
|
||||
data.buffer = malloc(size);
|
||||
|
||||
memcpy(data.buffer, (u8*)history->state + data.start, size);
|
||||
|
||||
history->list = list_insert(history->list, &data);
|
||||
}
|
||||
|
||||
memcpy(history->state, history->data, history->size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void history_undo(History* history)
|
||||
{
|
||||
if(history->list->prev)
|
||||
{
|
||||
history_diff(history, &history->list->data);
|
||||
|
||||
history->list = history->list->prev;
|
||||
}
|
||||
|
||||
memcpy(history->data, history->state, history->size);
|
||||
}
|
||||
|
||||
void history_redo(History* history)
|
||||
{
|
||||
if(history->list->next)
|
||||
{
|
||||
history->list = history->list->next;
|
||||
|
||||
history_diff(history, &history->list->data);
|
||||
}
|
||||
|
||||
memcpy(history->data, history->state, history->size);
|
||||
}
|
||||
33
src/history.h
Normal file
33
src/history.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tic80_types.h>
|
||||
|
||||
typedef struct History History;
|
||||
|
||||
History* history_create(void* data, u32 size);
|
||||
bool history_add(History* history);
|
||||
void history_undo(History* history);
|
||||
void history_redo(History* history);
|
||||
void history_delete(History* history);
|
||||
37
src/html.c
Normal file
37
src/html.c
Normal file
@@ -0,0 +1,37 @@
|
||||
// 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 <tic80_types.h>
|
||||
|
||||
const u8 EmbedIndexZip[] =
|
||||
{
|
||||
#include "../bin/assets/index.html.dat"
|
||||
};
|
||||
|
||||
const s32 EmbedIndexZipSize = sizeof EmbedIndexZip;
|
||||
|
||||
const u8 EmbedTicJsZip[] =
|
||||
{
|
||||
#include "../bin/assets/tic.js.dat"
|
||||
};
|
||||
|
||||
const s32 EmbedTicJsZipSize = sizeof EmbedTicJsZip;
|
||||
815
src/jsapi.c
Normal file
815
src/jsapi.c
Normal file
@@ -0,0 +1,815 @@
|
||||
// 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 "machine.h"
|
||||
#include "tools.h"
|
||||
|
||||
#include "ext/duktape/duktape.h"
|
||||
|
||||
static const char TicMachine[] = "_TIC80";
|
||||
|
||||
void closeJavascript(tic_machine* machine)
|
||||
{
|
||||
if(machine->js)
|
||||
{
|
||||
duk_destroy_heap(machine->js);
|
||||
machine->js = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static tic_machine* getDukMachine(duk_context* duk)
|
||||
{
|
||||
duk_push_global_stash(duk);
|
||||
duk_get_prop_string(duk, -1, TicMachine);
|
||||
tic_machine* machine = duk_to_pointer(duk, -1);
|
||||
duk_pop_2(duk);
|
||||
|
||||
return machine;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_print(duk_context* duk)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
const char* text = duk_is_null_or_undefined(duk, 0) ? "" : duk_to_string(duk, 0);
|
||||
s32 x = duk_is_null_or_undefined(duk, 1) ? 0 : duk_to_int(duk, 1);
|
||||
s32 y = duk_is_null_or_undefined(duk, 2) ? 0 : duk_to_int(duk, 2);
|
||||
s32 color = duk_is_null_or_undefined(duk, 3) ? (TIC_PALETTE_SIZE-1) : duk_to_int(duk, 3);
|
||||
bool fixed = duk_is_null_or_undefined(duk, 4) ? false : duk_to_boolean(duk, 4);
|
||||
s32 scale = duk_is_null_or_undefined(duk, 5) ? 1 : duk_to_int(duk, 5);
|
||||
|
||||
s32 size = memory->api.text_ex(memory, text ? text : "nil", x, y, color, fixed, scale);
|
||||
|
||||
duk_push_uint(duk, size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_cls(duk_context* duk)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
memory->api.clear(memory, duk_is_null_or_undefined(duk, 0) ? 0 : duk_to_int(duk, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_pix(duk_context* duk)
|
||||
{
|
||||
s32 x = duk_to_int(duk, 0);
|
||||
s32 y = duk_to_int(duk, 1);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
if(duk_is_null_or_undefined(duk, 2))
|
||||
{
|
||||
duk_push_uint(duk, memory->api.get_pixel(memory, x, y));
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 color = duk_to_int(duk, 2);
|
||||
memory->api.pixel(memory, x, y, color);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_line(duk_context* duk)
|
||||
{
|
||||
s32 x0 = duk_to_int(duk, 0);
|
||||
s32 y0 = duk_to_int(duk, 1);
|
||||
s32 x1 = duk_to_int(duk, 2);
|
||||
s32 y1 = duk_to_int(duk, 3);
|
||||
s32 color = duk_to_int(duk, 4);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
memory->api.line(memory, x0, y0, x1, y1, color);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_rect(duk_context* duk)
|
||||
{
|
||||
s32 x = duk_to_int(duk, 0);
|
||||
s32 y = duk_to_int(duk, 1);
|
||||
s32 w = duk_to_int(duk, 2);
|
||||
s32 h = duk_to_int(duk, 3);
|
||||
s32 color = duk_to_int(duk, 4);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
memory->api.rect(memory, x, y, w, h, color);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_rectb(duk_context* duk)
|
||||
{
|
||||
s32 x = duk_to_int(duk, 0);
|
||||
s32 y = duk_to_int(duk, 1);
|
||||
s32 w = duk_to_int(duk, 2);
|
||||
s32 h = duk_to_int(duk, 3);
|
||||
s32 color = duk_to_int(duk, 4);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
memory->api.rect_border(memory, x, y, w, h, color);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_spr(duk_context* duk)
|
||||
{
|
||||
static u8 colors[TIC_PALETTE_SIZE];
|
||||
s32 count = 0;
|
||||
|
||||
s32 index = duk_is_null_or_undefined(duk, 0) ? 0 : duk_to_int(duk, 0);
|
||||
s32 x = duk_is_null_or_undefined(duk, 1) ? 0 : duk_to_int(duk, 1);
|
||||
s32 y = duk_is_null_or_undefined(duk, 2) ? 0 : duk_to_int(duk, 2);
|
||||
|
||||
{
|
||||
if(!duk_is_null_or_undefined(duk, 3))
|
||||
{
|
||||
if(duk_is_array(duk, 3))
|
||||
{
|
||||
for(s32 i = 0; i < TIC_PALETTE_SIZE; i++)
|
||||
{
|
||||
duk_get_prop_index(duk, 3, i);
|
||||
if(duk_is_null_or_undefined(duk, -1))
|
||||
{
|
||||
duk_pop(duk);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[i] = duk_to_int(duk, -1);
|
||||
count++;
|
||||
duk_pop(duk);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[0] = duk_to_int(duk, 3);
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 scale = duk_is_null_or_undefined(duk, 4) ? 1 : duk_to_int(duk, 4);
|
||||
tic_flip flip = duk_is_null_or_undefined(duk, 5) ? tic_no_flip : duk_to_int(duk, 5);
|
||||
tic_rotate rotate = duk_is_null_or_undefined(duk, 6) ? tic_no_rotate : duk_to_int(duk, 6);
|
||||
s32 w = duk_is_null_or_undefined(duk, 7) ? 1 : duk_to_int(duk, 7);
|
||||
s32 h = duk_is_null_or_undefined(duk, 8) ? 1 : duk_to_int(duk, 8);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
memory->api.sprite_ex(memory, &memory->ram.gfx, index, x, y, w, h, colors, count, scale, flip, rotate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_btn(duk_context* duk)
|
||||
{
|
||||
tic_machine* machine = getDukMachine(duk);
|
||||
|
||||
if(machine->memory.input == tic_gamepad_input)
|
||||
{
|
||||
if (duk_is_null_or_undefined(duk, 0))
|
||||
{
|
||||
duk_push_uint(duk, machine->memory.ram.vram.input.gamepad.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 index = duk_to_int(duk, 0) & 0xf;
|
||||
duk_push_uint(duk, machine->memory.ram.vram.input.gamepad.data & (1 << index));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
else duk_error(duk, DUK_ERR_ERROR, "gamepad input not declared in metadata\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_btnp(duk_context* duk)
|
||||
{
|
||||
tic_machine* machine = getDukMachine(duk);
|
||||
tic_mem* memory = (tic_mem*)machine;
|
||||
|
||||
if(machine->memory.input == tic_gamepad_input)
|
||||
{
|
||||
if (duk_is_null_or_undefined(duk, 0))
|
||||
{
|
||||
duk_push_uint(duk, memory->api.btnp(memory, -1, -1, -1));
|
||||
}
|
||||
else if(duk_is_null_or_undefined(duk, 1) && duk_is_null_or_undefined(duk, 2))
|
||||
{
|
||||
s32 index = duk_to_int(duk, 0) & 0xf;
|
||||
|
||||
duk_push_boolean(duk, memory->api.btnp(memory, index, -1, -1));
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 index = duk_to_int(duk, 0) & 0xf;
|
||||
u32 hold = duk_to_int(duk, 1);
|
||||
u32 period = duk_to_int(duk, 2);
|
||||
|
||||
duk_push_boolean(duk, memory->api.btnp(memory, index, hold, period));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
else duk_error(duk, DUK_ERR_ERROR, "gamepad input not declared in metadata\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_sfx(duk_context* duk)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
s32 index = duk_is_null_or_undefined(duk, 0) ? -1 : duk_to_int(duk, 0);
|
||||
|
||||
s32 note = -1;
|
||||
s32 octave = -1;
|
||||
s32 speed = SFX_DEF_SPEED;
|
||||
|
||||
if (index < SFX_COUNT)
|
||||
{
|
||||
if(index >= 0)
|
||||
{
|
||||
tic_sound_effect* effect = memory->ram.sound.sfx.data + index;
|
||||
|
||||
note = effect->note;
|
||||
octave = effect->octave;
|
||||
speed = effect->speed;
|
||||
|
||||
if(!duk_is_null_or_undefined(duk, 1))
|
||||
{
|
||||
if(duk_is_string(duk, 1))
|
||||
{
|
||||
const char* noteStr = duk_to_string(duk, 1);
|
||||
|
||||
if(!tic_tool_parse_note(noteStr, ¬e, &octave))
|
||||
{
|
||||
duk_error(duk, DUK_ERR_ERROR, "invalid note, should be like C#4\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 id = duk_to_int(duk, 1);
|
||||
note = id % NOTES;
|
||||
octave = id / NOTES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
duk_error(duk, DUK_ERR_ERROR, "unknown sfx index\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 duration = duk_is_null_or_undefined(duk, 2) ? -1 : duk_to_int(duk, 2);
|
||||
s32 channel = duk_is_null_or_undefined(duk, 3) ? 0 : duk_to_int(duk, 3);
|
||||
s32 volume = duk_is_null_or_undefined(duk, 4) ? MAX_VOLUME : duk_to_int(duk, 4);
|
||||
|
||||
if(!duk_is_null_or_undefined(duk, 5))
|
||||
speed = duk_to_int(duk, 5);
|
||||
|
||||
if (channel >= 0 && channel < TIC_SOUND_CHANNELS)
|
||||
{
|
||||
memory->api.sfx_stop(memory, channel);
|
||||
memory->api.sfx_ex(memory, index, note, octave, duration, channel, volume & 0xf, speed);
|
||||
}
|
||||
else duk_error(duk, DUK_ERR_ERROR, "unknown channel\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
duk_context* duk;
|
||||
void* remap;
|
||||
} RemapData;
|
||||
|
||||
static void remapCallback(void* data, s32 x, s32 y, RemapResult* result)
|
||||
{
|
||||
|
||||
RemapData* remap = (RemapData*)data;
|
||||
duk_context* duk = remap->duk;
|
||||
|
||||
duk_push_heapptr(duk, remap->remap);
|
||||
duk_push_int(duk, result->index);
|
||||
duk_push_int(duk, x);
|
||||
duk_push_int(duk, y);
|
||||
duk_pcall(duk, 3);
|
||||
|
||||
if(duk_is_array(duk, -1))
|
||||
{
|
||||
duk_get_prop_index(duk, -1, 0);
|
||||
result->index = duk_to_int(duk, -1);
|
||||
duk_pop(duk);
|
||||
|
||||
duk_get_prop_index(duk, -1, 1);
|
||||
result->flip = duk_to_int(duk, -1);
|
||||
duk_pop(duk);
|
||||
|
||||
duk_get_prop_index(duk, -1, 2);
|
||||
result->rotate = duk_to_int(duk, -1);
|
||||
duk_pop(duk);
|
||||
}
|
||||
else
|
||||
{
|
||||
result->index = duk_to_int(duk, -1);
|
||||
}
|
||||
|
||||
duk_pop(duk);
|
||||
}
|
||||
|
||||
static duk_ret_t duk_map(duk_context* duk)
|
||||
{
|
||||
s32 x = duk_is_null_or_undefined(duk, 0) ? 0 : duk_to_int(duk, 0);
|
||||
s32 y = duk_is_null_or_undefined(duk, 1) ? 0 : duk_to_int(duk, 1);
|
||||
s32 w = duk_is_null_or_undefined(duk, 2) ? TIC_MAP_SCREEN_WIDTH : duk_to_int(duk, 2);
|
||||
s32 h = duk_is_null_or_undefined(duk, 3) ? TIC_MAP_SCREEN_HEIGHT : duk_to_int(duk, 3);
|
||||
s32 sx = duk_is_null_or_undefined(duk, 4) ? 0 : duk_to_int(duk, 4);
|
||||
s32 sy = duk_is_null_or_undefined(duk, 5) ? 0 : duk_to_int(duk, 5);
|
||||
u8 chromakey = duk_is_null_or_undefined(duk, 6) ? -1 : duk_to_int(duk, 6);
|
||||
s32 scale = duk_is_null_or_undefined(duk, 7) ? 1 : duk_to_int(duk, 7);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
if (duk_is_null_or_undefined(duk, 8))
|
||||
memory->api.map(memory, &memory->ram.gfx, x, y, w, h, sx, sy, chromakey, scale);
|
||||
else
|
||||
{
|
||||
void* remap = duk_get_heapptr(duk, 8);
|
||||
|
||||
RemapData data = {duk, remap};
|
||||
|
||||
memory->api.remap((tic_mem*)getDukMachine(duk), &memory->ram.gfx, x, y, w, h, sx, sy, chromakey, scale, remapCallback, &data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_mget(duk_context* duk)
|
||||
{
|
||||
s32 x = duk_is_null_or_undefined(duk, 0) ? 0 : duk_to_int(duk, 0);
|
||||
s32 y = duk_is_null_or_undefined(duk, 1) ? 0 : duk_to_int(duk, 1);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
u8 value = memory->api.map_get(memory, &memory->ram.gfx, x, y);
|
||||
duk_push_uint(duk, value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_mset(duk_context* duk)
|
||||
{
|
||||
s32 x = duk_is_null_or_undefined(duk, 0) ? 0 : duk_to_int(duk, 0);
|
||||
s32 y = duk_is_null_or_undefined(duk, 1) ? 0 : duk_to_int(duk, 1);
|
||||
u8 value = duk_is_null_or_undefined(duk, 2) ? 0 : duk_to_int(duk, 2);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
memory->api.map_set(memory, &memory->ram.gfx, x, y, value);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_peek(duk_context* duk)
|
||||
{
|
||||
|
||||
s32 address = duk_to_int(duk, 0);
|
||||
|
||||
if(address >= 0 && address < sizeof(tic_ram))
|
||||
{
|
||||
tic_machine* machine = getDukMachine(duk);
|
||||
duk_push_uint(duk, *((u8*)&machine->memory.ram + address));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_poke(duk_context* duk)
|
||||
{
|
||||
|
||||
s32 address = duk_to_int(duk, 0);
|
||||
u8 value = duk_to_int(duk, 1) & 0xff;
|
||||
|
||||
if(address >= 0 && address < sizeof(tic_ram))
|
||||
{
|
||||
tic_machine* machine = getDukMachine(duk);
|
||||
*((u8*)&machine->memory.ram + address) = value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_peek4(duk_context* duk)
|
||||
{
|
||||
|
||||
s32 address = duk_to_int(duk, 0);
|
||||
|
||||
if(address >= 0 && address < sizeof(tic_ram)*2)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
duk_push_uint(duk, tic_tool_peek4((u8*)&memory->ram, address));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_poke4(duk_context* duk)
|
||||
{
|
||||
|
||||
s32 address = duk_to_int(duk, 0);
|
||||
u8 value = duk_to_int(duk, 1) & 0xff;
|
||||
|
||||
if(address >= 0 && address < sizeof(tic_ram)*2)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
tic_tool_poke4((u8*)&memory->ram, address, value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_memcpy(duk_context* duk)
|
||||
{
|
||||
s32 dest = duk_to_int(duk, 0);
|
||||
s32 src = duk_to_int(duk, 1);
|
||||
s32 size = duk_to_int(duk, 2);
|
||||
s32 dstBound = sizeof(tic_ram) - size;
|
||||
s32 srcBound = sizeof(tic_mem) - size;
|
||||
|
||||
if(size > 0 && dest < dstBound && src < srcBound)
|
||||
{
|
||||
u8* base = (u8*)&getDukMachine(duk)->memory;
|
||||
memcpy(base + dest, base + src, size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_memset(duk_context* duk)
|
||||
{
|
||||
s32 dest = duk_to_int(duk, 0);
|
||||
u8 value = duk_to_int(duk, 1);
|
||||
s32 size = duk_to_int(duk, 2);
|
||||
s32 bound = sizeof(tic_ram) - size;
|
||||
|
||||
if(size > 0 && dest < bound)
|
||||
{
|
||||
u8* base = (u8*)&getDukMachine(duk)->memory;
|
||||
memset(base + dest, value, size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_trace(duk_context* duk)
|
||||
{
|
||||
tic_machine* machine = getDukMachine(duk);
|
||||
|
||||
const char* text = duk_is_null_or_undefined(duk, 0) ? "" : duk_to_string(duk, 0);
|
||||
u8 color = duk_is_null_or_undefined(duk, 1) ? tic_color_white : duk_to_int(duk, 1);
|
||||
|
||||
machine->data->trace(machine->data->data, text ? text : "nil", color);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_pmem(duk_context* duk)
|
||||
{
|
||||
tic_machine* machine = getDukMachine(duk);
|
||||
tic_mem* memory = &machine->memory;
|
||||
|
||||
u32 index = duk_to_int(duk, 0);
|
||||
|
||||
if(index < TIC_PERSISTENT_SIZE)
|
||||
{
|
||||
s32 val = memory->ram.persistent.data[index];
|
||||
|
||||
if(!duk_is_null_or_undefined(duk, 1))
|
||||
memory->ram.persistent.data[index] = duk_to_int(duk, 1);
|
||||
|
||||
duk_push_int(duk, val);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else duk_error(duk, DUK_ERR_ERROR, "invalid persistent memory index\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_time(duk_context* duk)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
duk_push_number(duk, memory->api.time(memory));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_exit(duk_context* duk)
|
||||
{
|
||||
tic_machine* machine = getDukMachine(duk);
|
||||
|
||||
machine->data->exit(machine->data->data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_font(duk_context* duk)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
const char* text = duk_to_string(duk, 0);
|
||||
s32 x = duk_to_int(duk, 1);
|
||||
s32 y = duk_to_int(duk, 2);
|
||||
u8 chromakey = duk_to_int(duk, 3);
|
||||
s32 width = duk_is_null_or_undefined(duk, 4) ? TIC_SPRITESIZE : duk_to_int(duk, 4);
|
||||
s32 height = duk_is_null_or_undefined(duk, 5) ? TIC_SPRITESIZE : duk_to_int(duk, 5);
|
||||
bool fixed = duk_is_null_or_undefined(duk, 6) ? false : duk_to_boolean(duk, 6);
|
||||
s32 scale = duk_is_null_or_undefined(duk, 7) ? 1 : duk_to_int(duk, 7);
|
||||
|
||||
if(scale == 0)
|
||||
{
|
||||
duk_push_int(duk, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
s32 size = drawText(memory, text, x, y, width, height, chromakey, scale, fixed ? drawSpriteFont : drawFixedSpriteFont);
|
||||
|
||||
duk_push_int(duk, size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_mouse(duk_context* duk)
|
||||
{
|
||||
tic_machine* machine = getDukMachine(duk);
|
||||
|
||||
if(machine->memory.input == tic_mouse_input)
|
||||
{
|
||||
u16 data = machine->memory.ram.vram.input.gamepad.data;
|
||||
|
||||
duk_idx_t idx = duk_push_array(duk);
|
||||
duk_push_int(duk, (data & 0x7fff) % TIC80_WIDTH);
|
||||
duk_put_prop_index(duk, idx, 0);
|
||||
duk_push_int(duk, (data & 0x7fff) / TIC80_WIDTH);
|
||||
duk_put_prop_index(duk, idx, 1);
|
||||
duk_push_int(duk, data >> 15);
|
||||
duk_put_prop_index(duk, idx, 2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else duk_error(duk, DUK_ERR_ERROR, "mouse input not declared in metadata\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_circ(duk_context* duk)
|
||||
{
|
||||
s32 radius = duk_to_int(duk, 2);
|
||||
if(radius < 0) return 0;
|
||||
|
||||
s32 x = duk_to_int(duk, 0);
|
||||
s32 y = duk_to_int(duk, 1);
|
||||
s32 color = duk_to_int(duk, 3);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
memory->api.circle(memory, x, y, radius, color);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_circb(duk_context* duk)
|
||||
{
|
||||
s32 radius = duk_to_int(duk, 2);
|
||||
if(radius < 0) return 0;
|
||||
|
||||
s32 x = duk_to_int(duk, 0);
|
||||
s32 y = duk_to_int(duk, 1);
|
||||
s32 color = duk_to_int(duk, 3);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
memory->api.circle_border(memory, x, y, radius, color);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_tri(duk_context* duk)
|
||||
{
|
||||
s32 pt[6];
|
||||
|
||||
for(s32 i = 0; i < COUNT_OF(pt); i++)
|
||||
pt[i] = duk_to_int(duk, i);
|
||||
|
||||
s32 color = duk_to_int(duk, 6);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
memory->api.tri(memory, pt[0], pt[1], pt[2], pt[3], pt[4], pt[5], color);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_clip(duk_context* duk)
|
||||
{
|
||||
s32 x = duk_to_int(duk, 0);
|
||||
s32 y = duk_to_int(duk, 1);
|
||||
s32 w = duk_is_null_or_undefined(duk, 2) ? TIC80_WIDTH : duk_to_int(duk, 2);
|
||||
s32 h = duk_is_null_or_undefined(duk, 3) ? TIC80_HEIGHT : duk_to_int(duk, 3);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
memory->api.clip(memory, x, y, w, h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_music(duk_context* duk)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
s32 track = duk_is_null_or_undefined(duk, 0) ? -1 : duk_to_int(duk, 0);
|
||||
memory->api.music(memory, -1, 0, 0, false);
|
||||
|
||||
if(track >= 0)
|
||||
{
|
||||
s32 frame = duk_is_null_or_undefined(duk, 1) ? -1 : duk_to_int(duk, 1);
|
||||
s32 row = duk_is_null_or_undefined(duk, 2) ? -1 : duk_to_int(duk, 2);
|
||||
bool loop = duk_is_null_or_undefined(duk, 3) ? true : duk_to_boolean(duk, 3);
|
||||
|
||||
memory->api.music(memory, track, frame, row, loop);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_sync(duk_context* duk)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
memory->api.sync(memory, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_ret_t duk_dofile(duk_context* duk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* const ApiKeywords[] = API_KEYWORDS;
|
||||
static const struct{duk_c_function func; s32 params;} ApiFunc[] =
|
||||
{
|
||||
{NULL, 0},
|
||||
{NULL, 1},
|
||||
{duk_print, 6},
|
||||
{duk_cls, 1},
|
||||
{duk_pix, 3},
|
||||
{duk_line, 5},
|
||||
{duk_rect, 5},
|
||||
{duk_rectb, 5},
|
||||
{duk_spr, 9},
|
||||
{duk_btn, 1},
|
||||
{duk_btnp, 3},
|
||||
{duk_sfx, 6},
|
||||
{duk_map, 9},
|
||||
{duk_mget, 2},
|
||||
{duk_mset, 3},
|
||||
{duk_peek, 1},
|
||||
{duk_poke, 2},
|
||||
{duk_peek4, 1},
|
||||
{duk_poke4, 2},
|
||||
{duk_memcpy, 3},
|
||||
{duk_memset, 3},
|
||||
{duk_trace, 2},
|
||||
{duk_pmem, 2},
|
||||
{duk_time, 0},
|
||||
{duk_exit, 0},
|
||||
{duk_font, 8},
|
||||
{duk_mouse, 0},
|
||||
{duk_circ, 4},
|
||||
{duk_circb, 4},
|
||||
{duk_tri, 7},
|
||||
{duk_clip, 4},
|
||||
{duk_music, 4},
|
||||
{duk_sync, 0},
|
||||
};
|
||||
|
||||
static void initDuktape(tic_machine* machine)
|
||||
{
|
||||
closeJavascript(machine);
|
||||
|
||||
duk_context* duk = machine->js = duk_create_heap_default();
|
||||
|
||||
{
|
||||
duk_push_global_stash(duk);
|
||||
duk_push_pointer(duk, machine);
|
||||
duk_put_prop_string(duk, -2, TicMachine);
|
||||
duk_pop(duk);
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < COUNT_OF(ApiFunc); i++)
|
||||
if (ApiFunc[i].func)
|
||||
{
|
||||
duk_push_c_function(machine->js, ApiFunc[i].func, ApiFunc[i].params);
|
||||
duk_put_global_string(machine->js, ApiKeywords[i]);
|
||||
}
|
||||
|
||||
{
|
||||
duk_push_c_function(machine->js, duk_dofile, 1);
|
||||
duk_put_global_string(machine->js, "dofile");
|
||||
}
|
||||
}
|
||||
|
||||
bool initJavascript(tic_machine* machine, const char* code)
|
||||
{
|
||||
initDuktape(machine);
|
||||
duk_context* duktape = machine->js;
|
||||
|
||||
if (duk_pcompile_string(duktape, 0, code) != 0 || duk_peval_string(duktape, code) != 0)
|
||||
{
|
||||
machine->data->error(machine->data->data, duk_safe_to_string(duktape, -1));
|
||||
duk_pop(duktape);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void callJavascriptTick(tic_machine* machine)
|
||||
{
|
||||
const char* TicFunc = ApiKeywords[0];
|
||||
|
||||
duk_context* duk = machine->js;
|
||||
|
||||
if(duk)
|
||||
{
|
||||
if(duk_get_global_string(duk, TicFunc))
|
||||
{
|
||||
if(duk_pcall(duk, 0) != 0)
|
||||
{
|
||||
machine->data->error(machine->data->data, duk_safe_to_string(duk, -1));
|
||||
duk_pop(duk);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
machine->data->error(machine->data->data, "'function TIC()...' isn't found :(");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void callJavascriptScanline(tic_mem* memory, s32 row)
|
||||
{
|
||||
tic_machine* machine = (tic_machine*)memory;
|
||||
duk_context* duk = machine->js;
|
||||
|
||||
const char* ScanlineFunc = ApiKeywords[1];
|
||||
|
||||
if(duk_get_global_string(duk, ScanlineFunc))
|
||||
{
|
||||
duk_push_int(duk, row);
|
||||
|
||||
if(duk_pcall(duk, 1) != 0)
|
||||
{
|
||||
machine->data->error(machine->data->data, duk_safe_to_string(duk, -1));
|
||||
duk_pop(duk);
|
||||
}
|
||||
else duk_pop(duk);
|
||||
}
|
||||
else duk_pop(duk);
|
||||
}
|
||||
178
src/keymap.c
Normal file
178
src/keymap.c
Normal file
@@ -0,0 +1,178 @@
|
||||
// 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 "keymap.h"
|
||||
#include "fs.h"
|
||||
|
||||
#define BUTTONS_COUNT BITS_IN_BYTE
|
||||
|
||||
static const char* ButtonNames[] = {"UP", "DOWN", "LEFT", "RIGHT", "A", "B", "X", "Y"};
|
||||
|
||||
static void saveMapping(Keymap* keymap)
|
||||
{
|
||||
fsSaveRootFile(keymap->fs, KEYMAP_DAT_PATH, getKeymap(), KEYMAP_SIZE, true);
|
||||
}
|
||||
|
||||
static void processKeydown(Keymap* keymap, SDL_Keysym* keysum)
|
||||
{
|
||||
SDL_Scancode scancode = keysum->scancode;
|
||||
|
||||
switch(scancode)
|
||||
{
|
||||
case SDL_SCANCODE_ESCAPE: break;
|
||||
default:
|
||||
if(keymap->button >= 0)
|
||||
{
|
||||
SDL_Scancode* codes = getKeymap();
|
||||
codes[keymap->button] = scancode;
|
||||
keymap->button = -1;
|
||||
|
||||
saveMapping(keymap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawPlayer(Keymap* keymap, s32 x, s32 y, s32 id)
|
||||
{
|
||||
{
|
||||
char label[] = "PLAYER #%i";
|
||||
sprintf(label, label, id+1);
|
||||
keymap->tic->api.text(keymap->tic, label, x, y, systemColor(tic_color_white));
|
||||
}
|
||||
|
||||
|
||||
enum{OffsetX = 32, OffsetY = 16, Height = TIC_FONT_HEIGHT+1, Buttons = BUTTONS_COUNT};
|
||||
|
||||
y += OffsetY;
|
||||
|
||||
SDL_Scancode* codes = getKeymap();
|
||||
|
||||
for(s32 i = 0; i < COUNT_OF(ButtonNames); i++)
|
||||
{
|
||||
SDL_Rect rect = {x, y + i * Height, (TIC80_WIDTH-OffsetX)/2, Height};
|
||||
|
||||
bool over = false;
|
||||
if(checkMousePos(&rect))
|
||||
{
|
||||
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
over = true;
|
||||
|
||||
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||||
keymap->button = id * Buttons + i;
|
||||
}
|
||||
|
||||
s32 button = id * Buttons + i;
|
||||
bool selected = keymap->button == button;
|
||||
|
||||
if(over)
|
||||
keymap->tic->api.rect(keymap->tic, rect.x-1, rect.y-1, rect.w, rect.h, systemColor(tic_color_dark_red));
|
||||
|
||||
if(selected)
|
||||
keymap->tic->api.rect(keymap->tic, rect.x-1, rect.y-1, rect.w, rect.h, systemColor(tic_color_white));
|
||||
|
||||
keymap->tic->api.text(keymap->tic, ButtonNames[i], rect.x, rect.y, selected ? systemColor(tic_color_black) : systemColor(tic_color_gray));
|
||||
|
||||
keymap->tic->api.text(keymap->tic, SDL_GetKeyName(SDL_GetKeyFromScancode(codes[button])), rect.x + OffsetX, rect.y, selected ? systemColor(tic_color_black) : systemColor(tic_color_white));
|
||||
}
|
||||
}
|
||||
|
||||
static void drawCenterText(Keymap* keymap, const char* text, s32 y, u8 color)
|
||||
{
|
||||
keymap->tic->api.fixed_text(keymap->tic, text, (TIC80_WIDTH - (s32)strlen(text) * TIC_FONT_WIDTH)/2, y, color);
|
||||
}
|
||||
|
||||
static void drawKeymap(Keymap* keymap)
|
||||
{
|
||||
keymap->tic->api.rect(keymap->tic, 0, 0, TIC80_WIDTH, TIC_FONT_HEIGHT * 3, systemColor(tic_color_white));
|
||||
|
||||
{
|
||||
static const char Label[] = "CONFIGURE BUTTONS MAPPING";
|
||||
keymap->tic->api.text(keymap->tic, Label, (TIC80_WIDTH - sizeof Label * TIC_FONT_WIDTH)/2, TIC_FONT_HEIGHT, systemColor(tic_color_black));
|
||||
}
|
||||
|
||||
drawPlayer(keymap, 16, 40, 0);
|
||||
drawPlayer(keymap, 120+16, 40, 1);
|
||||
|
||||
if(keymap->button < 0)
|
||||
{
|
||||
if(keymap->ticks % TIC_FRAMERATE < TIC_FRAMERATE/2)
|
||||
drawCenterText(keymap, "SELECT BUTTON", 120, systemColor(tic_color_white));
|
||||
}
|
||||
else
|
||||
{
|
||||
char label[256];
|
||||
sprintf(label, "PRESS A KEY FOR '%s'", ButtonNames[keymap->button % BUTTONS_COUNT]);
|
||||
drawCenterText(keymap, label, 120, systemColor(tic_color_white));
|
||||
drawCenterText(keymap, "ESC TO CANCEL", 126, systemColor(tic_color_white));
|
||||
}
|
||||
}
|
||||
|
||||
static void escape(Keymap* keymap)
|
||||
{
|
||||
if(keymap->button < 0)
|
||||
setStudioMode(TIC_CONSOLE_MODE);
|
||||
else keymap->button = -1;
|
||||
}
|
||||
|
||||
static void tick(Keymap* keymap)
|
||||
{
|
||||
keymap->ticks++;
|
||||
|
||||
SDL_Event* event = NULL;
|
||||
while ((event = pollEvent()))
|
||||
{
|
||||
switch(event->type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
processKeydown(keymap, &event->key.keysym);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
keymap->tic->api.clear(keymap->tic, TIC_COLOR_BG);
|
||||
|
||||
drawKeymap(keymap);
|
||||
}
|
||||
|
||||
void initKeymap(Keymap* keymap, tic_mem* tic, FileSystem* fs)
|
||||
{
|
||||
*keymap = (Keymap)
|
||||
{
|
||||
.tic = tic,
|
||||
.fs = fs,
|
||||
.tick = tick,
|
||||
.escape = escape,
|
||||
.ticks = 0,
|
||||
.button = -1,
|
||||
};
|
||||
|
||||
s32 size = 0;
|
||||
char* data = (char*)fsLoadFile(fs, KEYMAP_DAT_PATH, &size);
|
||||
|
||||
if(data)
|
||||
{
|
||||
if(size == KEYMAP_SIZE)
|
||||
memcpy(getKeymap(), data, KEYMAP_SIZE);
|
||||
|
||||
SDL_free(data);
|
||||
}
|
||||
}
|
||||
41
src/keymap.h
Normal file
41
src/keymap.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Keymap Keymap;
|
||||
|
||||
struct Keymap
|
||||
{
|
||||
tic_mem* tic;
|
||||
struct FileSystem* fs;
|
||||
|
||||
s32 ticks;
|
||||
s32 button;
|
||||
|
||||
void(*tick)(Keymap* keymap);
|
||||
void(*escape)(Keymap* keymap);
|
||||
};
|
||||
|
||||
void initKeymap(Keymap* keymap, tic_mem* tic, struct FileSystem* fs);
|
||||
1205
src/luaapi.c
Normal file
1205
src/luaapi.c
Normal file
File diff suppressed because it is too large
Load Diff
136
src/machine.h
Normal file
136
src/machine.h
Normal file
@@ -0,0 +1,136 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ticapi.h"
|
||||
#include "tools.h"
|
||||
#include "ext/blip_buf.h"
|
||||
|
||||
#define SFX_DEF_SPEED (1 << SFX_SPEED_BITS)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s32 time; /* clock time of next delta */
|
||||
s32 phase; /* position within waveform */
|
||||
s32 amp; /* current amplitude in delta buffer */
|
||||
}tic_sound_register_data;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s32 tick;
|
||||
tic_sfx_pos pos;
|
||||
s32 index;
|
||||
u16 freq;
|
||||
u8 volume:4;
|
||||
s8 speed:SFX_SPEED_BITS;
|
||||
s32 duration;
|
||||
} Channel;
|
||||
|
||||
typedef void(ScanlineFunc)(tic_mem* memory, s32 row);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s32 l;
|
||||
s32 t;
|
||||
s32 r;
|
||||
s32 b;
|
||||
} Clip;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
struct
|
||||
{
|
||||
tic80_input previous;
|
||||
|
||||
u32 holds[sizeof(tic80_input) * BITS_IN_BYTE];
|
||||
} gamepad;
|
||||
|
||||
Clip clip;
|
||||
|
||||
tic_sound_register_data registers[TIC_SOUND_CHANNELS];
|
||||
Channel channels[TIC_SOUND_CHANNELS];
|
||||
struct
|
||||
{
|
||||
enum
|
||||
{
|
||||
MusicStop = 0,
|
||||
MusicPlayFrame,
|
||||
MusicPlay,
|
||||
} play;
|
||||
|
||||
s32 ticks;
|
||||
|
||||
Channel channels[TIC_SOUND_CHANNELS];
|
||||
} music;
|
||||
|
||||
ScanlineFunc* scanline;
|
||||
bool initialized;
|
||||
} MachineState;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_mem memory; // it should be first
|
||||
tic_api api;
|
||||
|
||||
struct
|
||||
{
|
||||
struct duk_hthread* js;
|
||||
struct lua_State* lua;
|
||||
};
|
||||
|
||||
blip_buffer_t* blip;
|
||||
s32 samplerate;
|
||||
const tic_sound* soundSrc;
|
||||
|
||||
tic_tick_data* data;
|
||||
|
||||
MachineState state;
|
||||
|
||||
struct
|
||||
{
|
||||
MachineState state;
|
||||
tic_sound_register registers[TIC_SOUND_CHANNELS];
|
||||
tic_music_pos music_pos;
|
||||
tic_vram vram;
|
||||
} pause;
|
||||
|
||||
} tic_machine;
|
||||
|
||||
typedef s32(DrawCharFunc)(tic_mem* memory, u8 symbol, s32 x, s32 y, s32 width, s32 height, u8 color, s32 scale);
|
||||
s32 drawText(tic_mem* memory, const char* text, s32 x, s32 y, s32 width, s32 height, u8 color, s32 scale, DrawCharFunc* func);
|
||||
s32 drawSpriteFont(tic_mem* memory, u8 symbol, s32 x, s32 y, s32 width, s32 height, u8 chromakey, s32 scale);
|
||||
s32 drawFixedSpriteFont(tic_mem* memory, u8 index, s32 x, s32 y, s32 width, s32 height, u8 chromakey, s32 scale);
|
||||
|
||||
void closeLua(tic_machine* machine);
|
||||
void closeJavascript(tic_machine* machine);
|
||||
|
||||
bool initMoonscript(tic_machine* machine, const char* code);
|
||||
bool initLua(tic_machine* machine, const char* code);
|
||||
bool initJavascript(tic_machine* machine, const char* code);
|
||||
|
||||
void callLuaTick(tic_machine* machine);
|
||||
void callJavascriptTick(tic_machine* machine);
|
||||
|
||||
void callLuaScanline(tic_mem* memory, s32 row);
|
||||
void callJavascriptScanline(tic_mem* memory, s32 row);
|
||||
85
src/map.h
Normal file
85
src/map.h
Normal file
@@ -0,0 +1,85 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Map Map;
|
||||
|
||||
struct Map
|
||||
{
|
||||
tic_mem* tic;
|
||||
|
||||
s32 tickCounter;
|
||||
|
||||
enum
|
||||
{
|
||||
MAP_DRAW_MODE = 0,
|
||||
MAP_DRAG_MODE,
|
||||
MAP_SELECT_MODE,
|
||||
MAP_FILL_MODE,
|
||||
} mode;
|
||||
|
||||
struct
|
||||
{
|
||||
bool grid;
|
||||
bool draw;
|
||||
SDL_Point start;
|
||||
} canvas;
|
||||
|
||||
struct
|
||||
{
|
||||
bool show;
|
||||
SDL_Rect rect;
|
||||
SDL_Point start;
|
||||
bool drag;
|
||||
} sheet;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 x;
|
||||
s32 y;
|
||||
|
||||
SDL_Point start;
|
||||
|
||||
bool active;
|
||||
bool gesture;
|
||||
|
||||
} scroll;
|
||||
|
||||
struct
|
||||
{
|
||||
SDL_Rect rect;
|
||||
SDL_Point start;
|
||||
bool drag;
|
||||
} select;
|
||||
|
||||
u8* paste;
|
||||
|
||||
struct History* history;
|
||||
|
||||
void(*tick)(Map*);
|
||||
void(*event)(Map*, StudioEvent);
|
||||
};
|
||||
|
||||
void initMap(Map*, tic_mem*);
|
||||
538
src/menu.c
Normal file
538
src/menu.c
Normal file
@@ -0,0 +1,538 @@
|
||||
// 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 "menu.h"
|
||||
#include "fs.h"
|
||||
|
||||
#define DIALOG_WIDTH (TIC80_WIDTH/2)
|
||||
#define DIALOG_HEIGHT (TIC80_HEIGHT/2-TOOLBAR_SIZE-1)
|
||||
|
||||
static const char* Rows[] =
|
||||
{
|
||||
"RESUME GAME",
|
||||
"RESET GAME",
|
||||
"GAMEPAD CONFIG",
|
||||
"",
|
||||
"EXIT TO TIC-80",
|
||||
};
|
||||
|
||||
static void resumeGame(Menu* menu)
|
||||
{
|
||||
hideGameMenu();
|
||||
}
|
||||
|
||||
static void resetGame(Menu* menu)
|
||||
{
|
||||
tic_mem* tic = menu->tic;
|
||||
|
||||
tic->api.reset(tic);
|
||||
|
||||
setStudioMode(TIC_RUN_MODE);
|
||||
}
|
||||
|
||||
static void gamepadConfig(Menu* menu)
|
||||
{
|
||||
playSystemSfx(2);
|
||||
menu->mode = GAMEPAD_MENU_MODE;
|
||||
menu->gamepad.tab = 0;
|
||||
}
|
||||
|
||||
static void exitToTIC(Menu* menu)
|
||||
{
|
||||
exitFromGameMenu();
|
||||
}
|
||||
|
||||
static void(*const MenuHandlers[])(Menu*) = {resumeGame, resetGame, gamepadConfig, NULL, exitToTIC};
|
||||
|
||||
static SDL_Rect getRect(Menu* menu)
|
||||
{
|
||||
SDL_Rect rect = {(TIC80_WIDTH - DIALOG_WIDTH)/2, (TIC80_HEIGHT - DIALOG_HEIGHT)/2, DIALOG_WIDTH, DIALOG_HEIGHT};
|
||||
|
||||
rect.x -= menu->pos.x;
|
||||
rect.y -= menu->pos.y;
|
||||
|
||||
return rect;
|
||||
}
|
||||
static void drawDialog(Menu* menu)
|
||||
{
|
||||
SDL_Rect rect = getRect(menu);
|
||||
|
||||
tic_mem* tic = menu->tic;
|
||||
|
||||
SDL_Rect header = {rect.x, rect.y-(TOOLBAR_SIZE-2), rect.w, TOOLBAR_SIZE-1};
|
||||
|
||||
if(checkMousePos(&header))
|
||||
{
|
||||
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
|
||||
if(checkMouseDown(&header, SDL_BUTTON_LEFT))
|
||||
{
|
||||
if(!menu->drag.active)
|
||||
{
|
||||
menu->drag.start.x = getMouseX() + menu->pos.x;
|
||||
menu->drag.start.y = getMouseY() + menu->pos.y;
|
||||
|
||||
menu->drag.active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(menu->drag.active)
|
||||
{
|
||||
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
|
||||
menu->pos.x = menu->drag.start.x - getMouseX();
|
||||
menu->pos.y = menu->drag.start.y - getMouseY();
|
||||
|
||||
SDL_Rect rect = {0, 0, TIC80_WIDTH, TIC80_HEIGHT};
|
||||
if(!checkMouseDown(&rect, SDL_BUTTON_LEFT))
|
||||
menu->drag.active = false;
|
||||
}
|
||||
|
||||
rect = getRect(menu);
|
||||
|
||||
tic->api.rect(tic, rect.x, rect.y, rect.w, rect.h, systemColor(tic_color_blue));
|
||||
tic->api.rect_border(tic, rect.x, rect.y, rect.w, rect.h, systemColor(tic_color_white));
|
||||
tic->api.line(tic, rect.x, rect.y+DIALOG_HEIGHT, rect.x+DIALOG_WIDTH-1, rect.y+DIALOG_HEIGHT, systemColor(tic_color_black));
|
||||
tic->api.rect(tic, rect.x, rect.y-(TOOLBAR_SIZE-3), rect.w, TOOLBAR_SIZE-3, systemColor(tic_color_white));
|
||||
tic->api.line(tic, rect.x+1, rect.y-(TOOLBAR_SIZE-2), rect.x+DIALOG_WIDTH-2, rect.y-(TOOLBAR_SIZE-2), systemColor(tic_color_white));
|
||||
|
||||
{
|
||||
static const char Label[] = "GAME MENU";
|
||||
s32 size = tic->api.text(tic, Label, 0, -TIC_FONT_HEIGHT, 0);
|
||||
tic->api.text(tic, Label, rect.x + (DIALOG_WIDTH - size)/2, rect.y-(TOOLBAR_SIZE-3), systemColor(tic_color_gray));
|
||||
}
|
||||
|
||||
{
|
||||
u8 chromakey = 14;
|
||||
tic->api.sprite_ex(tic, &tic->config.gfx, 0, rect.x+6, rect.y-4, 2, 2, &chromakey, 1, 1, tic_no_flip, tic_no_rotate);
|
||||
}
|
||||
}
|
||||
|
||||
static void drawTabDisabled(Menu* menu, s32 x, s32 y, s32 id)
|
||||
{
|
||||
enum{Width = 15, Height = 7};
|
||||
tic_mem* tic = menu->tic;
|
||||
|
||||
SDL_Rect rect = {x, y, Width, Height};
|
||||
bool over = false;
|
||||
|
||||
if(menu->gamepad.tab != id && checkMousePos(&rect))
|
||||
{
|
||||
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
over = true;
|
||||
|
||||
if(checkMouseDown(&rect, SDL_BUTTON_LEFT))
|
||||
{
|
||||
menu->gamepad.tab = id;
|
||||
menu->gamepad.selected = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tic->api.rect(tic, x, y-1, Width, Height+1, systemColor(tic_color_dark_gray));
|
||||
tic->api.pixel(tic, x, y+Height-1, systemColor(tic_color_blue));
|
||||
tic->api.pixel(tic, x+Width-1, y+Height-1, systemColor(tic_color_blue));
|
||||
|
||||
{
|
||||
char buf[] = "#1";
|
||||
sprintf(buf, "#%i", id+1);
|
||||
tic->api.fixed_text(tic, buf, x+2, y, systemColor(over ? tic_color_light_blue : tic_color_gray));
|
||||
}
|
||||
}
|
||||
|
||||
static void drawTab(Menu* menu, s32 x, s32 y, s32 id)
|
||||
{
|
||||
enum{Width = 15, Height = 7};
|
||||
tic_mem* tic = menu->tic;
|
||||
|
||||
tic->api.rect(tic, x, y-2, Width, Height+2, systemColor(tic_color_white));
|
||||
tic->api.pixel(tic, x, y+Height-1, systemColor(tic_color_black));
|
||||
tic->api.pixel(tic, x+Width-1, y+Height-1, systemColor(tic_color_black));
|
||||
tic->api.rect(tic, x+1, y+Height, Width-2 , 1, systemColor(tic_color_black));
|
||||
|
||||
{
|
||||
char buf[] = "#1";
|
||||
sprintf(buf, "#%i", id+1);
|
||||
tic->api.fixed_text(tic, buf, x+2, y, systemColor(tic_color_gray));
|
||||
}
|
||||
}
|
||||
|
||||
static void drawPlayerButtons(Menu* menu, s32 x, s32 y)
|
||||
{
|
||||
tic_mem* tic = menu->tic;
|
||||
|
||||
u8 chromakey = 0;
|
||||
|
||||
SDL_Scancode* codes = getKeymap();
|
||||
|
||||
enum {Width = 41, Height = TIC_SPRITESIZE, Rows = 4, Cols = 2, MaxChars = 5, Buttons = 8};
|
||||
|
||||
for(s32 i = 0; i < Buttons; i++)
|
||||
{
|
||||
SDL_Rect rect = {x + i / Rows * (Width+2), y + (i%Rows)*(Height+1), Width, TIC_SPRITESIZE};
|
||||
bool over = false;
|
||||
|
||||
s32 index = i+menu->gamepad.tab * Buttons;
|
||||
|
||||
if(checkMousePos(&rect))
|
||||
{
|
||||
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
|
||||
over = true;
|
||||
|
||||
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||||
{
|
||||
menu->gamepad.selected = menu->gamepad.selected != index ? index : -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(menu->gamepad.selected == index && menu->ticks % TIC_FRAMERATE < TIC_FRAMERATE / 2)
|
||||
continue;
|
||||
|
||||
tic->api.sprite_ex(tic, &tic->config.gfx, 8+i, rect.x, rect.y, 1, 1, &chromakey, 1, 1, tic_no_flip, tic_no_rotate);
|
||||
|
||||
s32 code = codes[index];
|
||||
char label[32];
|
||||
strcpy(label, code ? SDL_GetKeyName(SDL_GetKeyFromScancode(code)) : "...");
|
||||
|
||||
if(strlen(label) > MaxChars)
|
||||
label[MaxChars] = '\0';
|
||||
|
||||
tic->api.text(tic, label, rect.x+10, rect.y+2, systemColor(over ? tic_color_gray : tic_color_black));
|
||||
}
|
||||
}
|
||||
|
||||
static void drawGamepadSetupTabs(Menu* menu, s32 x, s32 y)
|
||||
{
|
||||
enum{Width = 90, Height = 41, Tabs = 2};
|
||||
tic_mem* tic = menu->tic;
|
||||
|
||||
|
||||
tic->api.rect(tic, x, y, Width, Height, systemColor(tic_color_white));
|
||||
tic->api.pixel(tic, x, y, systemColor(tic_color_blue));
|
||||
tic->api.pixel(tic, x+Width-1, y, systemColor(tic_color_blue));
|
||||
tic->api.pixel(tic, x, y+Height-1, systemColor(tic_color_black));
|
||||
tic->api.pixel(tic, x+Width-1, y+Height-1, systemColor(tic_color_black));
|
||||
tic->api.rect(tic, x+1, y+Height, Width-2 , 1, systemColor(tic_color_black));
|
||||
|
||||
for(s32 i = 0; i < Tabs; i++)
|
||||
{
|
||||
if(menu->gamepad.tab == i)
|
||||
drawTab(menu, x + 73 - i*17, y + 43, i);
|
||||
else
|
||||
drawTabDisabled(menu, x + 73 - i*17, y + 43, i);
|
||||
}
|
||||
|
||||
drawPlayerButtons(menu, x + 3, y + 3);
|
||||
}
|
||||
|
||||
static void drawGamepadMenu(Menu* menu)
|
||||
{
|
||||
drawDialog(menu);
|
||||
|
||||
tic_mem* tic = menu->tic;
|
||||
|
||||
SDL_Rect dlgRect = getRect(menu);
|
||||
|
||||
static const char Label[] = "BACK";
|
||||
|
||||
SDL_Rect rect = {dlgRect.x + 25, dlgRect.y + 49, (sizeof(Label)-1)*TIC_FONT_WIDTH, TIC_FONT_HEIGHT};
|
||||
|
||||
bool over = false;
|
||||
bool down = false;
|
||||
|
||||
if(checkMousePos(&rect))
|
||||
{
|
||||
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
|
||||
over = true;
|
||||
|
||||
if(checkMouseDown(&rect, SDL_BUTTON_LEFT))
|
||||
{
|
||||
down = true;
|
||||
}
|
||||
|
||||
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||||
{
|
||||
menu->gamepad.selected = -1;
|
||||
menu->mode = MAIN_MENU_MODE;
|
||||
playSystemSfx(2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(down)
|
||||
{
|
||||
tic->api.text(tic, Label, rect.x, rect.y+1, systemColor(tic_color_light_blue));
|
||||
}
|
||||
else
|
||||
{
|
||||
tic->api.text(tic, Label, rect.x, rect.y+1, systemColor(tic_color_black));
|
||||
tic->api.text(tic, Label, rect.x, rect.y, systemColor(over ? tic_color_light_blue : tic_color_white));
|
||||
}
|
||||
|
||||
{
|
||||
static const u8 Icon[] =
|
||||
{
|
||||
0b10000000,
|
||||
0b11000000,
|
||||
0b11100000,
|
||||
0b11000000,
|
||||
0b10000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
};
|
||||
|
||||
drawBitIcon(rect.x-7, rect.y+1, Icon, systemColor(tic_color_black));
|
||||
drawBitIcon(rect.x-7, rect.y, Icon, systemColor(tic_color_white));
|
||||
}
|
||||
|
||||
drawGamepadSetupTabs(menu, dlgRect.x+25, dlgRect.y+4);
|
||||
}
|
||||
|
||||
static void drawMainMenu(Menu* menu)
|
||||
{
|
||||
tic_mem* tic = menu->tic;
|
||||
|
||||
drawDialog(menu);
|
||||
|
||||
SDL_Rect rect = getRect(menu);
|
||||
|
||||
{
|
||||
for(s32 i = 0; i < COUNT_OF(Rows); i++)
|
||||
{
|
||||
if(!*Rows[i])continue;
|
||||
|
||||
SDL_Rect label = {rect.x + 22, rect.y + (TIC_FONT_HEIGHT+1)*i + 16, 86, TIC_FONT_HEIGHT+1};
|
||||
bool over = false;
|
||||
bool down = false;
|
||||
|
||||
if(checkMousePos(&label))
|
||||
{
|
||||
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
|
||||
over = true;
|
||||
|
||||
if(checkMouseDown(&label, SDL_BUTTON_LEFT))
|
||||
{
|
||||
down = true;
|
||||
menu->main.focus = i;
|
||||
}
|
||||
|
||||
if(checkMouseClick(&label, SDL_BUTTON_LEFT))
|
||||
{
|
||||
MenuHandlers[i](menu);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(down)
|
||||
{
|
||||
tic->api.text(tic, Rows[i], label.x, label.y+1, systemColor(tic_color_light_blue));
|
||||
}
|
||||
else
|
||||
{
|
||||
tic->api.text(tic, Rows[i], label.x, label.y+1, systemColor(tic_color_black));
|
||||
tic->api.text(tic, Rows[i], label.x, label.y, systemColor(over ? tic_color_light_blue : tic_color_white));
|
||||
}
|
||||
|
||||
if(i == menu->main.focus)
|
||||
{
|
||||
static const u8 Icon[] =
|
||||
{
|
||||
0b10000000,
|
||||
0b11000000,
|
||||
0b11100000,
|
||||
0b11000000,
|
||||
0b10000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
};
|
||||
|
||||
drawBitIcon(label.x-7, label.y+1, Icon, systemColor(tic_color_black));
|
||||
drawBitIcon(label.x-7, label.y, Icon, systemColor(tic_color_white));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void processGamedMenuGamepad(Menu* menu)
|
||||
{
|
||||
if(menu->gamepad.selected < 0)
|
||||
{
|
||||
enum
|
||||
{
|
||||
Up, Down, Left, Right, A, B, X, Y
|
||||
};
|
||||
|
||||
if(menu->tic->api.btnp(menu->tic, A, -1, -1))
|
||||
{
|
||||
menu->mode = MAIN_MENU_MODE;
|
||||
playSystemSfx(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void processMainMenuGamepad(Menu* menu)
|
||||
{
|
||||
enum{Count = COUNT_OF(Rows), Hold = 30, Period = 5};
|
||||
|
||||
enum
|
||||
{
|
||||
Up, Down, Left, Right, A, B, X, Y
|
||||
};
|
||||
|
||||
if(menu->tic->api.btnp(menu->tic, Up, Hold, Period))
|
||||
{
|
||||
do
|
||||
{
|
||||
if(--menu->main.focus < 0)
|
||||
menu->main.focus = Count - 1;
|
||||
|
||||
menu->main.focus = menu->main.focus % Count;
|
||||
} while(!*Rows[menu->main.focus]);
|
||||
|
||||
playSystemSfx(2);
|
||||
}
|
||||
|
||||
if(menu->tic->api.btnp(menu->tic, Down, Hold, Period))
|
||||
{
|
||||
do
|
||||
{
|
||||
menu->main.focus = (menu->main.focus+1) % Count;
|
||||
} while(!*Rows[menu->main.focus]);
|
||||
|
||||
playSystemSfx(2);
|
||||
}
|
||||
|
||||
if(menu->tic->api.btnp(menu->tic, A, -1, -1))
|
||||
{
|
||||
MenuHandlers[menu->main.focus](menu);
|
||||
}
|
||||
}
|
||||
|
||||
static void saveMapping(Menu* menu)
|
||||
{
|
||||
fsSaveRootFile(menu->fs, KEYMAP_DAT_PATH, getKeymap(), KEYMAP_SIZE, true);
|
||||
}
|
||||
|
||||
static void processKeydown(Menu* menu, SDL_Keysym* keysum)
|
||||
{
|
||||
if(menu->gamepad.selected < 0)
|
||||
return;
|
||||
|
||||
SDL_Scancode scancode = keysum->scancode;
|
||||
|
||||
switch(scancode)
|
||||
{
|
||||
case SDL_SCANCODE_ESCAPE: break;
|
||||
default:
|
||||
{
|
||||
SDL_Scancode* codes = getKeymap();
|
||||
codes[menu->gamepad.selected] = scancode;
|
||||
|
||||
saveMapping(menu);
|
||||
}
|
||||
}
|
||||
|
||||
menu->gamepad.selected = -1;
|
||||
}
|
||||
|
||||
static void tick(Menu* menu)
|
||||
{
|
||||
menu->ticks++;
|
||||
|
||||
SDL_Event* event = NULL;
|
||||
while ((event = pollEvent()))
|
||||
{
|
||||
switch(event->type)
|
||||
{
|
||||
case SDL_KEYUP:
|
||||
processKeydown(menu, &event->key.keysym);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(getStudioMode() != TIC_MENU_MODE)
|
||||
return;
|
||||
|
||||
if(!menu->init)
|
||||
{
|
||||
playSystemSfx(0);
|
||||
|
||||
menu->init = true;
|
||||
}
|
||||
|
||||
SDL_memcpy(menu->tic->ram.vram.screen.data, menu->bg, sizeof menu->tic->ram.vram.screen.data);
|
||||
|
||||
switch(menu->mode)
|
||||
{
|
||||
case MAIN_MENU_MODE:
|
||||
processMainMenuGamepad(menu);
|
||||
drawMainMenu(menu);
|
||||
break;
|
||||
case GAMEPAD_MENU_MODE:
|
||||
processGamedMenuGamepad(menu);
|
||||
drawGamepadMenu(menu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void initMenu(Menu* menu, tic_mem* tic, FileSystem* fs)
|
||||
{
|
||||
*menu = (Menu)
|
||||
{
|
||||
.init = false,
|
||||
.fs = fs,
|
||||
.tic = tic,
|
||||
.tick = tick,
|
||||
.bg = menu->bg,
|
||||
.ticks = 0,
|
||||
.pos = {0, 0},
|
||||
.main =
|
||||
{
|
||||
.focus = 0,
|
||||
},
|
||||
.gamepad =
|
||||
{
|
||||
.tab = 0,
|
||||
.selected = -1,
|
||||
},
|
||||
.drag =
|
||||
{
|
||||
.start = {0, 0},
|
||||
.active = 0,
|
||||
},
|
||||
.mode = MAIN_MENU_MODE,
|
||||
};
|
||||
|
||||
enum{Size = sizeof tic->ram.vram.screen.data};
|
||||
|
||||
if(!menu->bg)
|
||||
menu->bg = SDL_malloc(Size);
|
||||
|
||||
if(menu->bg)
|
||||
SDL_memcpy(menu->bg, tic->ram.vram.screen.data, Size);
|
||||
}
|
||||
66
src/menu.h
Normal file
66
src/menu.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Menu Menu;
|
||||
|
||||
struct Menu
|
||||
{
|
||||
tic_mem* tic;
|
||||
struct FileSystem* fs;
|
||||
|
||||
bool init;
|
||||
void* bg;
|
||||
s32 ticks;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 focus;
|
||||
} main;
|
||||
|
||||
struct
|
||||
{
|
||||
u32 tab;
|
||||
s32 selected;
|
||||
} gamepad;
|
||||
|
||||
SDL_Point pos;
|
||||
|
||||
struct
|
||||
{
|
||||
SDL_Point start;
|
||||
bool active;
|
||||
} drag;
|
||||
|
||||
enum
|
||||
{
|
||||
MAIN_MENU_MODE,
|
||||
GAMEPAD_MENU_MODE,
|
||||
} mode;
|
||||
|
||||
void(*tick)(Menu* Menu);
|
||||
};
|
||||
|
||||
void initMenu(Menu* menu, tic_mem* tic, struct FileSystem* fs);
|
||||
1392
src/music.c
Normal file
1392
src/music.c
Normal file
File diff suppressed because it is too large
Load Diff
66
src/music.h
Normal file
66
src/music.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Music Music;
|
||||
|
||||
struct Music
|
||||
{
|
||||
tic_mem* tic;
|
||||
u8 track:MUSIC_TRACKS_BITS;
|
||||
|
||||
struct
|
||||
{
|
||||
bool follow;
|
||||
s32 patternCol;
|
||||
|
||||
s32 frame;
|
||||
s32 col;
|
||||
s32 row;
|
||||
s32 scroll;
|
||||
s32 note;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 octave;
|
||||
s32 sfx;
|
||||
s32 volume;
|
||||
} last;
|
||||
|
||||
} tracker;
|
||||
|
||||
enum
|
||||
{
|
||||
MUSIC_TRACKER_TAB,
|
||||
MUSIC_PIANO_TAB,
|
||||
} tab;
|
||||
|
||||
struct History* history;
|
||||
|
||||
void(*tick)(Music*);
|
||||
void(*event)(Music*, StudioEvent);
|
||||
};
|
||||
|
||||
void initMusic(Music*, tic_mem*);
|
||||
374
src/net.c
Normal file
374
src/net.c
Normal file
@@ -0,0 +1,374 @@
|
||||
// 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 "net.h"
|
||||
#include "tic.h"
|
||||
#include "ext/net/SDL_net.h"
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
struct Net
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8* buffer;
|
||||
s32 size;
|
||||
char path[FILENAME_MAX];
|
||||
} cache;
|
||||
};
|
||||
|
||||
|
||||
typedef void(*NetResponse)(u8* buffer, s32 size, void* data);
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
|
||||
static void getRequest(Net* net, const char* path, NetResponse callback, void* data)
|
||||
{
|
||||
callback(NULL, 0, data);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void netClearCache(Net* net)
|
||||
{
|
||||
if(net->cache.buffer)
|
||||
SDL_free(net->cache.buffer);
|
||||
|
||||
net->cache.buffer = NULL;
|
||||
net->cache.size = 0;
|
||||
memset(net->cache.path, 0, sizeof net->cache.path);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8* data;
|
||||
s32 size;
|
||||
}Buffer;
|
||||
|
||||
static Buffer httpRequest(const char* path, s32 timeout)
|
||||
{
|
||||
Buffer buffer = {.data = NULL, .size = 0};
|
||||
|
||||
{
|
||||
IPaddress ip;
|
||||
|
||||
if (SDLNet_ResolveHost(&ip, TIC_HOST, 80) >= 0)
|
||||
{
|
||||
TCPsocket sock = SDLNet_TCP_Open(&ip);
|
||||
|
||||
if (sock)
|
||||
{
|
||||
SDLNet_SocketSet set = SDLNet_AllocSocketSet(1);
|
||||
|
||||
if(set)
|
||||
{
|
||||
SDLNet_TCP_AddSocket(set, sock);
|
||||
|
||||
{
|
||||
char message[FILENAME_MAX];
|
||||
memset(message, 0, sizeof message);
|
||||
sprintf(message, "GET %s HTTP/1.0\r\nHost: " TIC_HOST "\r\n\r\n", path);
|
||||
SDLNet_TCP_Send(sock, message, (s32)strlen(message) + 1);
|
||||
}
|
||||
|
||||
if(SDLNet_CheckSockets(set, timeout) == 1 && SDLNet_SocketReady(sock))
|
||||
{
|
||||
enum {Size = 4*1024+1};
|
||||
buffer.data = SDL_malloc(Size);
|
||||
s32 size = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
size = SDLNet_TCP_Recv(sock, buffer.data + buffer.size, Size-1);
|
||||
|
||||
if(size > 0)
|
||||
{
|
||||
buffer.size += size;
|
||||
buffer.data = SDL_realloc(buffer.data, buffer.size + Size);
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
buffer.data[buffer.size] = '\0';
|
||||
}
|
||||
|
||||
SDLNet_FreeSocketSet(set);
|
||||
}
|
||||
|
||||
SDLNet_TCP_Close(sock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void getRequest(Net* net, const char* path, NetResponse callback, void* data)
|
||||
{
|
||||
if(strcmp(net->cache.path, path) == 0)
|
||||
{
|
||||
callback(net->cache.buffer, net->cache.size, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
netClearCache(net);
|
||||
|
||||
bool done = false;
|
||||
|
||||
enum {Timeout = 3000};
|
||||
Buffer buffer = httpRequest(path, Timeout);
|
||||
|
||||
if(buffer.data && buffer.size)
|
||||
{
|
||||
if(SDL_strstr((char*)buffer.data, "200 OK"))
|
||||
{
|
||||
s32 contentLength = 0;
|
||||
|
||||
{
|
||||
static const char ContentLength[] = "Content-Length:";
|
||||
|
||||
char* start = SDL_strstr((char*)buffer.data, ContentLength);
|
||||
|
||||
if(start)
|
||||
contentLength = SDL_atoi(start + sizeof(ContentLength));
|
||||
}
|
||||
|
||||
static const char Start[] = "\r\n\r\n";
|
||||
u8* start = (u8*)SDL_strstr((char*)buffer.data, Start);
|
||||
|
||||
if(start)
|
||||
{
|
||||
strcpy(net->cache.path, path);
|
||||
net->cache.size = contentLength ? contentLength : buffer.size - (s32)(start - buffer.data);
|
||||
net->cache.buffer = (u8*)SDL_malloc(net->cache.size);
|
||||
SDL_memcpy(net->cache.buffer, start + sizeof Start - 1, net->cache.size);
|
||||
callback(net->cache.buffer, net->cache.size, data);
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_free(buffer.data);
|
||||
}
|
||||
|
||||
if(!done)
|
||||
callback(NULL, 0, data);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static lua_State* netLuaInit(u8* buffer, s32 size)
|
||||
{
|
||||
if (buffer && size)
|
||||
{
|
||||
lua_State* lua = luaL_newstate();
|
||||
|
||||
if(lua)
|
||||
{
|
||||
if(luaL_loadstring(lua, (char*)buffer) == LUA_OK && lua_pcall(lua, 0, LUA_MULTRET, 0) == LUA_OK)
|
||||
return lua;
|
||||
|
||||
else lua_close(lua);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ListCallback callback;
|
||||
void* data;
|
||||
} NetDirData;
|
||||
|
||||
static void onDirResponse(u8* buffer, s32 size, void* data)
|
||||
{
|
||||
NetDirData* netDirData = (NetDirData*)data;
|
||||
|
||||
lua_State* lua = netLuaInit(buffer, size);
|
||||
|
||||
if(lua)
|
||||
{
|
||||
{
|
||||
lua_getglobal(lua, "folders");
|
||||
|
||||
if(lua_type(lua, -1) == LUA_TTABLE)
|
||||
{
|
||||
s32 count = (s32)lua_rawlen(lua, -1);
|
||||
|
||||
for(s32 i = 1; i <= count; i++)
|
||||
{
|
||||
lua_geti(lua, -1, i);
|
||||
|
||||
{
|
||||
lua_getfield(lua, -1, "name");
|
||||
if(lua_isstring(lua, -1))
|
||||
netDirData->callback(lua_tostring(lua, -1), NULL, 0, netDirData->data, true);
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
{
|
||||
lua_getglobal(lua, "files");
|
||||
|
||||
if(lua_type(lua, -1) == LUA_TTABLE)
|
||||
{
|
||||
s32 count = (s32)lua_rawlen(lua, -1);
|
||||
|
||||
for(s32 i = 1; i <= count; i++)
|
||||
{
|
||||
lua_geti(lua, -1, i);
|
||||
|
||||
char hash[FILENAME_MAX] = {0};
|
||||
char name[FILENAME_MAX] = {0};
|
||||
|
||||
{
|
||||
lua_getfield(lua, -1, "hash");
|
||||
if(lua_isstring(lua, -1))
|
||||
strcpy(hash, lua_tostring(lua, -1));
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
{
|
||||
lua_getfield(lua, -1, "name");
|
||||
|
||||
if(lua_isstring(lua, -1))
|
||||
strcpy(name, lua_tostring(lua, -1));
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
{
|
||||
lua_getfield(lua, -1, "id");
|
||||
|
||||
if(lua_isinteger(lua, -1))
|
||||
netDirData->callback(name, hash, lua_tointeger(lua, -1), netDirData->data, false);
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
lua_close(lua);
|
||||
}
|
||||
}
|
||||
|
||||
void netDirRequest(Net* net, const char* path, ListCallback callback, void* data)
|
||||
{
|
||||
char request[FILENAME_MAX] = {'\0'};
|
||||
sprintf(request, "/api?fn=dir&path=%s", path);
|
||||
|
||||
NetDirData netDirData = {callback, data};
|
||||
getRequest(net, request, onDirResponse, &netDirData);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void* buffer;
|
||||
s32* size;
|
||||
} NetGetData;
|
||||
|
||||
static void onGetResponse(u8* buffer, s32 size, void* data)
|
||||
{
|
||||
NetGetData* netGetData = (NetGetData*)data;
|
||||
|
||||
netGetData->buffer = SDL_malloc(size);
|
||||
*netGetData->size = size;
|
||||
SDL_memcpy(netGetData->buffer, buffer, size);
|
||||
}
|
||||
|
||||
void* netGetRequest(Net* net, const char* path, s32* size)
|
||||
{
|
||||
NetGetData netGetData = {NULL, size};
|
||||
getRequest(net, path, onGetResponse, &netGetData);
|
||||
|
||||
return netGetData.buffer;
|
||||
}
|
||||
|
||||
NetVersion netVersionRequest(Net* net)
|
||||
{
|
||||
NetVersion version =
|
||||
{
|
||||
.major = TIC_VERSION_MAJOR,
|
||||
.minor = TIC_VERSION_MINOR,
|
||||
.patch = TIC_VERSION_PATCH,
|
||||
};
|
||||
|
||||
s32 size = 0;
|
||||
void* buffer = netGetRequest(net, "/api?fn=version", &size);
|
||||
|
||||
if(buffer && size)
|
||||
{
|
||||
lua_State* lua = netLuaInit(buffer, size);
|
||||
|
||||
if(lua)
|
||||
{
|
||||
static const char* Fields[] = {"major", "minor", "patch"};
|
||||
|
||||
for(s32 i = 0; i < COUNT_OF(Fields); i++)
|
||||
{
|
||||
lua_getglobal(lua, Fields[i]);
|
||||
|
||||
if(lua_isinteger(lua, -1))
|
||||
((s32*)&version)[i] = (s32)lua_tointeger(lua, -1);
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
lua_close(lua);
|
||||
}
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
Net* createNet()
|
||||
{
|
||||
Net* net = (Net*)SDL_malloc(sizeof(Net));
|
||||
|
||||
*net = (Net)
|
||||
{
|
||||
.cache =
|
||||
{
|
||||
.buffer = NULL,
|
||||
.size = 0,
|
||||
.path = {0},
|
||||
},
|
||||
};
|
||||
|
||||
return net;
|
||||
}
|
||||
40
src/net.h
Normal file
40
src/net.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
typedef struct Net Net;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s32 major;
|
||||
s32 minor;
|
||||
s32 patch;
|
||||
} NetVersion;
|
||||
|
||||
NetVersion netVersionRequest(Net* net);
|
||||
void netDirRequest(Net* net, const char* path, ListCallback callback, void* data);
|
||||
void* netGetRequest(Net* net, const char* path, s32* size);
|
||||
|
||||
Net* createNet();
|
||||
154
src/run.c
Normal file
154
src/run.c
Normal file
@@ -0,0 +1,154 @@
|
||||
// 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 "run.h"
|
||||
#include "console.h"
|
||||
#include "fs.h"
|
||||
#include "ext/md5.h"
|
||||
|
||||
static void onTrace(void* data, const char* text, u8 color)
|
||||
{
|
||||
Run* run = (Run*)data;
|
||||
|
||||
run->console->trace(run->console, text, color);
|
||||
}
|
||||
|
||||
static void onError(void* data, const char* info)
|
||||
{
|
||||
Run* run = (Run*)data;
|
||||
|
||||
setStudioMode(TIC_CONSOLE_MODE);
|
||||
run->console->error(run->console, info);
|
||||
}
|
||||
|
||||
static void onExit(void* data)
|
||||
{
|
||||
Run* run = (Run*)data;
|
||||
|
||||
run->exit = true;
|
||||
}
|
||||
|
||||
static char* data2md5(const void* data, s32 length)
|
||||
{
|
||||
const char *str = data;
|
||||
MD5_CTX c;
|
||||
|
||||
static char out[33];
|
||||
|
||||
MD5_Init(&c);
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
MD5_Update(&c, str, length > 512 ? 512: length);
|
||||
|
||||
length -= 512;
|
||||
str += 512;
|
||||
}
|
||||
|
||||
{
|
||||
u8 digest[16];
|
||||
MD5_Final(digest, &c);
|
||||
|
||||
for (s32 n = 0; n < 16; ++n)
|
||||
snprintf(&(out[n*2]), 16*2, "%02x", (u32)digest[n]);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static const char* getPMemName(Run* run)
|
||||
{
|
||||
static char buffer[FILENAME_MAX];
|
||||
|
||||
const char* data = strlen(run->tic->saveid) ? run->tic->saveid : run->tic->cart.code.data;
|
||||
char* md5 = data2md5(data, (s32)strlen(data));
|
||||
strcpy(buffer, TIC_LOCAL);
|
||||
strcat(buffer, md5);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void tick(Run* run)
|
||||
{
|
||||
while(pollEvent());
|
||||
|
||||
if (getStudioMode() != TIC_RUN_MODE)
|
||||
return;
|
||||
|
||||
if(!run->init)
|
||||
{
|
||||
run->tickData.start = run->tickData.counter(),
|
||||
run->init = true;
|
||||
}
|
||||
|
||||
run->tic->api.tick(run->tic, &run->tickData);
|
||||
|
||||
enum {Size = sizeof(tic_persistent)};
|
||||
|
||||
if(SDL_memcmp(&run->tic->ram.persistent, run->persistent, Size) != 0)
|
||||
{
|
||||
fsSaveRootFile(run->console->fs, getPMemName(run), &run->tic->ram.persistent, Size, true);
|
||||
|
||||
SDL_memcpy(run->persistent, &run->tic->ram.persistent, Size);
|
||||
}
|
||||
|
||||
if(run->exit)
|
||||
setStudioMode(TIC_CONSOLE_MODE);
|
||||
}
|
||||
|
||||
void initRun(Run* run, Console* console, tic_mem* tic)
|
||||
{
|
||||
*run = (Run)
|
||||
{
|
||||
.tic = tic,
|
||||
.console = console,
|
||||
.tick = tick,
|
||||
.exit = false,
|
||||
.init = false,
|
||||
.tickData =
|
||||
{
|
||||
.error = onError,
|
||||
.trace = onTrace,
|
||||
.counter = SDL_GetPerformanceCounter,
|
||||
.freq = SDL_GetPerformanceFrequency,
|
||||
.start = 0,
|
||||
.data = run,
|
||||
.exit = onExit,
|
||||
},
|
||||
};
|
||||
|
||||
{
|
||||
enum {Size = sizeof(tic_persistent)};
|
||||
SDL_memset(&run->tic->ram.persistent, 0, Size);
|
||||
|
||||
s32 size = 0;
|
||||
void* data = fsLoadRootFile(run->console->fs, getPMemName(run), &size);
|
||||
|
||||
if(size == Size && data)
|
||||
{
|
||||
SDL_memcpy(&run->tic->ram.persistent, data, Size);
|
||||
SDL_memcpy(run->persistent, data, Size);
|
||||
}
|
||||
|
||||
if(data) SDL_free(data);
|
||||
}
|
||||
}
|
||||
43
src/run.h
Normal file
43
src/run.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Run Run;
|
||||
|
||||
struct Run
|
||||
{
|
||||
tic_mem* tic;
|
||||
struct Console* console;
|
||||
tic_tick_data tickData;
|
||||
|
||||
bool exit;
|
||||
bool init;
|
||||
|
||||
s32 persistent[TIC_PERSISTENT_SIZE];
|
||||
|
||||
void(*tick)(Run*);
|
||||
};
|
||||
|
||||
void initRun(Run*, struct Console*, tic_mem*);
|
||||
70
src/sfx.h
Normal file
70
src/sfx.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Sfx Sfx;
|
||||
|
||||
struct Sfx
|
||||
{
|
||||
tic_mem* tic;
|
||||
|
||||
u8 index:SFX_COUNT_BITS;
|
||||
|
||||
struct
|
||||
{
|
||||
bool active;
|
||||
s32 note;
|
||||
} play;
|
||||
|
||||
enum
|
||||
{
|
||||
SFX_WAVE_TAB = 0,
|
||||
SFX_VOLUME_TAB,
|
||||
SFX_ARPEGGIO_TAB,
|
||||
SFX_PITCH_TAB,
|
||||
}canvasTab;
|
||||
|
||||
struct
|
||||
{
|
||||
u8 index:4;
|
||||
} waveform;
|
||||
|
||||
enum
|
||||
{
|
||||
SFX_WAVEFORM_TAB,
|
||||
SFX_ENVELOPES_TAB,
|
||||
} tab;
|
||||
|
||||
struct
|
||||
{
|
||||
struct History* envelope;
|
||||
struct History* waveform;
|
||||
} history;
|
||||
|
||||
void(*tick)(Sfx*);
|
||||
void(*event)(Sfx*, StudioEvent);
|
||||
};
|
||||
|
||||
void initSfx(Sfx*, tic_mem*);
|
||||
1515
src/sprite.c
Normal file
1515
src/sprite.c
Normal file
File diff suppressed because it is too large
Load Diff
66
src/sprite.h
Normal file
66
src/sprite.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Sprite Sprite;
|
||||
|
||||
struct Sprite
|
||||
{
|
||||
tic_mem* tic;
|
||||
|
||||
u32 tickCounter;
|
||||
|
||||
u16 index;
|
||||
u8 color;
|
||||
u8 color2;
|
||||
u8 size;
|
||||
u8 brushSize;
|
||||
|
||||
bool editPalette;
|
||||
|
||||
struct
|
||||
{
|
||||
SDL_Rect rect;
|
||||
SDL_Point start;
|
||||
bool drag;
|
||||
u8* back;
|
||||
u8* front;
|
||||
}select;
|
||||
|
||||
enum
|
||||
{
|
||||
SPRITE_DRAW_MODE,
|
||||
SPRITE_PICK_MODE,
|
||||
SPRITE_SELECT_MODE,
|
||||
SPRITE_FILL_MODE,
|
||||
}mode;
|
||||
|
||||
struct History* history;
|
||||
|
||||
void (*tick)(Sprite*);
|
||||
void(*event)(Sprite*, StudioEvent);
|
||||
};
|
||||
|
||||
void initSprite(Sprite*, tic_mem*);
|
||||
106
src/start.c
Normal file
106
src/start.c
Normal file
@@ -0,0 +1,106 @@
|
||||
// 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 "start.h"
|
||||
|
||||
static void reset(Start* start)
|
||||
{
|
||||
u8* tile = (u8*)start->tic->ram.gfx.tiles;
|
||||
|
||||
start->tic->api.clear(start->tic, systemColor(tic_color_black));
|
||||
|
||||
static const u8 Reset[] = {0x00, 0x06, 0x96, 0x00};
|
||||
u8 val = Reset[sizeof(Reset) * (start->ticks % TIC_FRAMERATE) / TIC_FRAMERATE];
|
||||
|
||||
for(s32 i = 0; i < sizeof(tic_tile); i++) tile[i] = val;
|
||||
|
||||
start->tic->api.map(start->tic, &start->tic->ram.gfx, 0, 0, TIC_MAP_SCREEN_WIDTH, TIC_MAP_SCREEN_HEIGHT + (TIC80_HEIGHT % TIC_SPRITESIZE ? 1 : 0), 0, 0, -1, 1);
|
||||
}
|
||||
|
||||
static void drawHeader(Start* start)
|
||||
{
|
||||
start->tic->api.fixed_text(start->tic, TIC_NAME_FULL, STUDIO_TEXT_WIDTH, STUDIO_TEXT_HEIGHT, systemColor(tic_color_white));
|
||||
start->tic->api.fixed_text(start->tic, TIC_VERSION_LABEL, (sizeof(TIC_NAME_FULL) + 1) * STUDIO_TEXT_WIDTH, STUDIO_TEXT_HEIGHT, systemColor(tic_color_dark_gray));
|
||||
start->tic->api.fixed_text(start->tic, TIC_COPYRIGHT, STUDIO_TEXT_WIDTH, STUDIO_TEXT_HEIGHT*2, systemColor(tic_color_dark_gray));
|
||||
}
|
||||
|
||||
static void header(Start* start)
|
||||
{
|
||||
if(!start->play)
|
||||
{
|
||||
playSystemSfx(1);
|
||||
|
||||
start->play = true;
|
||||
}
|
||||
|
||||
drawHeader(start);
|
||||
}
|
||||
|
||||
static void end(Start* start)
|
||||
{
|
||||
if(start->play)
|
||||
{
|
||||
start->tic->api.sfx_stop(start->tic, 0);
|
||||
start->play = false;
|
||||
}
|
||||
|
||||
drawHeader(start);
|
||||
|
||||
setStudioMode(TIC_CONSOLE_MODE);
|
||||
}
|
||||
|
||||
static void tick(Start* start)
|
||||
{
|
||||
if(!start->initialized)
|
||||
{
|
||||
start->phase = 1;
|
||||
start->ticks = 0;
|
||||
|
||||
start->initialized = true;
|
||||
}
|
||||
|
||||
while (pollEvent());
|
||||
|
||||
start->tic->api.clear(start->tic, TIC_COLOR_BG);
|
||||
|
||||
static void(*const steps[])(Start*) = {reset, header, end};
|
||||
|
||||
steps[start->ticks / TIC_FRAMERATE](start);
|
||||
|
||||
start->ticks++;
|
||||
}
|
||||
|
||||
void initStart(Start* start, tic_mem* tic)
|
||||
{
|
||||
*start = (Start)
|
||||
{
|
||||
.tic = tic,
|
||||
.initialized = false,
|
||||
.phase = 1,
|
||||
.ticks = 0,
|
||||
.tick = tick,
|
||||
.play = false,
|
||||
};
|
||||
|
||||
memcpy(tic->cart.palette.data, tic->config.palette.data, sizeof(tic_palette));
|
||||
tic->api.reset(tic);
|
||||
}
|
||||
42
src/start.h
Normal file
42
src/start.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Start Start;
|
||||
|
||||
struct Start
|
||||
{
|
||||
tic_mem* tic;
|
||||
|
||||
bool initialized;
|
||||
|
||||
u32 phase;
|
||||
u32 ticks;
|
||||
bool play;
|
||||
|
||||
void (*tick)(Start*);
|
||||
};
|
||||
|
||||
void initStart(Start* start, tic_mem* tic);
|
||||
2436
src/studio.c
Normal file
2436
src/studio.c
Normal file
File diff suppressed because it is too large
Load Diff
200
src/studio.h
Normal file
200
src/studio.h
Normal file
@@ -0,0 +1,200 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#include "tic.h"
|
||||
#include "ticapi.h"
|
||||
#include "defines.h"
|
||||
#include "tools.h"
|
||||
|
||||
#define TIC_LOCAL ".local/"
|
||||
#define TIC_CACHE TIC_LOCAL "cache/"
|
||||
|
||||
#define TIC_MOD_CTRL (KMOD_GUI|KMOD_CTRL)
|
||||
|
||||
#define TOOLBAR_SIZE 8
|
||||
#define STUDIO_TEXT_WIDTH (TIC_FONT_WIDTH)
|
||||
#define STUDIO_TEXT_HEIGHT (TIC_FONT_HEIGHT+1)
|
||||
#define STUDIO_TEXT_BUFFER_WIDTH (TIC80_WIDTH / STUDIO_TEXT_WIDTH)
|
||||
#define STUDIO_TEXT_BUFFER_HEIGHT (TIC80_HEIGHT / STUDIO_TEXT_HEIGHT)
|
||||
|
||||
#define TIC_COLOR_BG systemColor(tic_color_black)
|
||||
#define DEFAULT_CHMOD 0755
|
||||
|
||||
#define CONFIG_TIC "config " TIC_VERSION_LABEL ".tic"
|
||||
#define CONFIG_TIC_PATH TIC_LOCAL CONFIG_TIC
|
||||
|
||||
#define KEYMAP_COUNT (sizeof(tic80_input) * BITS_IN_BYTE)
|
||||
#define KEYMAP_SIZE (KEYMAP_COUNT * sizeof(SDL_Scancode))
|
||||
#define KEYMAP_DAT "keymap.dat"
|
||||
#define KEYMAP_DAT_PATH TIC_LOCAL KEYMAP_DAT
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 data[TIC_PALETTE_SIZE];
|
||||
} palmap;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 sprite;
|
||||
bool pixelPerfect;
|
||||
} cursor;
|
||||
|
||||
struct
|
||||
{
|
||||
u8 bg;
|
||||
u8 string;
|
||||
u8 number;
|
||||
u8 keyword;
|
||||
u8 api;
|
||||
u8 comment;
|
||||
u8 sign;
|
||||
u8 var;
|
||||
u8 other;
|
||||
u8 select;
|
||||
u8 cursor;
|
||||
} code;
|
||||
|
||||
struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 alpha;
|
||||
} touch;
|
||||
|
||||
} gamepad;
|
||||
|
||||
} theme;
|
||||
|
||||
s32 gifScale;
|
||||
s32 gifLength;
|
||||
|
||||
bool checkNewVersion;
|
||||
|
||||
} StudioConfig;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TIC_START_MODE,
|
||||
TIC_CONSOLE_MODE,
|
||||
TIC_RUN_MODE,
|
||||
TIC_CODE_MODE,
|
||||
TIC_SPRITE_MODE,
|
||||
TIC_MAP_MODE,
|
||||
TIC_WORLD_MODE,
|
||||
TIC_SFX_MODE,
|
||||
TIC_MUSIC_MODE,
|
||||
TIC_KEYMAP_MODE,
|
||||
TIC_DIALOG_MODE,
|
||||
TIC_MENU_MODE,
|
||||
TIC_SURF_MODE,
|
||||
} EditorMode;
|
||||
|
||||
SDL_Event* pollEvent();
|
||||
void setCursor(SDL_SystemCursor id);
|
||||
|
||||
s32 getMouseX();
|
||||
s32 getMouseY();
|
||||
bool checkMousePos(const SDL_Rect* rect);
|
||||
bool checkMouseClick(const SDL_Rect* rect, s32 button);
|
||||
bool checkMouseDown(const SDL_Rect* rect, s32 button);
|
||||
|
||||
bool getGesturePos(SDL_Point* pos);
|
||||
|
||||
const u8* getKeyboard();
|
||||
|
||||
void drawToolbar(tic_mem* tic, u8 color, bool bg);
|
||||
void drawExtrabar(tic_mem* tic);
|
||||
void drawBitIcon(s32 x, s32 y, const u8* ptr, u8 color);
|
||||
|
||||
void studioRomLoaded();
|
||||
void studioRomSaved();
|
||||
void studioConfigChanged();
|
||||
|
||||
void setStudioMode(EditorMode mode);
|
||||
EditorMode getStudioMode();
|
||||
void exitStudio();
|
||||
u32 unzip(u8** dest, const u8* source, size_t size);
|
||||
|
||||
void str2buf(const char* str, void* buf, bool flip);
|
||||
void toClipboard(const void* data, s32 size, bool flip);
|
||||
bool fromClipboard(void* data, s32 size, bool flip);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TIC_CLIPBOARD_NONE,
|
||||
TIC_CLIPBOARD_CUT,
|
||||
TIC_CLIPBOARD_COPY,
|
||||
TIC_CLIPBOARD_PASTE,
|
||||
} ClipboardEvent;
|
||||
|
||||
ClipboardEvent getClipboardEvent(SDL_Keycode keycode);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TIC_TOOLBAR_CUT,
|
||||
TIC_TOOLBAR_COPY,
|
||||
TIC_TOOLBAR_PASTE,
|
||||
TIC_TOOLBAR_UNDO,
|
||||
TIC_TOOLBAR_REDO,
|
||||
} StudioEvent;
|
||||
|
||||
void setStudioEvent(StudioEvent event);
|
||||
void showTooltip(const char* text);
|
||||
|
||||
SDL_Scancode* getKeymap();
|
||||
|
||||
u8 systemColor(u8 color);
|
||||
|
||||
const StudioConfig* getConfig();
|
||||
|
||||
void setSpritePixel(tic_tile* tiles, s32 x, s32 y, u8 color);
|
||||
u8 getSpritePixel(tic_tile* tiles, s32 x, s32 y);
|
||||
|
||||
typedef void(*DialogCallback)(bool yes, void* data);
|
||||
void showDialog(const char** text, s32 rows, DialogCallback callback, void* data);
|
||||
void hideDialog();
|
||||
|
||||
void showGameMenu();
|
||||
void hideGameMenu();
|
||||
|
||||
bool studioCartChanged();
|
||||
void playSystemSfx(s32 id);
|
||||
|
||||
void runGameFromSurf();
|
||||
void exitFromGameMenu();
|
||||
815
src/surf.c
Normal file
815
src/surf.c
Normal file
@@ -0,0 +1,815 @@
|
||||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "surf.h"
|
||||
#include "fs.h"
|
||||
#include "net.h"
|
||||
#include "console.h"
|
||||
|
||||
#include "ext/gif.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define MAIN_OFFSET 4
|
||||
#define MENU_HEIGHT 10
|
||||
#define MAX_CARTS 256
|
||||
#define ANIM 10
|
||||
#define COVER_WIDTH 140
|
||||
#define COVER_HEIGHT 116
|
||||
#define COVER_Y 5
|
||||
#define COVER_X (TIC80_WIDTH - COVER_WIDTH - COVER_Y)
|
||||
|
||||
#if defined(__WINDOWS__) || (defined(__LINUX__) && !defined(__ARM_LINUX__)) || defined(__MACOSX__)
|
||||
#define CAN_OPEN_URL 1
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s32 start;
|
||||
s32 end;
|
||||
s32 duration;
|
||||
|
||||
s32* val;
|
||||
} Anim;
|
||||
|
||||
typedef struct Movie Movie;
|
||||
|
||||
struct Movie
|
||||
{
|
||||
Anim** items;
|
||||
|
||||
s32 time;
|
||||
s32 duration;
|
||||
s32 count;
|
||||
|
||||
Movie* next;
|
||||
void (*done)(Surf* surf);
|
||||
};
|
||||
|
||||
static struct
|
||||
{
|
||||
s32 topBarY;
|
||||
s32 bottomBarY;
|
||||
s32 menuX;
|
||||
s32 menuHeight;
|
||||
} AnimVar;
|
||||
|
||||
static Anim topBarShowAnim = {0, MENU_HEIGHT, ANIM, &AnimVar.topBarY};
|
||||
static Anim bottomBarShowAnim = {0, MENU_HEIGHT, ANIM, &AnimVar.bottomBarY};
|
||||
|
||||
static Anim topBarHideAnim = {MENU_HEIGHT, 0, ANIM, &AnimVar.topBarY};
|
||||
static Anim bottomBarHideAnim = {MENU_HEIGHT, 0, ANIM, &AnimVar.bottomBarY};
|
||||
|
||||
static Anim menuLeftHideAnim = {0, -240, ANIM, &AnimVar.menuX};
|
||||
static Anim menuRightHideAnim = {0, 240, ANIM, &AnimVar.menuX};
|
||||
static Anim menuHideAnim = {MENU_HEIGHT, 0, ANIM, &AnimVar.menuHeight};
|
||||
|
||||
static Anim menuLeftShowAnim = {240, 0, ANIM, &AnimVar.menuX};
|
||||
static Anim menuRightShowAnim = {-240, 0, ANIM, &AnimVar.menuX};
|
||||
static Anim menuShowAnim = {0, MENU_HEIGHT, ANIM, &AnimVar.menuHeight};
|
||||
|
||||
static Anim* MenuModeShowMovieItems[] =
|
||||
{
|
||||
&topBarShowAnim,
|
||||
&bottomBarShowAnim,
|
||||
&menuRightShowAnim,
|
||||
&menuShowAnim,
|
||||
};
|
||||
|
||||
static Anim* MenuModeHideMovieItems[] =
|
||||
{
|
||||
&topBarHideAnim,
|
||||
&bottomBarHideAnim,
|
||||
&menuLeftHideAnim,
|
||||
&menuHideAnim,
|
||||
};
|
||||
|
||||
static Anim* MenuLeftHideMovieItems[] =
|
||||
{
|
||||
&menuLeftHideAnim,
|
||||
&menuHideAnim,
|
||||
};
|
||||
|
||||
static Anim* MenuRightHideMovieItems[] =
|
||||
{
|
||||
&menuRightHideAnim,
|
||||
&menuHideAnim,
|
||||
};
|
||||
|
||||
static Anim* MenuLeftShowMovieItems[] =
|
||||
{
|
||||
&menuLeftShowAnim,
|
||||
&menuShowAnim,
|
||||
};
|
||||
|
||||
static Anim* MenuRightShowMovieItems[] =
|
||||
{
|
||||
&menuRightShowAnim,
|
||||
&menuShowAnim,
|
||||
};
|
||||
|
||||
static Movie EmptyState;
|
||||
static Movie MenuModeState;
|
||||
|
||||
#define DECLARE_MOVIE(NAME, NEXT) static Movie NAME ## State = \
|
||||
{ \
|
||||
.items = NAME ## MovieItems, \
|
||||
.count = COUNT_OF(NAME ## MovieItems), \
|
||||
.duration = ANIM, \
|
||||
.next = & NEXT ## State, \
|
||||
}
|
||||
|
||||
DECLARE_MOVIE(MenuModeShow, MenuMode);
|
||||
DECLARE_MOVIE(MenuModeHide, Empty);
|
||||
DECLARE_MOVIE(MenuLeftShow, MenuMode);
|
||||
DECLARE_MOVIE(MenuRightShow, MenuMode);
|
||||
DECLARE_MOVIE(MenuLeftHide, MenuLeftShow);
|
||||
DECLARE_MOVIE(MenuRightHide, MenuRightShow);
|
||||
|
||||
typedef struct MenuItem MenuItem;
|
||||
|
||||
struct MenuItem
|
||||
{
|
||||
char* label;
|
||||
const char* name;
|
||||
const char* hash;
|
||||
s32 id;
|
||||
tic_screen* cover;
|
||||
bool dir;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MenuItem* items;
|
||||
s32 count;
|
||||
Surf* surf;
|
||||
} AddMenuItem;
|
||||
|
||||
static void resetMovie(Surf* surf, Movie* movie, void (*done)(Surf* surf))
|
||||
{
|
||||
surf->state = movie;
|
||||
|
||||
movie->time = 0;
|
||||
for(s32 i = 0; i < movie->count; i++)
|
||||
{
|
||||
Anim* anim = movie->items[i];
|
||||
*anim->val = anim->start;
|
||||
}
|
||||
|
||||
movie->time = 0;
|
||||
movie->done = done;
|
||||
}
|
||||
|
||||
static void drawTopToolbar(Surf* surf, s32 x, s32 y)
|
||||
{
|
||||
tic_mem* tic = surf->tic;
|
||||
|
||||
enum{Height = MENU_HEIGHT};
|
||||
|
||||
tic->api.rect(tic, x, y, TIC80_WIDTH, Height, tic_color_blue);
|
||||
tic->api.rect(tic, x, y + Height, TIC80_WIDTH, 1, tic_color_black);
|
||||
{
|
||||
char label[FILENAME_MAX];
|
||||
|
||||
sprintf(label, "%s", "TIC-80 SURF");
|
||||
|
||||
s32 xl = x + MAIN_OFFSET;
|
||||
s32 yl = y + (Height - TIC_FONT_HEIGHT)/2;
|
||||
tic->api.text(tic, label, xl, yl+1, tic_color_black);
|
||||
tic->api.text(tic, label, xl, yl, tic_color_white);
|
||||
}
|
||||
|
||||
enum{Gap = 10, TipX = 150, SelectWidth = 54};
|
||||
|
||||
u8 colorkey = 0;
|
||||
tic->api.sprite_ex(tic, &tic->config.gfx, 12, TipX, y+1, 1, 1, &colorkey, 1, 1, tic_no_flip, tic_no_rotate);
|
||||
{
|
||||
static const char Label[] = "SELECT";
|
||||
tic->api.text(tic, Label, TipX + Gap, y+3, tic_color_black);
|
||||
tic->api.text(tic, Label, TipX + Gap, y+2, tic_color_white);
|
||||
}
|
||||
|
||||
tic->api.sprite_ex(tic, &tic->config.gfx, 13, TipX + SelectWidth, y + 1, 1, 1, &colorkey, 1, 1, tic_no_flip, tic_no_rotate);
|
||||
{
|
||||
static const char Label[] = "BACK";
|
||||
tic->api.text(tic, Label, TipX + Gap + SelectWidth, y +3, tic_color_black);
|
||||
tic->api.text(tic, Label, TipX + Gap + SelectWidth, y +2, tic_color_white);
|
||||
}
|
||||
}
|
||||
|
||||
static void drawBottomToolbar(Surf* surf, s32 x, s32 y)
|
||||
{
|
||||
tic_mem* tic = surf->tic;
|
||||
|
||||
enum{Height = MENU_HEIGHT};
|
||||
|
||||
tic->api.rect(tic, x, y, TIC80_WIDTH, Height, tic_color_blue);
|
||||
tic->api.rect(tic, x, y + Height, TIC80_WIDTH, 1, tic_color_black);
|
||||
{
|
||||
char label[FILENAME_MAX];
|
||||
|
||||
sprintf(label, "/%s", fsGetDir(surf->fs));
|
||||
|
||||
s32 xl = x + MAIN_OFFSET;
|
||||
s32 yl = y + (Height - TIC_FONT_HEIGHT)/2;
|
||||
tic->api.text(tic, label, xl, yl+1, tic_color_black);
|
||||
tic->api.text(tic, label, xl, yl, tic_color_white);
|
||||
}
|
||||
|
||||
#ifdef CAN_OPEN_URL
|
||||
|
||||
if(surf->menu.items[surf->menu.pos].hash)
|
||||
{
|
||||
enum{Gap = 10, TipX = 134, SelectWidth = 54};
|
||||
|
||||
u8 colorkey = 0;
|
||||
|
||||
tic->api.sprite_ex(tic, &tic->config.gfx, 15, TipX + SelectWidth, y + 1, 1, 1, &colorkey, 1, 1, tic_no_flip, tic_no_rotate);
|
||||
{
|
||||
static const char Label[] = "WEBSITE";
|
||||
tic->api.text(tic, Label, TipX + Gap + SelectWidth, y +3, tic_color_black);
|
||||
tic->api.text(tic, Label, TipX + Gap + SelectWidth, y +2, tic_color_white);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static void drawCover(Surf* surf, s32 pos, s32 x, s32 y)
|
||||
{
|
||||
if(!surf->menu.items[surf->menu.pos].cover)
|
||||
return;
|
||||
|
||||
tic_mem* tic = surf->tic;
|
||||
|
||||
enum{Width = TIC80_WIDTH, Height = TIC80_HEIGHT};
|
||||
|
||||
tic_screen* cover = surf->menu.items[pos].cover;
|
||||
|
||||
if(cover)
|
||||
{
|
||||
for(s32 yc = 0; yc < Height; yc++)
|
||||
memcpy(tic->ram.vram.screen.data + (yc * TIC80_WIDTH)/2, cover->data + (yc * Width)/2, Width/2);
|
||||
}
|
||||
}
|
||||
|
||||
static void drawInverseRect(tic_mem* tic, s32 x, s32 y, s32 w, s32 h)
|
||||
{
|
||||
if(x < 0)
|
||||
{
|
||||
w += x;
|
||||
x = 0;
|
||||
}
|
||||
|
||||
if(y < 0)
|
||||
{
|
||||
h += y;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
w += x;
|
||||
h += y;
|
||||
|
||||
if(w > TIC80_WIDTH)
|
||||
w = TIC80_WIDTH;
|
||||
|
||||
if(h > TIC80_HEIGHT)
|
||||
h = TIC80_HEIGHT;
|
||||
|
||||
for(s32 j = y; j < h; j++)
|
||||
{
|
||||
for(s32 i = x; i < w; i++)
|
||||
{
|
||||
s32 index = i + j*TIC80_WIDTH;
|
||||
u8 color = tic_tool_peek4(tic->ram.vram.screen.data, index);
|
||||
tic_tool_poke4(tic->ram.vram.screen.data, index, color % 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawMenu(Surf* surf, s32 x, s32 y, bool bg)
|
||||
{
|
||||
tic_mem* tic = surf->tic;
|
||||
|
||||
enum {Height = MENU_HEIGHT};
|
||||
|
||||
if(bg)
|
||||
{
|
||||
if(AnimVar.menuHeight)
|
||||
drawInverseRect(tic, 0, y + (MENU_HEIGHT - AnimVar.menuHeight)/2 - 1, TIC80_WIDTH, AnimVar.menuHeight+2);
|
||||
}
|
||||
else
|
||||
{
|
||||
tic->api.rect(tic, 0, y + (MENU_HEIGHT - AnimVar.menuHeight)/2, TIC80_WIDTH, AnimVar.menuHeight, tic_color_red);
|
||||
}
|
||||
|
||||
for(s32 i = 0; i < surf->menu.count; i++)
|
||||
{
|
||||
const char* name = surf->menu.items[i].label;
|
||||
|
||||
s32 ym = Height * i + y - surf->menu.pos*MENU_HEIGHT - surf->menu.anim + (MENU_HEIGHT - TIC_FONT_HEIGHT)/2;
|
||||
|
||||
if(bg)
|
||||
{
|
||||
s32 size = tic->api.text(tic, name, 0, -TIC_FONT_HEIGHT, 0);
|
||||
|
||||
drawInverseRect(tic, x + MAIN_OFFSET - 1, ym-1, size+1, TIC_FONT_HEIGHT+2);
|
||||
}
|
||||
else
|
||||
{
|
||||
tic->api.text(tic, name, x + MAIN_OFFSET, ym + 1, tic_color_black);
|
||||
tic->api.text(tic, name, x + MAIN_OFFSET, ym, tic_color_white);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawBG(Surf* surf)
|
||||
{
|
||||
tic_mem* tic = surf->tic;
|
||||
|
||||
enum{Size = 16, Width = TIC80_WIDTH/Size+1, Height = TIC80_HEIGHT/Size+1};
|
||||
|
||||
s32 offset = surf->ticks % Size;
|
||||
s32 counter = 0;
|
||||
|
||||
for(s32 j = 0; j < Height + 1; j++)
|
||||
for(s32 i = 0; i < Width + 1; i++)
|
||||
if(counter++ % 2)
|
||||
tic->api.sprite_ex(tic, &tic->config.gfx, 34, i*Size - offset, j*Size - offset, 2, 2, 0, 0, 1, tic_no_flip, tic_no_rotate);
|
||||
}
|
||||
|
||||
static void replace(char* src, const char* what, const char* with)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
char* pos = strstr(src, what);
|
||||
|
||||
if(pos)
|
||||
{
|
||||
strcpy(pos, pos + strlen(what) - strlen(with));
|
||||
memcpy(pos, with, strlen(with));
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool addMenuItem(const char* name, const char* info, s32 id, void* ptr, bool dir)
|
||||
{
|
||||
AddMenuItem* data = (AddMenuItem*)ptr;
|
||||
|
||||
static const char CartExt[] = ".tic";
|
||||
|
||||
if(dir || (strstr(name, CartExt) == name + strlen(name) - sizeof(CartExt)+1))
|
||||
{
|
||||
MenuItem* item = &data->items[data->count++];
|
||||
|
||||
item->name = SDL_strdup(name);
|
||||
|
||||
if(dir)
|
||||
{
|
||||
char folder[FILENAME_MAX];
|
||||
sprintf(folder, "[%s]", name);
|
||||
item->label = SDL_strdup(folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
item->label = SDL_strdup(name);
|
||||
item->label[strlen(item->label)-sizeof(CartExt)+1] = '\0';
|
||||
|
||||
replace(item->label, "&", "&");
|
||||
replace(item->label, "'", "'");
|
||||
}
|
||||
|
||||
item->hash = info ? SDL_strdup(info) : NULL;
|
||||
item->id = id;
|
||||
item->dir = dir;
|
||||
item->cover = NULL;
|
||||
}
|
||||
|
||||
return data->count < MAX_CARTS;
|
||||
}
|
||||
|
||||
static void resetMenu(Surf* surf)
|
||||
{
|
||||
if(surf->menu.items)
|
||||
{
|
||||
for(s32 i = 0; i < surf->menu.count; i++)
|
||||
{
|
||||
SDL_free((void*)surf->menu.items[i].name);
|
||||
|
||||
const char* hash = surf->menu.items[i].hash;
|
||||
if(hash) SDL_free((void*)hash);
|
||||
|
||||
tic_screen* cover = surf->menu.items[i].cover;
|
||||
if(cover) SDL_free(cover);
|
||||
|
||||
const char* label = surf->menu.items[i].label;
|
||||
if(label) SDL_free((void*)label);
|
||||
}
|
||||
|
||||
SDL_free(surf->menu.items);
|
||||
|
||||
surf->menu.items = NULL;
|
||||
surf->menu.count = 0;
|
||||
}
|
||||
|
||||
surf->menu.pos = 0;
|
||||
surf->menu.anim = 0;
|
||||
}
|
||||
|
||||
static void* requestCover(Surf* surf, const char* hash, s32* size)
|
||||
{
|
||||
char cachePath[FILENAME_MAX] = {0};
|
||||
sprintf(cachePath, TIC_CACHE "%s.gif", hash);
|
||||
|
||||
{
|
||||
void* data = fsLoadRootFile(surf->fs, cachePath, size);
|
||||
|
||||
if(data)
|
||||
return data;
|
||||
}
|
||||
|
||||
char path[FILENAME_MAX] = {0};
|
||||
sprintf(path, "/cart/%s/cover.gif", hash);
|
||||
void* data = netGetRequest(surf->net, path, size);
|
||||
|
||||
if(data)
|
||||
{
|
||||
fsSaveRootFile(surf->fs, cachePath, data, *size, false);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void updateMenuItemCover(Surf* surf, const u8* cover, s32 size)
|
||||
{
|
||||
tic_mem* tic = surf->tic;
|
||||
|
||||
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
||||
|
||||
|
||||
item->cover = SDL_malloc(sizeof(tic_screen));
|
||||
|
||||
gif_image* image = gif_read_data(cover, size);
|
||||
|
||||
if(image)
|
||||
{
|
||||
if (image->width == TIC80_WIDTH && image->height == TIC80_HEIGHT)
|
||||
{
|
||||
enum { Size = TIC80_WIDTH * TIC80_HEIGHT };
|
||||
|
||||
for (s32 i = 0; i < Size; i++)
|
||||
{
|
||||
const gif_color* c = &image->palette[image->buffer[i]];
|
||||
tic_rgb rgb = { c->r, c->g, c->b };
|
||||
u8 color = tic_tool_find_closest_color(tic->cart.palette.colors, &rgb);
|
||||
tic_tool_poke4(item->cover->data, i, color);
|
||||
}
|
||||
}
|
||||
|
||||
gif_close(image);
|
||||
}
|
||||
}
|
||||
|
||||
static void loadCover(Surf* surf)
|
||||
{
|
||||
tic_mem* tic = surf->tic;
|
||||
|
||||
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
||||
|
||||
if(!fsIsInPublicDir(surf->fs))
|
||||
{
|
||||
|
||||
s32 size = 0;
|
||||
void* data = fsLoadFile(surf->fs, item->name, &size);
|
||||
|
||||
if(data)
|
||||
{
|
||||
tic_cartridge* cart = (tic_cartridge*)SDL_malloc(sizeof(tic_cartridge));
|
||||
|
||||
if(cart)
|
||||
{
|
||||
tic->api.load(cart, data, size, true);
|
||||
|
||||
if(cart->cover.size)
|
||||
updateMenuItemCover(surf, cart->cover.data, cart->cover.size);
|
||||
|
||||
SDL_free(cart);
|
||||
}
|
||||
|
||||
SDL_free(data);
|
||||
}
|
||||
}
|
||||
else if(item->hash && !item->cover)
|
||||
{
|
||||
s32 size = 0;
|
||||
|
||||
u8* cover = requestCover(surf, item->hash, &size);
|
||||
|
||||
if(cover)
|
||||
{
|
||||
updateMenuItemCover(surf, cover, size);
|
||||
SDL_free(cover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void initMenu(Surf* surf)
|
||||
{
|
||||
resetMenu(surf);
|
||||
|
||||
// TODO: calc files count before
|
||||
enum{Count = MAX_CARTS, Size = sizeof(MenuItem) * Count};
|
||||
|
||||
AddMenuItem data =
|
||||
{
|
||||
.items = SDL_malloc(Size),
|
||||
.count = 0,
|
||||
.surf = surf,
|
||||
};
|
||||
|
||||
if(strcmp(fsGetDir(surf->fs), "") != 0)
|
||||
addMenuItem("..", NULL, 0, &data, true);
|
||||
|
||||
fsEnumFiles(surf->fs, addMenuItem, &data);
|
||||
|
||||
surf->menu.items = data.items;
|
||||
surf->menu.count = data.count;
|
||||
}
|
||||
|
||||
static void onGoBackDir(Surf* surf)
|
||||
{
|
||||
char last[FILENAME_MAX];
|
||||
strcpy(last, fsGetDir(surf->fs));
|
||||
|
||||
fsDirBack(surf->fs);
|
||||
initMenu(surf);
|
||||
|
||||
const char* current = fsGetDir(surf->fs);
|
||||
|
||||
for(s32 i = 0; i < surf->menu.count; i++)
|
||||
{
|
||||
const MenuItem* item = &surf->menu.items[i];
|
||||
|
||||
if(item->dir)
|
||||
{
|
||||
char path[FILENAME_MAX];
|
||||
|
||||
if(strlen(current))
|
||||
sprintf(path, "%s/%s", current, item->name);
|
||||
else strcpy(path, item->name);
|
||||
|
||||
if(strcmp(path, last) == 0)
|
||||
{
|
||||
surf->menu.pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void onGoToDir(Surf* surf)
|
||||
{
|
||||
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
||||
|
||||
fsChangeDir(surf->fs, item->name);
|
||||
initMenu(surf);
|
||||
}
|
||||
|
||||
static void changeDirectory(Surf* surf, const char* dir)
|
||||
{
|
||||
if(strcmp(dir, "..") == 0)
|
||||
{
|
||||
if(strcmp(fsGetDir(surf->fs), "") != 0)
|
||||
{
|
||||
playSystemSfx(2);
|
||||
resetMovie(surf, &MenuRightHideState, onGoBackDir);
|
||||
}
|
||||
}
|
||||
else if(fsIsDir(surf->fs, dir))
|
||||
{
|
||||
playSystemSfx(2);
|
||||
resetMovie(surf, &MenuLeftHideState, onGoToDir);
|
||||
}
|
||||
}
|
||||
|
||||
static void onPlayCart(Surf* surf)
|
||||
{
|
||||
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
||||
|
||||
surf->console->load(surf->console, item->name);
|
||||
|
||||
runGameFromSurf();
|
||||
}
|
||||
|
||||
static void loadCart(Surf* surf)
|
||||
{
|
||||
resetMovie(surf, &MenuModeHideState, onPlayCart);
|
||||
}
|
||||
|
||||
static void processAnim(Surf* surf)
|
||||
{
|
||||
enum{Frames = MENU_HEIGHT};
|
||||
|
||||
{
|
||||
if(surf->state->time > surf->state->duration)
|
||||
{
|
||||
if(surf->state->done)
|
||||
surf->state->done(surf);
|
||||
|
||||
if(surf->state->next)
|
||||
resetMovie(surf, surf->state->next, NULL);
|
||||
}
|
||||
|
||||
for(s32 i = 0; i < surf->state->count; i++)
|
||||
{
|
||||
Anim* anim = surf->state->items[i];
|
||||
|
||||
if(surf->state->time < anim->duration)
|
||||
{
|
||||
*anim->val = anim->start + (anim->end - anim->start) * surf->state->time / anim->duration;
|
||||
}
|
||||
else
|
||||
{
|
||||
*anim->val = anim->end;
|
||||
}
|
||||
}
|
||||
|
||||
surf->state->time++;
|
||||
|
||||
}
|
||||
|
||||
if(surf->menu.anim)
|
||||
{
|
||||
if(surf->menu.anim < 0) surf->menu.anim--;
|
||||
if(surf->menu.anim > 0) surf->menu.anim++;
|
||||
|
||||
if(surf->menu.anim <= -Frames)
|
||||
{
|
||||
surf->menu.anim = 0;
|
||||
surf->menu.pos--;
|
||||
|
||||
if(surf->menu.pos < 0)
|
||||
surf->menu.pos = surf->menu.count-1;
|
||||
}
|
||||
|
||||
if(surf->menu.anim >= Frames)
|
||||
{
|
||||
surf->menu.anim = 0;
|
||||
surf->menu.pos++;
|
||||
|
||||
if(surf->menu.pos >= surf->menu.count)
|
||||
surf->menu.pos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void processGamepad(Surf* surf)
|
||||
{
|
||||
tic_mem* tic = surf->tic;
|
||||
|
||||
enum{Frames = MENU_HEIGHT};
|
||||
|
||||
{
|
||||
enum{Hold = 20, Period = Frames};
|
||||
|
||||
enum
|
||||
{
|
||||
Up, Down, Left, Right, A, B, X, Y
|
||||
};
|
||||
|
||||
if(tic->api.btnp(tic, Up, Hold, Period))
|
||||
{
|
||||
surf->menu.anim = -1;
|
||||
|
||||
playSystemSfx(2);
|
||||
}
|
||||
|
||||
if(tic->api.btnp(tic, Down, Hold, Period))
|
||||
{
|
||||
surf->menu.anim = 1;
|
||||
|
||||
playSystemSfx(2);
|
||||
}
|
||||
|
||||
if(tic->api.btnp(tic, A, -1, -1))
|
||||
{
|
||||
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
||||
item->dir ? changeDirectory(surf, item->name) : loadCart(surf);
|
||||
}
|
||||
|
||||
if(tic->api.btnp(tic, B, -1, -1))
|
||||
{
|
||||
changeDirectory(surf, "..");
|
||||
}
|
||||
|
||||
#ifdef CAN_OPEN_URL
|
||||
|
||||
if(tic->api.btnp(tic, Y, -1, -1))
|
||||
{
|
||||
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
||||
|
||||
if(!item->dir)
|
||||
{
|
||||
char url[FILENAME_MAX];
|
||||
sprintf(url, "https://" TIC_HOST "/play?cart=%i", item->id);
|
||||
fsOpenSystemPath(surf->fs, url);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void tick(Surf* surf)
|
||||
{
|
||||
if(!surf->init)
|
||||
{
|
||||
initMenu(surf);
|
||||
|
||||
resetMovie(surf, &MenuModeShowState, NULL);
|
||||
|
||||
surf->init = true;
|
||||
}
|
||||
|
||||
surf->ticks++;
|
||||
|
||||
while (pollEvent());
|
||||
|
||||
|
||||
tic_mem* tic = surf->tic;
|
||||
tic->api.clear(tic, TIC_COLOR_BG);
|
||||
|
||||
drawBG(surf);
|
||||
processAnim(surf);
|
||||
|
||||
if(surf->state == &MenuModeState)
|
||||
{
|
||||
processGamepad(surf);
|
||||
}
|
||||
|
||||
{
|
||||
loadCover(surf);
|
||||
|
||||
drawCover(surf, surf->menu.pos, 0, 0);
|
||||
|
||||
if(surf->menu.items[surf->menu.pos].cover)
|
||||
drawMenu(surf, AnimVar.menuX, (TIC80_HEIGHT - MENU_HEIGHT)/2, true);
|
||||
|
||||
drawMenu(surf, AnimVar.menuX, (TIC80_HEIGHT - MENU_HEIGHT)/2, false);
|
||||
|
||||
drawTopToolbar(surf, 0, AnimVar.topBarY - MENU_HEIGHT);
|
||||
drawBottomToolbar(surf, 0, TIC80_HEIGHT - AnimVar.bottomBarY);
|
||||
}
|
||||
}
|
||||
|
||||
static void resume(Surf* surf)
|
||||
{
|
||||
resetMovie(surf, &MenuModeShowState, NULL);
|
||||
}
|
||||
|
||||
void initSurf(Surf* surf, tic_mem* tic, struct Console* console)
|
||||
{
|
||||
*surf = (Surf)
|
||||
{
|
||||
.tic = tic,
|
||||
.console = console,
|
||||
.fs = console->fs,
|
||||
.tick = tick,
|
||||
.ticks = 0,
|
||||
.state = &EmptyState,
|
||||
.init = false,
|
||||
.resume = resume,
|
||||
.menu =
|
||||
{
|
||||
.pos = 0,
|
||||
.anim = 0,
|
||||
.items = NULL,
|
||||
.count = 0,
|
||||
},
|
||||
.net = createNet(),
|
||||
};
|
||||
|
||||
fsMakeDir(surf->fs, TIC_CACHE);
|
||||
}
|
||||
52
src/surf.h
Normal file
52
src/surf.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Surf Surf;
|
||||
|
||||
struct Surf
|
||||
{
|
||||
tic_mem* tic;
|
||||
struct FileSystem* fs;
|
||||
struct Console* console;
|
||||
struct Net* net;
|
||||
struct Movie* state;
|
||||
|
||||
bool init;
|
||||
s32 ticks;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 pos;
|
||||
s32 anim;
|
||||
struct MenuItem* items;
|
||||
s32 count;
|
||||
} menu;
|
||||
|
||||
void(*tick)(Surf* surf);
|
||||
void(*resume)(Surf* surf);
|
||||
};
|
||||
|
||||
void initSurf(Surf* surf, tic_mem* tic, struct Console* console);
|
||||
413
src/tic.h
Normal file
413
src/tic.h
Normal file
@@ -0,0 +1,413 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tic80.h"
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#define TIC_VERSION_MAJOR 0
|
||||
#define TIC_VERSION_MINOR 45
|
||||
#define TIC_VERSION_PATCH 0
|
||||
#define TIC_VERSION_STATUS ""
|
||||
|
||||
#define TIC_MAKE_VERSION(major, minor, patch) ((major) * 10000 + (minor) * 100 + (patch))
|
||||
#define TIC_VERSION TIC_MAKE_VERSION(MYPROJ_VERSION_MAJOR, MYPROJ_VERSION_MINOR, MYPROJ_VERSION_PATCH)
|
||||
|
||||
#define DEF2STR2(x) #x
|
||||
#define DEF2STR(x) DEF2STR2(x)
|
||||
|
||||
#define TIC_VERSION_LABEL DEF2STR(TIC_VERSION_MAJOR) "." DEF2STR(TIC_VERSION_MINOR) "." DEF2STR(TIC_VERSION_PATCH) TIC_VERSION_STATUS
|
||||
#define TIC_PACKAGE "com.nesbox.tic"
|
||||
#define TIC_NAME "TIC-80"
|
||||
#define TIC_NAME_FULL TIC_NAME " tiny computer"
|
||||
#define TIC_TITLE TIC_NAME_FULL " " TIC_VERSION_LABEL
|
||||
#define TIC_HOST "tic.computer"
|
||||
#define TIC_COPYRIGHT "http://" TIC_HOST " (C) 2017"
|
||||
|
||||
#define TIC_VRAM_SIZE (16*1024) //16K
|
||||
#define TIC_RAM_SIZE (80*1024) //80K
|
||||
#define TIC_FONT_WIDTH 6
|
||||
#define TIC_FONT_HEIGHT 6
|
||||
#define TIC_PALETTE_BPP 4
|
||||
#define TIC_PALETTE_SIZE (1 << TIC_PALETTE_BPP)
|
||||
#define TIC_FRAMERATE 60
|
||||
#define TIC_SPRITESIZE 8
|
||||
#define TIC_GAMEPAD_MASK 0xff
|
||||
|
||||
#define BITS_IN_BYTE 8
|
||||
#define TIC_BANK_SPRITES (1 << BITS_IN_BYTE)
|
||||
#define TIC_SPRITES (TIC_BANK_SPRITES * 2)
|
||||
|
||||
#define TIC_SPRITESHEET_SIZE 128
|
||||
|
||||
#define TIC_MAP_ROWS (TIC_SPRITESIZE)
|
||||
#define TIC_MAP_COLS (TIC_SPRITESIZE)
|
||||
#define TIC_MAP_SCREEN_WIDTH (TIC80_WIDTH / TIC_SPRITESIZE)
|
||||
#define TIC_MAP_SCREEN_HEIGHT (TIC80_HEIGHT / TIC_SPRITESIZE)
|
||||
#define TIC_MAP_WIDTH (TIC_MAP_SCREEN_WIDTH * TIC_MAP_ROWS)
|
||||
#define TIC_MAP_HEIGHT (TIC_MAP_SCREEN_HEIGHT * TIC_MAP_COLS)
|
||||
|
||||
#define TIC_PERSISTENT_SIZE ((56-25)/sizeof(s32))
|
||||
#define TIC_SAVEID_SIZE 64
|
||||
|
||||
#define TIC_SOUND_CHANNELS 4
|
||||
#define SFX_TICKS 30
|
||||
#define SFX_COUNT_BITS 6
|
||||
#define SFX_COUNT (1 << SFX_COUNT_BITS)
|
||||
#define SFX_SPEED_BITS 3
|
||||
|
||||
#define NOTES 12
|
||||
#define OCTAVES 8
|
||||
#define MAX_VOLUME 15
|
||||
#define MUSIC_PATTERN_ROWS 64
|
||||
#define MUSIC_PATTERNS 60
|
||||
#define TRACK_PATTERN_BITS 6
|
||||
#define TRACK_PATTERN_MASK ((1 << TRACK_PATTERN_BITS) - 1)
|
||||
#define TRACK_PATTERNS_SIZE (TRACK_PATTERN_BITS * TIC_SOUND_CHANNELS / BITS_IN_BYTE)
|
||||
#define MUSIC_FRAMES 16
|
||||
#define MUSIC_TRACKS_BITS 3
|
||||
#define MUSIC_TRACKS (1 << MUSIC_TRACKS_BITS)
|
||||
#define DEFAULT_TEMPO 150
|
||||
#define DEFAULT_SPEED 6
|
||||
#define NOTES_PER_BEET 4
|
||||
#define PATTERN_START 1
|
||||
#define MUSIC_SFXID_LOW_BITS 5
|
||||
#define ENVELOPES_COUNT 16
|
||||
#define ENVELOPE_VALUES 32
|
||||
#define ENVELOPE_VALUE_BITS 4
|
||||
#define ENVELOPE_SIZE (ENVELOPE_VALUES * ENVELOPE_VALUE_BITS / BITS_IN_BYTE)
|
||||
|
||||
#define TIC_CODE_SIZE (0x10000)
|
||||
|
||||
#define SFX_NOTES {"C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-"}
|
||||
|
||||
#define API_KEYWORDS {"TIC", "scanline", "print", "cls", "pix", "line", "rect", "rectb", \
|
||||
"spr", "btn", "btnp", "sfx", "map", "mget", "mset", "peek", "poke", "peek4", "poke4", \
|
||||
"memcpy", "memset", "trace", "pmem", "time", "exit", "font", "mouse", "circ", "circb", "tri", \
|
||||
"clip", "music", "sync"}
|
||||
|
||||
#define TIC_FONT_CHARS 128
|
||||
|
||||
enum
|
||||
{
|
||||
NoteNone = 0,
|
||||
NoteStop,
|
||||
NoteNone2,
|
||||
NoteNone3,
|
||||
NoteStart,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
tic_color_black, // 0
|
||||
tic_color_dark_red, // 1
|
||||
tic_color_dark_blue, // 2
|
||||
tic_color_dark_gray, // 3
|
||||
tic_color_brown, // 4
|
||||
tic_color_green, // 5
|
||||
tic_color_red, // 6
|
||||
tic_color_gray, // 7
|
||||
tic_color_blue, // 8
|
||||
tic_color_orange, // 9
|
||||
tic_color_light_blue, // 10
|
||||
tic_color_light_green, // 11
|
||||
tic_color_peach, // 12
|
||||
tic_color_cyan, // 13
|
||||
tic_color_yellow, // 14
|
||||
tic_color_white, // 15
|
||||
} tic_color;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_no_flip = 0b00,
|
||||
tic_horz_flip = 0b01,
|
||||
tic_vert_flip = 0b10,
|
||||
} tic_flip;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_no_rotate,
|
||||
tic_90_rotate,
|
||||
tic_180_rotate,
|
||||
tic_270_rotate,
|
||||
} tic_rotate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 start:4;
|
||||
u8 size:4;
|
||||
} tic_sound_loop;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
struct
|
||||
{
|
||||
u8 volume:4;
|
||||
u8 wave:4;
|
||||
u8 arpeggio:4;
|
||||
s8 pitch:4;
|
||||
} data[SFX_TICKS];
|
||||
|
||||
struct
|
||||
{
|
||||
u8 octave:3;
|
||||
u8 pitch16x:1; // pitch factor
|
||||
s8 speed:3;
|
||||
u8 reverse:1; // arpeggio reverse
|
||||
u8 note:4;
|
||||
u8 chain:1;
|
||||
u8 temp:3;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
tic_sound_loop wave;
|
||||
tic_sound_loop volume;
|
||||
tic_sound_loop arpeggio;
|
||||
tic_sound_loop pitch;
|
||||
};
|
||||
|
||||
tic_sound_loop loops[4];
|
||||
};
|
||||
|
||||
} tic_sound_effect;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[ENVELOPE_SIZE];
|
||||
}tic_waveform;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_waveform envelopes[ENVELOPES_COUNT];
|
||||
} tic_waveforms;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 note:4;
|
||||
u8 volume:4;
|
||||
u8 param:4;
|
||||
u8 effect:3;
|
||||
u8 sfxhi:1;
|
||||
u8 sfxlow:MUSIC_SFXID_LOW_BITS;
|
||||
u8 octave:3;
|
||||
} rows[MUSIC_PATTERN_ROWS];
|
||||
|
||||
} tic_track_pattern;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[MUSIC_FRAMES * TRACK_PATTERNS_SIZE]; // sfx - 6bits per channel = 24 bit
|
||||
|
||||
s8 tempo; // delta value, rel to 120 bpm * 10 [32-255]
|
||||
u8 rows; // delta value, rel to 64 rows, can be [1-64]
|
||||
s8 speed; // delta value, rel to 6 [1-31]
|
||||
|
||||
} tic_track;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_track_pattern data[MUSIC_PATTERNS];
|
||||
} tic_patterns;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_track data[MUSIC_TRACKS];
|
||||
} tic_tracks;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_waveforms waveform;
|
||||
tic_sound_effect data[SFX_COUNT];
|
||||
}tic_sfx;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_patterns patterns;
|
||||
tic_tracks tracks;
|
||||
}tic_music;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s8 track;
|
||||
s8 frame;
|
||||
s8 row;
|
||||
|
||||
struct
|
||||
{
|
||||
bool loop:1;
|
||||
} flag;
|
||||
|
||||
} tic_music_pos;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
u16 freq:12;
|
||||
u16 volume:4;
|
||||
};
|
||||
|
||||
tic_waveform waveform;
|
||||
} tic_sound_register;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[TIC_MAP_WIDTH * TIC_MAP_HEIGHT];
|
||||
} tic_map;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[TIC_SPRITESIZE * TIC_SPRITESIZE * TIC_PALETTE_BPP / BITS_IN_BYTE];
|
||||
} tic_tile;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_tile tiles[TIC_BANK_SPRITES];
|
||||
tic_tile sprites[TIC_BANK_SPRITES];
|
||||
tic_map map;
|
||||
} tic_gfx;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char data[TIC_CODE_SIZE];
|
||||
} tic_code;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data [TIC80_WIDTH * TIC80_HEIGHT * sizeof(u32)];
|
||||
s32 size;
|
||||
} tic_cover_image;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 r;
|
||||
u8 g;
|
||||
u8 b;
|
||||
} tic_rgb;
|
||||
|
||||
typedef union
|
||||
{
|
||||
tic_rgb colors[TIC_PALETTE_SIZE];
|
||||
|
||||
u8 data[TIC_PALETTE_SIZE * sizeof(tic_rgb)];
|
||||
} tic_palette;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_sfx sfx;
|
||||
tic_music music;
|
||||
} tic_sound;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_gfx gfx;
|
||||
tic_sound sound;
|
||||
tic_code code;
|
||||
tic_cover_image cover;
|
||||
tic_palette palette;
|
||||
} tic_cartridge;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[TIC_FONT_CHARS * BITS_IN_BYTE];
|
||||
} tic_font;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[TIC80_WIDTH * TIC80_HEIGHT * TIC_PALETTE_BPP / BITS_IN_BYTE];
|
||||
} tic_screen;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
tic_screen screen;
|
||||
tic_palette palette;
|
||||
u8 mapping[TIC_PALETTE_SIZE * TIC_PALETTE_BPP / BITS_IN_BYTE];
|
||||
|
||||
struct
|
||||
{
|
||||
u8 border;
|
||||
|
||||
struct
|
||||
{
|
||||
s8 x;
|
||||
s8 y;
|
||||
} offset;
|
||||
|
||||
union
|
||||
{
|
||||
u8 cursor;
|
||||
tic80_gamepad mask;
|
||||
};
|
||||
} vars;
|
||||
|
||||
struct
|
||||
{
|
||||
tic80_input gamepad;
|
||||
u8 reserved[2];
|
||||
} input;
|
||||
};
|
||||
|
||||
u8 data[TIC_VRAM_SIZE];
|
||||
} tic_vram;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s32 data[TIC_PERSISTENT_SIZE];
|
||||
} tic_persistent;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
tic_vram vram;
|
||||
tic_gfx gfx;
|
||||
tic_persistent persistent;
|
||||
tic_sound_register registers[TIC_SOUND_CHANNELS];
|
||||
tic_sound sound;
|
||||
tic_music_pos music_pos;
|
||||
};
|
||||
|
||||
u8 data[TIC_RAM_SIZE];
|
||||
} tic_ram;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_gamepad_input,
|
||||
tic_mouse_input,
|
||||
} tic_input_method;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_script_lua,
|
||||
tic_script_moon,
|
||||
tic_script_js,
|
||||
} tic_script_lang;
|
||||
182
src/tic80.c
Normal file
182
src/tic80.c
Normal file
File diff suppressed because one or more lines are too long
137
src/ticapi.h
Normal file
137
src/ticapi.h
Normal file
@@ -0,0 +1,137 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tic.h"
|
||||
|
||||
typedef struct { u8 index; tic_flip flip; tic_rotate rotate; } RemapResult;
|
||||
typedef void(*RemapFunc)(void*, s32 x, s32 y, RemapResult* result);
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
s8 wave;
|
||||
s8 volume;
|
||||
s8 arpeggio;
|
||||
s8 pitch;
|
||||
};
|
||||
|
||||
s8 data[4];
|
||||
};
|
||||
} tic_sfx_pos;
|
||||
|
||||
typedef void(*TraceOutput)(void*, const char*, u8 color);
|
||||
typedef void(*ErrorOutput)(void*, const char*);
|
||||
typedef void(*ExitCallback)(void*);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
TraceOutput trace;
|
||||
ErrorOutput error;
|
||||
ExitCallback exit;
|
||||
|
||||
u64 (*counter)();
|
||||
u64 (*freq)();
|
||||
u64 start;
|
||||
|
||||
void* data;
|
||||
} tic_tick_data;
|
||||
|
||||
typedef struct tic_mem tic_mem;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s32 (*draw_char) (tic_mem* memory, u8 symbol, s32 x, s32 y, u8 color);
|
||||
s32 (*text) (tic_mem* memory, const char* text, s32 x, s32 y, u8 color);
|
||||
s32 (*fixed_text) (tic_mem* memory, const char* text, s32 x, s32 y, u8 color);
|
||||
s32 (*text_ex) (tic_mem* memory, const char* text, s32 x, s32 y, u8 color, bool fixed, s32 scale);
|
||||
void (*clear) (tic_mem* memory, u8 color);
|
||||
void (*pixel) (tic_mem* memory, s32 x, s32 y, u8 color);
|
||||
u8 (*get_pixel) (tic_mem* memory, s32 x, s32 y);
|
||||
void (*line) (tic_mem* memory, s32 x1, s32 y1, s32 x2, s32 y2, u8 color);
|
||||
void (*rect) (tic_mem* memory, s32 x, s32 y, s32 width, s32 height, u8 color);
|
||||
void (*rect_border) (tic_mem* memory, s32 x, s32 y, s32 width, s32 height, u8 color);
|
||||
void (*sprite) (tic_mem* memory, const tic_gfx* src, s32 index, s32 x, s32 y, u8* colors, s32 count);
|
||||
void (*sprite_ex) (tic_mem* memory, const tic_gfx* src, s32 index, s32 x, s32 y, s32 w, s32 h, u8* colors, s32 count, s32 scale, tic_flip flip, tic_rotate rotate);
|
||||
void (*map) (tic_mem* memory, const tic_gfx* src, s32 x, s32 y, s32 width, s32 height, s32 sx, s32 sy, u8 chromakey, s32 scale);
|
||||
void (*remap) (tic_mem* memory, const tic_gfx* src, s32 x, s32 y, s32 width, s32 height, s32 sx, s32 sy, u8 chromakey, s32 scale, RemapFunc remap, void* data);
|
||||
void (*map_set) (tic_mem* memory, tic_gfx* src, s32 x, s32 y, u8 value);
|
||||
u8 (*map_get) (tic_mem* memory, const tic_gfx* src, s32 x, s32 y);
|
||||
void (*circle) (tic_mem* memory, s32 x, s32 y, u32 radius, u8 color);
|
||||
void (*circle_border) (tic_mem* memory, s32 x, s32 y, u32 radius, u8 color);
|
||||
void (*tri) (tic_mem* memory, s32 x1, s32 y1, s32 x2, s32 y2, s32 x3, s32 y3, u8 color);
|
||||
void (*clip) (tic_mem* memory, s32 x, s32 y, s32 width, s32 height);
|
||||
void (*sfx) (tic_mem* memory, s32 index, s32 note, s32 octave, s32 duration, s32 channel);
|
||||
void (*sfx_stop) (tic_mem* memory, s32 channel);
|
||||
void (*sfx_ex) (tic_mem* memory, s32 index, s32 note, s32 octave, s32 duration, s32 channel, s32 volume, s32 speed);
|
||||
tic_sfx_pos (*sfx_pos) (tic_mem* memory, s32 channel);
|
||||
void (*music) (tic_mem* memory, s32 track, s32 frame, s32 row, bool loop);
|
||||
void (*music_frame) (tic_mem* memory, s32 track, s32 frame, s32 row, bool loop);
|
||||
double (*time) (tic_mem* memory);
|
||||
void (*tick) (tic_mem* memory, tic_tick_data* data);
|
||||
void (*scanline) (tic_mem* memory, s32 row);
|
||||
void (*reset) (tic_mem* memory);
|
||||
void (*pause) (tic_mem* memory);
|
||||
void (*resume) (tic_mem* memory);
|
||||
void (*sync) (tic_mem* memory, bool toCart);
|
||||
u32 (*btnp) (tic_mem* memory, s32 id, s32 hold, s32 period);
|
||||
|
||||
void (*load) (tic_cartridge* rom, const u8* buffer, s32 size, bool palette);
|
||||
s32 (*save) (const tic_cartridge* rom, u8* buffer);
|
||||
|
||||
void (*tick_start) (tic_mem* memory, const tic_sound* src);
|
||||
void (*tick_end) (tic_mem* memory);
|
||||
|
||||
tic_script_lang (*get_script)(tic_mem* memory);
|
||||
} tic_api;
|
||||
|
||||
struct tic_mem
|
||||
{
|
||||
tic_ram ram;
|
||||
tic_cartridge cart;
|
||||
tic_cartridge config;
|
||||
tic_input_method input;
|
||||
tic_script_lang script;
|
||||
tic_font font;
|
||||
tic_api api;
|
||||
|
||||
char saveid[TIC_SAVEID_SIZE];
|
||||
|
||||
struct
|
||||
{
|
||||
s16* buffer;
|
||||
s32 size;
|
||||
} samples;
|
||||
};
|
||||
|
||||
tic_mem* tic_create(s32 samplerate);
|
||||
void tic_close(tic_mem* memory);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic80 tic;
|
||||
tic_mem* memory;
|
||||
tic_tick_data tickData;
|
||||
} tic80_local;
|
||||
107
src/tools.c
Normal file
107
src/tools.c
Normal file
@@ -0,0 +1,107 @@
|
||||
// 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 "tools.h"
|
||||
#include "ext/gif.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void tic_tool_poke4(void* addr, u32 index, u8 value)
|
||||
{
|
||||
u8* val = (u8*)addr + (index >> 1);
|
||||
|
||||
if(index & 1)
|
||||
{
|
||||
*val &= 0x0f;
|
||||
*val |= (value << 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
*val &= 0xf0;
|
||||
*val |= value & 0x0f;
|
||||
}
|
||||
}
|
||||
|
||||
u8 tic_tool_peek4(const void* addr, u32 index)
|
||||
{
|
||||
u8 val = ((u8*)addr)[index >> 1];
|
||||
|
||||
return index & 1 ? val >> 4 : val & 0xf;
|
||||
}
|
||||
|
||||
s32 tic_tool_get_pattern_id(const tic_track* track, s32 frame, s32 channel)
|
||||
{
|
||||
u32 patternData = 0;
|
||||
for(s32 b = 0; b < TRACK_PATTERNS_SIZE; b++)
|
||||
patternData |= track->data[frame * TRACK_PATTERNS_SIZE + b] << (BITS_IN_BYTE * b);
|
||||
|
||||
return (patternData >> (channel * TRACK_PATTERN_BITS)) & TRACK_PATTERN_MASK;
|
||||
}
|
||||
|
||||
bool tic_tool_parse_note(const char* noteStr, s32* note, s32* octave)
|
||||
{
|
||||
if(noteStr && strlen(noteStr) == 3)
|
||||
{
|
||||
static const char* Notes[] = SFX_NOTES;
|
||||
|
||||
for(s32 i = 0; i < COUNT_OF(Notes); i++)
|
||||
{
|
||||
if(memcmp(Notes[i], noteStr, 2) == 0)
|
||||
{
|
||||
*note = i;
|
||||
*octave = noteStr[2] - '1';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 tic_tool_find_closest_color(const tic_rgb* palette, const tic_rgb* color)
|
||||
{
|
||||
u32 minDst = -1;
|
||||
u32 closetColor = 0;
|
||||
|
||||
enum{Size = TIC_PALETTE_SIZE};
|
||||
|
||||
for (s32 i = 0; i < Size; i++)
|
||||
{
|
||||
const tic_rgb* rgb = palette + i;
|
||||
|
||||
s32 r = color->r - rgb->r;
|
||||
s32 g = color->g - rgb->g;
|
||||
s32 b = color->b - rgb->b;
|
||||
|
||||
u32 dst = r*r + g*g + b*b;
|
||||
|
||||
if (dst < minDst)
|
||||
{
|
||||
minDst = dst;
|
||||
closetColor = i;
|
||||
}
|
||||
}
|
||||
|
||||
return closetColor;
|
||||
}
|
||||
32
src/tools.h
Normal file
32
src/tools.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tic.h"
|
||||
|
||||
void tic_tool_poke4(void* addr, u32 index, u8 value);
|
||||
u8 tic_tool_peek4(const void* addr, u32 index);
|
||||
bool tic_tool_parse_note(const char* noteStr, s32* note, s32* octave);
|
||||
s32 tic_tool_get_pattern_id(const tic_track* track, s32 frame, s32 channel);
|
||||
void tic_tool_set_pattern_id(tic_track* track, s32 frame, s32 channel, s32 id);
|
||||
u32 tic_tool_find_closest_color(const tic_rgb* palette, const tic_rgb* color);
|
||||
133
src/world.c
Normal file
133
src/world.c
Normal file
@@ -0,0 +1,133 @@
|
||||
// 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 "world.h"
|
||||
#include "map.h"
|
||||
|
||||
#define PREVIEW_SIZE (TIC80_WIDTH * TIC80_HEIGHT * TIC_PALETTE_BPP / BITS_IN_BYTE)
|
||||
|
||||
static void drawGrid(World* world)
|
||||
{
|
||||
Map* map = world->map;
|
||||
u8 color = systemColor(tic_color_light_blue);
|
||||
|
||||
for(s32 c = 0; c < TIC80_WIDTH; c += TIC_MAP_SCREEN_WIDTH)
|
||||
world->tic->api.line(world->tic, c, 0, c, TIC80_HEIGHT, color);
|
||||
|
||||
for(s32 r = 0; r < TIC80_HEIGHT; r += TIC_MAP_SCREEN_HEIGHT)
|
||||
world->tic->api.line(world->tic, 0, r, TIC80_WIDTH, r, color);
|
||||
|
||||
world->tic->api.rect_border(world->tic, 0, 0, TIC80_WIDTH, TIC80_HEIGHT, color);
|
||||
|
||||
SDL_Rect rect = {0, 0, TIC80_WIDTH, TIC80_HEIGHT};
|
||||
|
||||
if(checkMousePos(&rect))
|
||||
{
|
||||
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
|
||||
s32 mx = getMouseX();
|
||||
s32 my = getMouseY();
|
||||
|
||||
if(checkMouseDown(&rect, SDL_BUTTON_LEFT))
|
||||
{
|
||||
map->scroll.x = (mx - TIC_MAP_SCREEN_WIDTH/2) * TIC_SPRITESIZE;
|
||||
map->scroll.y = (my - TIC_MAP_SCREEN_HEIGHT/2) * TIC_SPRITESIZE;
|
||||
}
|
||||
|
||||
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||||
setStudioMode(TIC_MAP_MODE);
|
||||
}
|
||||
|
||||
world->tic->api.rect_border(world->tic, map->scroll.x / TIC_SPRITESIZE, map->scroll.y / TIC_SPRITESIZE,
|
||||
TIC_MAP_SCREEN_WIDTH+1, TIC_MAP_SCREEN_HEIGHT+1, systemColor(tic_color_red));
|
||||
}
|
||||
|
||||
static void processKeydown(World* world, SDL_Keycode keycode)
|
||||
{
|
||||
switch(keycode)
|
||||
{
|
||||
case SDLK_TAB: setStudioMode(TIC_MAP_MODE); break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tick(World* world)
|
||||
{
|
||||
SDL_Event* event = NULL;
|
||||
while ((event = pollEvent()))
|
||||
{
|
||||
switch(event->type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
processKeydown(world, event->key.keysym.sym);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_memcpy(&world->tic->ram.vram, world->preview, PREVIEW_SIZE);
|
||||
|
||||
drawGrid(world);
|
||||
}
|
||||
|
||||
void initWorld(World* world, tic_mem* tic, Map* map)
|
||||
{
|
||||
if(!world->preview)
|
||||
world->preview = SDL_malloc(PREVIEW_SIZE);
|
||||
|
||||
*world = (World)
|
||||
{
|
||||
.tic = tic,
|
||||
.map = map,
|
||||
.tick = tick,
|
||||
.preview = world->preview,
|
||||
};
|
||||
|
||||
SDL_memset(world->preview, 0, PREVIEW_SIZE);
|
||||
s32 colors[TIC_PALETTE_SIZE];
|
||||
|
||||
for(s32 i = 0; i < TIC80_WIDTH * TIC80_HEIGHT; i++)
|
||||
{
|
||||
u8 index = tic->cart.gfx.map.data[i];
|
||||
|
||||
if(index)
|
||||
{
|
||||
SDL_memset(colors, 0, sizeof colors);
|
||||
|
||||
tic_tile* tile = &tic->cart.gfx.tiles[index];
|
||||
|
||||
for(s32 p = 0; p < TIC_SPRITESIZE * TIC_SPRITESIZE; p++)
|
||||
{
|
||||
u8 color = tic_tool_peek4(tile, p);
|
||||
|
||||
if(color)
|
||||
colors[color]++;
|
||||
}
|
||||
|
||||
s32 max = 0;
|
||||
|
||||
for(s32 c = 0; c < SDL_arraysize(colors); c++)
|
||||
if(colors[c] > colors[max]) max = c;
|
||||
|
||||
tic_tool_poke4(world->preview, i, max);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
40
src/world.h
Normal file
40
src/world.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "studio.h"
|
||||
|
||||
typedef struct Map Map;
|
||||
typedef struct World World;
|
||||
|
||||
struct World
|
||||
{
|
||||
tic_mem* tic;
|
||||
Map* map;
|
||||
|
||||
void* preview;
|
||||
|
||||
void(*tick)(World* world);
|
||||
};
|
||||
|
||||
void initWorld(World* world, tic_mem* tic, Map* map);
|
||||
Reference in New Issue
Block a user