.50 version
This commit is contained in:
@@ -83,6 +83,7 @@ static void drawCode(Code* code, bool withCursor)
|
||||
|
||||
if(code->cursor.selection && pointer >= selection.start && pointer < selection.end)
|
||||
code->tic->api.rect(code->tic, x-1, y-1, TIC_FONT_WIDTH+1, TIC_FONT_HEIGHT+1, getConfig()->theme.code.select);
|
||||
else code->tic->api.draw_char(code->tic, symbol, x+1, y+1, 0);
|
||||
|
||||
code->tic->api.draw_char(code->tic, symbol, x, y, *colorPointer);
|
||||
|
||||
@@ -143,9 +144,9 @@ static s32 getLinesCount(Code* code)
|
||||
|
||||
static void removeInvalidChars(char* code)
|
||||
{
|
||||
// remove \r symbol
|
||||
char* s; char* d;
|
||||
for(s = d = code; (*d = *s); d += (*s++ != '\r'));
|
||||
// remove \r symbol
|
||||
char* s; char* d;
|
||||
for(s = d = code; (*d = *s); d += (*s++ != '\r'));
|
||||
}
|
||||
|
||||
static void updateEditor(Code* code)
|
||||
@@ -647,7 +648,7 @@ static void pageDown(Code* code)
|
||||
s32 line = 0;
|
||||
getCursorPosition(code, &column, &line);
|
||||
s32 lines = getLinesCount(code);
|
||||
setCursorPosition(code, column, line < lines - TEXT_BUFFER_HEIGHT ? line + TEXT_BUFFER_HEIGHT : lines);
|
||||
setCursorPosition(code, column, line < lines - TEXT_BUFFER_HEIGHT ? line + TEXT_BUFFER_HEIGHT : lines);
|
||||
}
|
||||
|
||||
static bool replaceSelection(Code* code)
|
||||
|
||||
13
src/config.c
13
src/config.c
@@ -56,6 +56,16 @@ static void readConfigCheckNewVersion(Config* config, lua_State* lua)
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
static void readConfigNoSound(Config* config, lua_State* lua)
|
||||
{
|
||||
lua_getglobal(lua, "NO_SOUND");
|
||||
|
||||
if(lua_isboolean(lua, -1))
|
||||
config->data.noSound = lua_toboolean(lua, -1);
|
||||
|
||||
lua_pop(lua, 1);
|
||||
}
|
||||
|
||||
static void readCursorTheme(Config* config, lua_State* lua)
|
||||
{
|
||||
lua_getfield(lua, -1, "CURSOR");
|
||||
@@ -156,6 +166,7 @@ static void readConfig(Config* config)
|
||||
readConfigVideoLength(config, lua);
|
||||
readConfigVideoScale(config, lua);
|
||||
readConfigCheckNewVersion(config, lua);
|
||||
readConfigNoSound(config, lua);
|
||||
readTheme(config, lua);
|
||||
}
|
||||
|
||||
@@ -173,6 +184,8 @@ static void update(Config* config, const u8* buffer, size_t size)
|
||||
|
||||
static void setDefault(Config* config)
|
||||
{
|
||||
SDL_memset(&config->data, 0, sizeof(StudioConfig));
|
||||
|
||||
{
|
||||
static const u8 DefaultBiosZip[] =
|
||||
{
|
||||
|
||||
603
src/console.c
603
src/console.c
@@ -40,7 +40,7 @@
|
||||
#define CONSOLE_BUFFER_SCREENS 64
|
||||
#define CONSOLE_BUFFER_SIZE (CONSOLE_BUFFER_WIDTH * CONSOLE_BUFFER_HEIGHT * CONSOLE_BUFFER_SCREENS)
|
||||
|
||||
#if defined(__WINDOWS__) || (defined(__LINUX__) && !defined(__ARM_LINUX__)) || defined(__MACOSX__)
|
||||
#if defined(__WINDOWS__) || defined(__LINUX__) || defined(__MACOSX__)
|
||||
#define CAN_EXPORT 1
|
||||
#endif
|
||||
|
||||
@@ -62,27 +62,30 @@ static struct
|
||||
.fast = false,
|
||||
};
|
||||
|
||||
static const char CartExt[] = ".tic";
|
||||
|
||||
static const char DefaultLuaTicPath[] = TIC_LOCAL "default.tic";
|
||||
static const char DefaultMoonTicPath[] = TIC_LOCAL "default_moon.tic";
|
||||
static const char DefaultJSTicPath[] = TIC_LOCAL "default_js.tic";
|
||||
|
||||
static const char* getRomName(const char* name)
|
||||
static const char* getName(const char* name, const char* ext)
|
||||
{
|
||||
static char path[FILENAME_MAX];
|
||||
|
||||
strcpy(path, name);
|
||||
|
||||
size_t ps = strlen(path);
|
||||
size_t es = strlen(CartExt);
|
||||
size_t es = strlen(ext);
|
||||
|
||||
if(!(ps > es && strstr(path, CartExt) + es == path + ps))
|
||||
strcat(path, CartExt);
|
||||
if(!(ps > es && strstr(path, ext) + es == path + ps))
|
||||
strcat(path, ext);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static const char* getCartName(const char* name)
|
||||
{
|
||||
return getName(name, CART_EXT);
|
||||
}
|
||||
|
||||
static void scrollBuffer(char* buffer)
|
||||
{
|
||||
memmove(buffer, buffer + CONSOLE_BUFFER_WIDTH, CONSOLE_BUFFER_SIZE - CONSOLE_BUFFER_WIDTH);
|
||||
@@ -288,7 +291,7 @@ static s32 writeGifData(const tic_mem* tic, u8* dst, const u8* src, s32 width, s
|
||||
|
||||
if(palette)
|
||||
{
|
||||
const tic_rgb* pal = tic->ram.vram.palette.colors;
|
||||
const tic_rgb* pal = tic->cart.palette.colors;
|
||||
for(s32 i = 0; i < TIC_PALETTE_SIZE; i++, pal++)
|
||||
palette[i].r = pal->r, palette[i].g = pal->g, palette[i].b = pal->b;
|
||||
|
||||
@@ -337,13 +340,13 @@ static bool onConsoleLoadSectionCommand(Console* console, const char* param)
|
||||
|
||||
for(s32 i = 0; i < COUNT_OF(Sections); i++)
|
||||
{
|
||||
sprintf(buf, "%s %s", CartExt, Sections[i]);
|
||||
sprintf(buf, "%s %s", CART_EXT, Sections[i]);
|
||||
char* pos = SDL_strstr(param, buf);
|
||||
|
||||
if(pos)
|
||||
{
|
||||
pos[sizeof(CartExt) - 1] = 0;
|
||||
const char* name = getRomName(param);
|
||||
pos[sizeof(CART_EXT) - 1] = 0;
|
||||
const char* name = getCartName(param);
|
||||
s32 size = 0;
|
||||
void* data = fsLoadFile(console->fs, name, &size);
|
||||
|
||||
@@ -475,7 +478,7 @@ static void onConsoleLoadDemoCommandConfirmed(Console* console, const char* para
|
||||
else if(strcmp(param, DefaultJSTicPath) == 0)
|
||||
data = getDemoCart(console, tic_script_js, &size);
|
||||
|
||||
const char* name = getRomName(param);
|
||||
const char* name = getCartName(param);
|
||||
|
||||
strcpy(console->romName, name);
|
||||
|
||||
@@ -490,6 +493,302 @@ static void onConsoleLoadDemoCommandConfirmed(Console* console, const char* para
|
||||
SDL_free(data);
|
||||
}
|
||||
|
||||
static void onCartLoaded(Console* console, const char* name)
|
||||
{
|
||||
strcpy(console->romName, name);
|
||||
|
||||
studioRomLoaded();
|
||||
|
||||
printBack(console, "\ncart ");
|
||||
printFront(console, console->romName);
|
||||
printBack(console, " loaded!\nuse ");
|
||||
printFront(console, "RUN");
|
||||
printBack(console, " command to run it\n");
|
||||
|
||||
}
|
||||
|
||||
static bool hasExt(const char* name, const char* ext)
|
||||
{
|
||||
return strcmp(name + strlen(name) - strlen(ext), ext) == 0;
|
||||
}
|
||||
|
||||
#if defined(TIC80_PRO)
|
||||
|
||||
static bool hasProjectExt(const char* name)
|
||||
{
|
||||
return hasExt(name, PROJECT_LUA_EXT) || hasExt(name, PROJECT_MOON_EXT) || hasExt(name, PROJECT_JS_EXT);
|
||||
}
|
||||
|
||||
static const char* projectComment(const char* name)
|
||||
{
|
||||
return hasExt(name, PROJECT_JS_EXT) ? "//" : "--";
|
||||
}
|
||||
|
||||
static void buf2str(const void* data, s32 size, char* ptr, bool flip)
|
||||
{
|
||||
enum {Len = 2};
|
||||
|
||||
for(s32 i = 0; i < size; i++, ptr+=Len)
|
||||
{
|
||||
sprintf(ptr, "%02x", ((u8*)data)[i]);
|
||||
|
||||
if(flip)
|
||||
{
|
||||
char tmp = ptr[0];
|
||||
ptr[0] = ptr[1];
|
||||
ptr[1] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool bufferEmpty(const u8* data, s32 size)
|
||||
{
|
||||
for(s32 i = 0; i < size; i++)
|
||||
if(*data++)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static char* saveTextSection(char* ptr, const char* data)
|
||||
{
|
||||
if(strlen(data) == 0)
|
||||
return ptr;
|
||||
|
||||
sprintf(ptr, "%s\n", data);
|
||||
ptr += strlen(ptr);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static char* saveBinaryBuffer(char* ptr, const char* comment, const void* data, s32 size, s32 row, bool flip)
|
||||
{
|
||||
if(bufferEmpty(data, size))
|
||||
return ptr;
|
||||
|
||||
sprintf(ptr, "%s %03i:", comment, row);
|
||||
ptr += strlen(ptr);
|
||||
|
||||
buf2str(data, size, ptr, flip);
|
||||
ptr += strlen(ptr);
|
||||
|
||||
sprintf(ptr, "\n");
|
||||
ptr += strlen(ptr);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static char* saveBinarySection(char* ptr, const char* comment, const char* tag, s32 count, const void* data, s32 size, bool flip)
|
||||
{
|
||||
if(bufferEmpty(data, size * count))
|
||||
return ptr;
|
||||
|
||||
sprintf(ptr, "%s <%s>\n", comment, tag);
|
||||
ptr += strlen(ptr);
|
||||
|
||||
for(s32 i = 0; i < count; i++, data = (u8*)data + size)
|
||||
ptr = saveBinaryBuffer(ptr, comment, data, size, i, flip);
|
||||
|
||||
sprintf(ptr, "%s </%s>\n\n", comment, tag);
|
||||
ptr += strlen(ptr);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
typedef struct {char* tag; s32 count; s32 offset; s32 size; bool flip;} BinarySection;
|
||||
static const BinarySection BinarySections[] =
|
||||
{
|
||||
{"PALETTE", 1, offsetof(tic_cartridge, palette.data), sizeof(tic_palette), false},
|
||||
{"TILES", TIC_BANK_SPRITES, offsetof(tic_cartridge, gfx.tiles), sizeof(tic_tile), true},
|
||||
{"SPRITES", TIC_BANK_SPRITES, offsetof(tic_cartridge, gfx.sprites), sizeof(tic_tile), true},
|
||||
{"MAP", TIC_MAP_HEIGHT, offsetof(tic_cartridge, gfx.map), TIC_MAP_WIDTH, true},
|
||||
{"WAVES", ENVELOPES_COUNT, offsetof(tic_cartridge,sound.sfx.waveform.envelopes), sizeof(tic_waveform), true},
|
||||
{"SFX", SFX_COUNT, offsetof(tic_cartridge, sound.sfx.data), sizeof(tic_sound_effect), true},
|
||||
{"PATTERNS", MUSIC_PATTERNS, offsetof(tic_cartridge, sound.music.patterns), sizeof(tic_track_pattern), true},
|
||||
{"TRACKS", MUSIC_TRACKS, offsetof(tic_cartridge, sound.music.tracks), sizeof(tic_track), true},
|
||||
};
|
||||
|
||||
static s32 saveProject(Console* console, void* buffer, const char* comment)
|
||||
{
|
||||
tic_mem* tic = console->tic;
|
||||
|
||||
char* stream = buffer;
|
||||
char* ptr = saveTextSection(stream, tic->cart.code.data);
|
||||
|
||||
for(s32 i = 0; i < COUNT_OF(BinarySections); i++)
|
||||
{
|
||||
const BinarySection* section = &BinarySections[i];
|
||||
ptr = saveBinarySection(ptr, comment, section->tag, section->count, (u8*)&tic->cart + section->offset, section->size, section->flip);
|
||||
}
|
||||
|
||||
saveBinarySection(ptr, comment, "COVER", 1, &tic->cart.cover, tic->cart.cover.size + sizeof(s32), true);
|
||||
|
||||
return strlen(stream);
|
||||
}
|
||||
|
||||
static bool loadTextSection(const char* project, const char* comment, void* dst, s32 size)
|
||||
{
|
||||
bool done = false;
|
||||
|
||||
const char* start = project;
|
||||
const char* end = project + strlen(project);
|
||||
|
||||
{
|
||||
char tagbuf[64];
|
||||
|
||||
for(s32 i = 0; i < COUNT_OF(BinarySections); i++)
|
||||
{
|
||||
sprintf(tagbuf, "\n%s <%s>\n", comment, BinarySections[i].tag);
|
||||
|
||||
const char* ptr = SDL_strstr(project, tagbuf);
|
||||
|
||||
if(ptr && ptr < end)
|
||||
end = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(end > start)
|
||||
{
|
||||
SDL_memcpy(dst, start, SDL_min(size, end - start));
|
||||
done = true;
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
static bool loadBinarySection(const char* project, const char* comment, const char* tag, s32 count, void* dst, s32 size, bool flip)
|
||||
{
|
||||
char tagbuf[64];
|
||||
sprintf(tagbuf, "%s <%s>\n", comment, tag);
|
||||
|
||||
const char* start = SDL_strstr(project, tagbuf);
|
||||
bool done = false;
|
||||
|
||||
if(start)
|
||||
{
|
||||
start += strlen(tagbuf);
|
||||
|
||||
sprintf(tagbuf, "\n%s </%s>", comment, tag);
|
||||
const char* end = SDL_strstr(start, tagbuf);
|
||||
|
||||
if(end > start)
|
||||
{
|
||||
const char* ptr = start;
|
||||
|
||||
if(size > 0)
|
||||
{
|
||||
while(ptr < end)
|
||||
{
|
||||
static char lineStr[] = "999";
|
||||
memcpy(lineStr, ptr + sizeof("-- ") - 1, sizeof lineStr - 1);
|
||||
|
||||
s32 index = SDL_atoi(lineStr);
|
||||
|
||||
if(index < count)
|
||||
{
|
||||
ptr += sizeof("-- 999:") - 1;
|
||||
str2buf(ptr, size*2, (u8*)dst + size*index, flip);
|
||||
ptr += size*2 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr += sizeof("-- 999:") - 1;
|
||||
str2buf(ptr, end - ptr, (u8*)dst, flip);
|
||||
}
|
||||
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
static bool loadProject(Console* console, const char* name, const char* data, s32 size, tic_cartridge* dst)
|
||||
{
|
||||
tic_mem* tic = console->tic;
|
||||
|
||||
char* project = (char*)SDL_malloc(size+1);
|
||||
|
||||
bool done = false;
|
||||
|
||||
if(project)
|
||||
{
|
||||
SDL_memcpy(project, data, size);
|
||||
project[size] = '\0';
|
||||
|
||||
// remove all the '\r' chars
|
||||
{
|
||||
char *s, *d;
|
||||
for(s = d = project; (*d = *s); d += (*s++ != '\r'));
|
||||
}
|
||||
|
||||
tic_cartridge* cart = (tic_cartridge*)SDL_malloc(sizeof(tic_cartridge));
|
||||
|
||||
if(cart)
|
||||
{
|
||||
SDL_memset(cart, 0, sizeof(tic_cartridge));
|
||||
SDL_memcpy(&cart->palette, &tic->config.palette.data, sizeof(tic_palette));
|
||||
|
||||
const char* comment = projectComment(name);
|
||||
|
||||
if(loadTextSection(project, comment, cart->code.data, sizeof(tic_code)))
|
||||
done = true;
|
||||
|
||||
for(s32 i = 0; i < COUNT_OF(BinarySections); i++)
|
||||
{
|
||||
const BinarySection* section = &BinarySections[i];
|
||||
if(loadBinarySection(project, comment, section->tag, section->count, (u8*)cart + section->offset, section->size, section->flip))
|
||||
done = true;
|
||||
}
|
||||
|
||||
if(loadBinarySection(project, comment, "COVER", 1, &cart->cover, -1, true))
|
||||
done = true;
|
||||
|
||||
SDL_memcpy(dst, cart, sizeof(tic_cartridge));
|
||||
|
||||
SDL_free(cart);
|
||||
}
|
||||
|
||||
SDL_free(project);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
static void updateProject(Console* console)
|
||||
{
|
||||
tic_mem* tic = console->tic;
|
||||
|
||||
if(strlen(console->romName) && hasProjectExt(console->romName))
|
||||
{
|
||||
s32 size = 0;
|
||||
void* data = fsLoadFile(console->fs, console->romName, &size);
|
||||
|
||||
if(data)
|
||||
{
|
||||
tic_cartridge* cart = SDL_malloc(sizeof(tic_cartridge));
|
||||
|
||||
if(cart)
|
||||
{
|
||||
if(loadProject(console, console->romName, data, size, cart))
|
||||
{
|
||||
SDL_memcpy(&tic->cart, cart, sizeof(tic_cartridge));
|
||||
|
||||
studioRomLoaded();
|
||||
}
|
||||
|
||||
SDL_free(cart);
|
||||
}
|
||||
SDL_free(data);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void onConsoleLoadCommandConfirmed(Console* console, const char* param)
|
||||
{
|
||||
if(onConsoleLoadSectionCommand(console, param)) return;
|
||||
@@ -497,7 +796,7 @@ static void onConsoleLoadCommandConfirmed(Console* console, const char* param)
|
||||
if(param)
|
||||
{
|
||||
s32 size = 0;
|
||||
const char* name = getRomName(param);
|
||||
const char* name = getCartName(param);
|
||||
|
||||
void* data = strcmp(name, CONFIG_TIC_PATH) == 0
|
||||
? fsLoadRootFile(console->fs, name, &size)
|
||||
@@ -507,21 +806,40 @@ static void onConsoleLoadCommandConfirmed(Console* console, const char* param)
|
||||
{
|
||||
console->showGameMenu = fsIsInPublicDir(console->fs);
|
||||
|
||||
strcpy(console->romName, name);
|
||||
|
||||
loadRom(console->tic, data, size, true);
|
||||
|
||||
studioRomLoaded();
|
||||
|
||||
printBack(console, "\ncart ");
|
||||
printFront(console, console->romName);
|
||||
printBack(console, " loaded!\nuse ");
|
||||
printFront(console, "RUN");
|
||||
printBack(console, " command to run it\n");
|
||||
onCartLoaded(console, name);
|
||||
|
||||
SDL_free(data);
|
||||
}
|
||||
else printBack(console, "\ncart loading error");
|
||||
else
|
||||
{
|
||||
#if defined(TIC80_PRO)
|
||||
const char* name = getName(param, PROJECT_LUA_EXT);
|
||||
|
||||
if(!fsExistsFile(console->fs, name))
|
||||
name = getName(param, PROJECT_MOON_EXT);
|
||||
|
||||
if(!fsExistsFile(console->fs, name))
|
||||
name = getName(param, PROJECT_JS_EXT);
|
||||
|
||||
void* data = fsLoadFile(console->fs, name, &size);
|
||||
|
||||
if(data)
|
||||
{
|
||||
loadProject(console, name, data, size, &console->tic->cart);
|
||||
onCartLoaded(console, name);
|
||||
|
||||
SDL_free(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
printBack(console, "\ncart loading error");
|
||||
}
|
||||
#else
|
||||
printBack(console, "\ncart loading error");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else printBack(console, "\ncart name is missing");
|
||||
|
||||
@@ -676,6 +994,7 @@ typedef struct
|
||||
|
||||
static bool printFilename(const char* name, const char* info, s32 id, void* data, bool dir)
|
||||
{
|
||||
|
||||
PrintFileNameData* printData = data;
|
||||
Console* console = printData->console;
|
||||
|
||||
@@ -683,6 +1002,7 @@ static bool printFilename(const char* name, const char* info, s32 id, void* data
|
||||
|
||||
if(dir)
|
||||
{
|
||||
|
||||
printBack(console, "[");
|
||||
printBack(console, name);
|
||||
printBack(console, "]");
|
||||
@@ -819,18 +1139,24 @@ static void onConsoleInstallDemosCommand(Console* console, const char* param)
|
||||
#include "../bin/assets/tetris.tic.dat"
|
||||
};
|
||||
|
||||
static const u8 Benchmark[] =
|
||||
{
|
||||
#include "../bin/assets/benchmark.tic.dat"
|
||||
};
|
||||
|
||||
FileSystem* fs = console->fs;
|
||||
|
||||
static const struct {const char* name; const u8* data; s32 size;} Demos[] =
|
||||
{
|
||||
{"fire.tic", DemoFire, sizeof DemoFire},
|
||||
{"font.tic", DemoFont, sizeof DemoFont},
|
||||
{"music.tic", DemoMusic, sizeof DemoMusic},
|
||||
{"p3d.tic", DemoP3D, sizeof DemoP3D},
|
||||
{"palette.tic", DemoPalette, sizeof DemoPalette},
|
||||
{"quest.tic", GameQuest, sizeof GameQuest},
|
||||
{"sfx.tic", DemoSFX, sizeof DemoSFX},
|
||||
{"tetris.tic", GameTetris, sizeof GameTetris},
|
||||
{"fire.tic", DemoFire, sizeof DemoFire},
|
||||
{"font.tic", DemoFont, sizeof DemoFont},
|
||||
{"music.tic", DemoMusic, sizeof DemoMusic},
|
||||
{"p3d.tic", DemoP3D, sizeof DemoP3D},
|
||||
{"palette.tic", DemoPalette, sizeof DemoPalette},
|
||||
{"quest.tic", GameQuest, sizeof GameQuest},
|
||||
{"sfx.tic", DemoSFX, sizeof DemoSFX},
|
||||
{"tetris.tic", GameTetris, sizeof GameTetris},
|
||||
{"benchmark.tic", Benchmark, sizeof Benchmark},
|
||||
};
|
||||
|
||||
printBack(console, "\nadded carts:\n\n");
|
||||
@@ -852,12 +1178,26 @@ static void onConsoleSurfCommand(Console* console, const char* param)
|
||||
commandDone(console);
|
||||
}
|
||||
|
||||
static void onConsoleCodeCommand(Console* console, const char* param)
|
||||
{
|
||||
gotoCode();
|
||||
commandDone(console);
|
||||
}
|
||||
|
||||
|
||||
static void onConsoleKeymapCommand(Console* console, const char* param)
|
||||
{
|
||||
setStudioMode(TIC_KEYMAP_MODE);
|
||||
commandDone(console);
|
||||
}
|
||||
|
||||
static void onConsoleVersionCommand(Console* console, const char* param)
|
||||
{
|
||||
printBack(console, "\n");
|
||||
consolePrint(console, TIC_VERSION_LABEL, CONSOLE_BACK_TEXT_COLOR);
|
||||
commandDone(console);
|
||||
}
|
||||
|
||||
static void onConsoleConfigCommand(Console* console, const char* param)
|
||||
{
|
||||
if(param == NULL)
|
||||
@@ -891,13 +1231,6 @@ static void onConsoleConfigCommand(Console* console, const char* param)
|
||||
commandDone(console);
|
||||
}
|
||||
|
||||
static s32 saveRom(tic_mem* tic, void* buffer)
|
||||
{
|
||||
s32 size = tic->api.save(&tic->cart, buffer);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void onFileDownloaded(GetResult result, void* data)
|
||||
{
|
||||
Console* console = (Console*)data;
|
||||
@@ -1153,7 +1486,7 @@ static void exportMap(Console* console)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__EMSCRIPTEN__) || defined(__ARM_LINUX__)
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
|
||||
static void onConsoleExportCommand(Console* console, const char* param)
|
||||
{
|
||||
@@ -1231,6 +1564,8 @@ static void writeMemoryString(MemoryBuffer* memory, const char* str)
|
||||
|
||||
static void onConsoleExportHtmlCommand(Console* console, const char* name)
|
||||
{
|
||||
tic_mem* tic = console->tic;
|
||||
|
||||
char cartName[FILENAME_MAX];
|
||||
strcpy(cartName, name);
|
||||
|
||||
@@ -1272,43 +1607,46 @@ static void onConsoleExportHtmlCommand(Console* console, const char* name)
|
||||
{
|
||||
writeMemoryString(&output, "var cartridge = [");
|
||||
|
||||
s32 size = saveRom(console->tic, buffer);
|
||||
s32 size = tic->api.save(&tic->cart, buffer);
|
||||
|
||||
// zip buffer
|
||||
if(size)
|
||||
{
|
||||
unsigned long outSize = sizeof(tic_cartridge);
|
||||
u8* output = (u8*)SDL_malloc(outSize);
|
||||
// zip buffer
|
||||
{
|
||||
unsigned long outSize = sizeof(tic_cartridge);
|
||||
u8* output = (u8*)SDL_malloc(outSize);
|
||||
|
||||
compress2(output, &outSize, buffer, size, Z_BEST_COMPRESSION);
|
||||
SDL_free(buffer);
|
||||
|
||||
buffer = output;
|
||||
size = outSize;
|
||||
}
|
||||
|
||||
{
|
||||
u8* ptr = buffer;
|
||||
u8* end = ptr + size;
|
||||
|
||||
char value[] = "999,";
|
||||
while(ptr != end)
|
||||
{
|
||||
sprintf(value, "%i,", *ptr++);
|
||||
writeMemoryString(&output, value);
|
||||
}
|
||||
}
|
||||
|
||||
compress2(output, &outSize, buffer, size, Z_BEST_COMPRESSION);
|
||||
SDL_free(buffer);
|
||||
|
||||
buffer = output;
|
||||
size = outSize;
|
||||
writeMemoryString(&output, "];\n");
|
||||
|
||||
writeMemoryData(&output, EmbedTicJs, EmbedTicJsSize);
|
||||
writeMemoryString(&output, "</script>\n");
|
||||
|
||||
ptr += sizeof(Placeholder)-1;
|
||||
writeMemoryData(&output, ptr, EmbedIndexSize - (ptr - EmbedIndex));
|
||||
|
||||
fsGetFileData(onFileDownloaded, cartName, output.data, output.size, DEFAULT_CHMOD, console);
|
||||
}
|
||||
|
||||
{
|
||||
u8* ptr = buffer;
|
||||
u8* end = ptr + size;
|
||||
|
||||
char value[] = "999,";
|
||||
while(ptr != end)
|
||||
{
|
||||
sprintf(value, "%i,", *ptr++);
|
||||
writeMemoryString(&output, value);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_free(buffer);
|
||||
|
||||
writeMemoryString(&output, "];\n");
|
||||
|
||||
writeMemoryData(&output, EmbedTicJs, EmbedTicJsSize);
|
||||
writeMemoryString(&output, "</script>\n");
|
||||
|
||||
ptr += sizeof(Placeholder)-1;
|
||||
writeMemoryData(&output, ptr, EmbedIndexSize - (ptr - EmbedIndex));
|
||||
|
||||
fsGetFileData(onFileDownloaded, cartName, output.data, output.size, DEFAULT_CHMOD, console);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1321,6 +1659,8 @@ static void onConsoleExportHtmlCommand(Console* console, const char* name)
|
||||
|
||||
static void* embedCart(Console* console, s32* size)
|
||||
{
|
||||
tic_mem* tic = console->tic;
|
||||
|
||||
void* data = fsReadFile(console->appPath, size);
|
||||
|
||||
if(data)
|
||||
@@ -1330,13 +1670,15 @@ static void* embedCart(Console* console, s32* size)
|
||||
if(start)
|
||||
{
|
||||
embed.yes = true;
|
||||
memcpy(&embed.file, &console->tic->cart, sizeof(tic_cartridge));
|
||||
memcpy(start, &embed, sizeof(embed));
|
||||
SDL_memcpy(&embed.file, &tic->cart, sizeof(tic_cartridge));
|
||||
SDL_memcpy(start, &embed, sizeof(embed));
|
||||
embed.yes = false;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
return data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
@@ -1512,11 +1854,13 @@ static void onConsoleExportCommand(Console* console, const char* param)
|
||||
|
||||
static CartSaveResult saveCartName(Console* console, const char* name)
|
||||
{
|
||||
tic_mem* tic = console->tic;
|
||||
|
||||
bool success = false;
|
||||
|
||||
if(name && strlen(name))
|
||||
{
|
||||
u8* buffer = (u8*)SDL_malloc(sizeof(tic_cartridge));
|
||||
u8* buffer = (u8*)SDL_malloc(sizeof(tic_cartridge) * 3);
|
||||
|
||||
if(buffer)
|
||||
{
|
||||
@@ -1529,16 +1873,25 @@ static CartSaveResult saveCartName(Console* console, const char* name)
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 size = saveRom(console->tic, buffer);
|
||||
s32 size = 0;
|
||||
|
||||
name = getRomName(name);
|
||||
#if defined(TIC80_PRO)
|
||||
if(hasProjectExt(name))
|
||||
{
|
||||
size = saveProject(console, buffer, projectComment(name));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
name = getCartName(name);
|
||||
size = tic->api.save(&tic->cart, buffer);
|
||||
}
|
||||
|
||||
if(size && fsSaveFile(console->fs, name, buffer, size, true))
|
||||
{
|
||||
strcpy(console->romName, name);
|
||||
success = true;
|
||||
studioRomSaved();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1579,7 +1932,7 @@ static void onConsoleSaveCommandConfirmed(Console* console, const char* param)
|
||||
|
||||
static void onConsoleSaveCommand(Console* console, const char* param)
|
||||
{
|
||||
if(param && strlen(param) && fsExistsFile(console->fs, getRomName(param)))
|
||||
if(param && strlen(param) && fsExistsFile(console->fs, getCartName(param)))
|
||||
{
|
||||
static const char* Rows[] =
|
||||
{
|
||||
@@ -1761,32 +2114,28 @@ static void onConsoleRamCommand(Console* console, const char* param)
|
||||
"\n| ADDR | INFO | SIZE |" \
|
||||
"\n+-------+-------------------+-------+");
|
||||
|
||||
#define ADDR_RECORD(addr, name) {(s32)((u8*)&addr - (u8*)console->tic), name}
|
||||
|
||||
const struct{s32 addr; const char* info;} Layout[] =
|
||||
static const struct{s32 addr; const char* info;} Layout[] =
|
||||
{
|
||||
ADDR_RECORD(console->tic->ram.vram.screen, "SCREEN"),
|
||||
ADDR_RECORD(console->tic->ram.vram.palette, "PALETTE"),
|
||||
ADDR_RECORD(console->tic->ram.vram.mapping, "PALETTE MAP"),
|
||||
ADDR_RECORD(console->tic->ram.vram.vars.border, "BORDER COLOR"),
|
||||
ADDR_RECORD(console->tic->ram.vram.vars.offset, "SCREEN OFFSET"),
|
||||
ADDR_RECORD(console->tic->ram.vram.vars.mask, "GAMEPAD MASK"),
|
||||
ADDR_RECORD(console->tic->ram.vram.input.gamepad, "GAMEPAD"),
|
||||
ADDR_RECORD(console->tic->ram.vram.input.reserved, "..."),
|
||||
ADDR_RECORD(console->tic->ram.gfx.tiles, "SPRITES"),
|
||||
ADDR_RECORD(console->tic->ram.gfx.map, "MAP"),
|
||||
ADDR_RECORD(console->tic->ram.persistent, "PERSISTENT MEMORY"),
|
||||
ADDR_RECORD(console->tic->ram.registers, "SOUND REGISTERS"),
|
||||
ADDR_RECORD(console->tic->ram.sound.sfx.waveform, "WAVEFORMS"),
|
||||
ADDR_RECORD(console->tic->ram.sound.sfx.data, "SFX"),
|
||||
ADDR_RECORD(console->tic->ram.sound.music.patterns.data, "MUSIC PATTERNS"),
|
||||
ADDR_RECORD(console->tic->ram.sound.music.tracks.data, "MUSIC TRACKS"),
|
||||
ADDR_RECORD(console->tic->ram.music_pos, "MUSIC POS"),
|
||||
{TIC_RAM_SIZE, "..."},
|
||||
{offsetof(tic_ram, vram.screen), "SCREEN"},
|
||||
{offsetof(tic_ram, vram.palette), "PALETTE"},
|
||||
{offsetof(tic_ram, vram.mapping), "PALETTE MAP"},
|
||||
{offsetof(tic_ram, vram.vars.colors), "BORDER/BG COLOR"},
|
||||
{offsetof(tic_ram, vram.vars.offset), "SCREEN OFFSET"},
|
||||
{offsetof(tic_ram, vram.vars.mask), "GAMEPAD MASK"},
|
||||
{offsetof(tic_ram, vram.input.gamepad), "GAMEPAD"},
|
||||
{offsetof(tic_ram, vram.input.reserved), "..."},
|
||||
{offsetof(tic_ram, gfx.tiles), "SPRITES"},
|
||||
{offsetof(tic_ram, gfx.map), "MAP"},
|
||||
{offsetof(tic_ram, persistent), "PERSISTENT MEMORY"},
|
||||
{offsetof(tic_ram, registers), "SOUND REGISTERS"},
|
||||
{offsetof(tic_ram, sound.sfx.waveform), "WAVEFORMS"},
|
||||
{offsetof(tic_ram, sound.sfx.data), "SFX"},
|
||||
{offsetof(tic_ram, sound.music.patterns.data), "MUSIC PATTERNS"},
|
||||
{offsetof(tic_ram, sound.music.tracks.data), "MUSIC TRACKS"},
|
||||
{offsetof(tic_ram, music_pos), "MUSIC POS"},
|
||||
{TIC_RAM_SIZE, "..."},
|
||||
};
|
||||
|
||||
#undef ADDR_RECORD
|
||||
|
||||
enum{Last = COUNT_OF(Layout)-1};
|
||||
|
||||
for(s32 i = 0; i < Last; i++)
|
||||
@@ -1832,6 +2181,8 @@ static const struct
|
||||
{"demo", NULL, "install demo carts", onConsoleInstallDemosCommand},
|
||||
{"config", NULL, "edit TIC config", onConsoleConfigCommand},
|
||||
{"keymap", NULL, "configure keyboard mapping", onConsoleKeymapCommand},
|
||||
{"version", NULL, "show the current version", onConsoleVersionCommand},
|
||||
{"edit", NULL, "open cart editor", onConsoleCodeCommand},
|
||||
{"surf", NULL, "open carts browser", onConsoleSurfCommand},
|
||||
};
|
||||
|
||||
@@ -2242,11 +2593,12 @@ static void tick(Console* console)
|
||||
printLine(console);
|
||||
commandDone(console);
|
||||
console->active = true;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
if(console->cursor.delay)
|
||||
console->cursor.delay--;
|
||||
|
||||
@@ -2256,6 +2608,12 @@ static void tick(Console* console)
|
||||
}
|
||||
|
||||
console->tickCounter++;
|
||||
|
||||
if(console->startSurf)
|
||||
{
|
||||
console->startSurf = false;
|
||||
gotoSurf();
|
||||
}
|
||||
}
|
||||
|
||||
static void cmdLoadCart(Console* console, const char* name)
|
||||
@@ -2265,9 +2623,24 @@ static void cmdLoadCart(Console* console, const char* name)
|
||||
|
||||
if(data)
|
||||
{
|
||||
loadCart(console->tic, &embed.file, data, size, true);
|
||||
embed.yes = true;
|
||||
#if defined(TIC80_PRO)
|
||||
if(hasProjectExt(name))
|
||||
{
|
||||
loadProject(console, name, data, size, &embed.file);
|
||||
strcpy(console->romName, fsFilename(name));
|
||||
embed.yes = true;
|
||||
embed.fast = true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
if(hasExt(name, CART_EXT))
|
||||
{
|
||||
loadCart(console->tic, &embed.file, data, size, true);
|
||||
strcpy(console->romName, fsFilename(name));
|
||||
embed.yes = true;
|
||||
}
|
||||
|
||||
SDL_free(data);
|
||||
}
|
||||
}
|
||||
@@ -2292,9 +2665,6 @@ static bool loadFileIntoBuffer(Console* console, char* buffer, const char* fileN
|
||||
memcpy(buffer, contents, SDL_min(size, TIC_CODE_SIZE-1));
|
||||
SDL_free(contents);
|
||||
|
||||
embed.yes = true;
|
||||
embed.fast = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2408,6 +2778,14 @@ void initConsole(Console* console, tic_mem* tic, FileSystem* fs, Config* config,
|
||||
.tic = tic,
|
||||
.config = config,
|
||||
.load = onConsoleLoadCommandConfirmed,
|
||||
|
||||
#if defined(TIC80_PRO)
|
||||
.loadProject = loadProject,
|
||||
.updateProject = updateProject,
|
||||
#else
|
||||
.loadProject = NULL,
|
||||
.updateProject = NULL,
|
||||
#endif
|
||||
.error = error,
|
||||
.trace = trace,
|
||||
.tick = tick,
|
||||
@@ -2433,6 +2811,7 @@ void initConsole(Console* console, tic_mem* tic, FileSystem* fs, Config* config,
|
||||
.colorBuffer = console->colorBuffer,
|
||||
.fs = fs,
|
||||
.showGameMenu = false,
|
||||
.startSurf = false,
|
||||
};
|
||||
|
||||
memset(console->buffer, 0, CONSOLE_BUFFER_SIZE);
|
||||
@@ -2476,6 +2855,14 @@ void initConsole(Console* console, tic_mem* tic, FileSystem* fs, Config* config,
|
||||
cmdInjectMap(console, argv[i], argv[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
for (s32 i = 1; i < argc; i++)
|
||||
{
|
||||
if(strcmp(argv[i], "-nosound") == 0)
|
||||
config->data.noSound = true;
|
||||
else if(strcmp(argv[i], "-surf") == 0)
|
||||
console->startSurf = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
|
||||
@@ -88,8 +88,11 @@ struct Console
|
||||
u32 tickCounter;
|
||||
bool active;
|
||||
bool showGameMenu;
|
||||
bool startSurf;
|
||||
|
||||
void(*load)(Console*, const char* name);
|
||||
bool(*loadProject)(Console*, const char* name, const char* data, s32 size, tic_cartridge* dst);
|
||||
void(*updateProject)(Console*);
|
||||
void(*error)(Console*, const char*);
|
||||
void(*trace)(Console*, const char*, u8 color);
|
||||
void(*tick)(Console*);
|
||||
|
||||
@@ -191,13 +191,6 @@ void file_dialog_save(file_dialog_save_callback callback, const char* name, cons
|
||||
|
||||
#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>
|
||||
@@ -306,8 +299,6 @@ void file_dialog_save(file_dialog_save_callback callback, const char* name, cons
|
||||
callback(false, data);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#elif defined(__MACOSX__)
|
||||
|
||||
#include <string.h>
|
||||
|
||||
1198
src/ext/gif/dgif_lib.c
Normal file
1198
src/ext/gif/dgif_lib.c
Normal file
File diff suppressed because it is too large
Load Diff
1159
src/ext/gif/egif_lib.c
Normal file
1159
src/ext/gif/egif_lib.c
Normal file
File diff suppressed because it is too large
Load Diff
97
src/ext/gif/gif_err.c
Normal file
97
src/ext/gif/gif_err.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/*****************************************************************************
|
||||
|
||||
gif_err.c - handle error reporting for the GIF library.
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "gif_lib.h"
|
||||
#include "gif_lib_private.h"
|
||||
|
||||
/*****************************************************************************
|
||||
Return a string description of the last GIF error
|
||||
*****************************************************************************/
|
||||
const char *
|
||||
GifErrorString(int ErrorCode)
|
||||
{
|
||||
const char *Err;
|
||||
|
||||
switch (ErrorCode) {
|
||||
case E_GIF_ERR_OPEN_FAILED:
|
||||
Err = "Failed to open given file";
|
||||
break;
|
||||
case E_GIF_ERR_WRITE_FAILED:
|
||||
Err = "Failed to write to given file";
|
||||
break;
|
||||
case E_GIF_ERR_HAS_SCRN_DSCR:
|
||||
Err = "Screen descriptor has already been set";
|
||||
break;
|
||||
case E_GIF_ERR_HAS_IMAG_DSCR:
|
||||
Err = "Image descriptor is still active";
|
||||
break;
|
||||
case E_GIF_ERR_NO_COLOR_MAP:
|
||||
Err = "Neither global nor local color map";
|
||||
break;
|
||||
case E_GIF_ERR_DATA_TOO_BIG:
|
||||
Err = "Number of pixels bigger than width * height";
|
||||
break;
|
||||
case E_GIF_ERR_NOT_ENOUGH_MEM:
|
||||
Err = "Failed to allocate required memory";
|
||||
break;
|
||||
case E_GIF_ERR_DISK_IS_FULL:
|
||||
Err = "Write failed (disk full?)";
|
||||
break;
|
||||
case E_GIF_ERR_CLOSE_FAILED:
|
||||
Err = "Failed to close given file";
|
||||
break;
|
||||
case E_GIF_ERR_NOT_WRITEABLE:
|
||||
Err = "Given file was not opened for write";
|
||||
break;
|
||||
case D_GIF_ERR_OPEN_FAILED:
|
||||
Err = "Failed to open given file";
|
||||
break;
|
||||
case D_GIF_ERR_READ_FAILED:
|
||||
Err = "Failed to read from given file";
|
||||
break;
|
||||
case D_GIF_ERR_NOT_GIF_FILE:
|
||||
Err = "Data is not in GIF format";
|
||||
break;
|
||||
case D_GIF_ERR_NO_SCRN_DSCR:
|
||||
Err = "No screen descriptor detected";
|
||||
break;
|
||||
case D_GIF_ERR_NO_IMAG_DSCR:
|
||||
Err = "No Image Descriptor detected";
|
||||
break;
|
||||
case D_GIF_ERR_NO_COLOR_MAP:
|
||||
Err = "Neither global nor local color map";
|
||||
break;
|
||||
case D_GIF_ERR_WRONG_RECORD:
|
||||
Err = "Wrong record type detected";
|
||||
break;
|
||||
case D_GIF_ERR_DATA_TOO_BIG:
|
||||
Err = "Number of pixels bigger than width * height";
|
||||
break;
|
||||
case D_GIF_ERR_NOT_ENOUGH_MEM:
|
||||
Err = "Failed to allocate required memory";
|
||||
break;
|
||||
case D_GIF_ERR_CLOSE_FAILED:
|
||||
Err = "Failed to close given file";
|
||||
break;
|
||||
case D_GIF_ERR_NOT_READABLE:
|
||||
Err = "Given file was not opened for read";
|
||||
break;
|
||||
case D_GIF_ERR_IMAGE_DEFECT:
|
||||
Err = "Image is defective, decoding aborted";
|
||||
break;
|
||||
case D_GIF_ERR_EOF_TOO_SOON:
|
||||
Err = "Image EOF detected before image complete";
|
||||
break;
|
||||
default:
|
||||
Err = NULL;
|
||||
break;
|
||||
}
|
||||
return Err;
|
||||
}
|
||||
|
||||
/* end */
|
||||
259
src/ext/gif/gif_font.c
Normal file
259
src/ext/gif/gif_font.c
Normal file
@@ -0,0 +1,259 @@
|
||||
/*****************************************************************************
|
||||
|
||||
gif_font.c - utility font handling and simple drawing for the GIF library
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gif_lib.h"
|
||||
|
||||
/*****************************************************************************
|
||||
Ascii 8 by 8 regular font - only first 128 characters are supported.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* Each array entry holds the bits for 8 horizontal scan lines, topmost
|
||||
* first. The most significant bit of each constant is the leftmost bit of
|
||||
* the scan line.
|
||||
*/
|
||||
/*@+charint@*/
|
||||
const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH] = {
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* Ascii 0 */
|
||||
{0x3c, 0x42, 0xa5, 0x81, 0xbd, 0x42, 0x3c, 0x00}, /* Ascii 1 */
|
||||
{0x3c, 0x7e, 0xdb, 0xff, 0xc3, 0x7e, 0x3c, 0x00}, /* Ascii 2 */
|
||||
{0x00, 0xee, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 3 */
|
||||
{0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 4 */
|
||||
{0x00, 0x3c, 0x18, 0xff, 0xff, 0x08, 0x18, 0x00}, /* Ascii 5 */
|
||||
{0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x10, 0x38, 0x00}, /* Ascii 6 */
|
||||
{0x00, 0x00, 0x18, 0x3c, 0x18, 0x00, 0x00, 0x00}, /* Ascii 7 */
|
||||
{0xff, 0xff, 0xe7, 0xc3, 0xe7, 0xff, 0xff, 0xff}, /* Ascii 8 */
|
||||
{0x00, 0x3c, 0x42, 0x81, 0x81, 0x42, 0x3c, 0x00}, /* Ascii 9 */
|
||||
{0xff, 0xc3, 0xbd, 0x7e, 0x7e, 0xbd, 0xc3, 0xff}, /* Ascii 10 */
|
||||
{0x1f, 0x07, 0x0d, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* Ascii 11 */
|
||||
{0x00, 0x7e, 0xc3, 0xc3, 0x7e, 0x18, 0x7e, 0x18}, /* Ascii 12 */
|
||||
{0x04, 0x06, 0x07, 0x04, 0x04, 0xfc, 0xf8, 0x00}, /* Ascii 13 */
|
||||
{0x0c, 0x0a, 0x0d, 0x0b, 0xf9, 0xf9, 0x1f, 0x1f}, /* Ascii 14 */
|
||||
{0x00, 0x92, 0x7c, 0x44, 0xc6, 0x7c, 0x92, 0x00}, /* Ascii 15 */
|
||||
{0x00, 0x00, 0x60, 0x78, 0x7e, 0x78, 0x60, 0x00}, /* Ascii 16 */
|
||||
{0x00, 0x00, 0x06, 0x1e, 0x7e, 0x1e, 0x06, 0x00}, /* Ascii 17 */
|
||||
{0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18}, /* Ascii 18 */
|
||||
{0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00}, /* Ascii 19 */
|
||||
{0xff, 0xb6, 0x76, 0x36, 0x36, 0x36, 0x36, 0x00}, /* Ascii 20 */
|
||||
{0x7e, 0xc1, 0xdc, 0x22, 0x22, 0x1f, 0x83, 0x7e}, /* Ascii 21 */
|
||||
{0x00, 0x00, 0x00, 0x7e, 0x7e, 0x00, 0x00, 0x00}, /* Ascii 22 */
|
||||
{0x18, 0x7e, 0x18, 0x18, 0x7e, 0x18, 0x00, 0xff}, /* Ascii 23 */
|
||||
{0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, /* Ascii 24 */
|
||||
{0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x00}, /* Ascii 25 */
|
||||
{0x00, 0x04, 0x06, 0xff, 0x06, 0x04, 0x00, 0x00}, /* Ascii 26 */
|
||||
{0x00, 0x20, 0x60, 0xff, 0x60, 0x20, 0x00, 0x00}, /* Ascii 27 */
|
||||
{0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xff, 0x00}, /* Ascii 28 */
|
||||
{0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00}, /* Ascii 29 */
|
||||
{0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x00, 0x00}, /* Ascii 30 */
|
||||
{0x00, 0x00, 0x00, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 31 */
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* */
|
||||
{0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00}, /* ! */
|
||||
{0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* " */
|
||||
{0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00}, /* # */
|
||||
{0x10, 0x7c, 0xd2, 0x7c, 0x86, 0x7c, 0x10, 0x00}, /* $ */
|
||||
{0xf0, 0x96, 0xfc, 0x18, 0x3e, 0x72, 0xde, 0x00}, /* % */
|
||||
{0x30, 0x48, 0x30, 0x78, 0xce, 0xcc, 0x78, 0x00}, /* & */
|
||||
{0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ' */
|
||||
{0x10, 0x60, 0xc0, 0xc0, 0xc0, 0x60, 0x10, 0x00}, /* ( */
|
||||
{0x10, 0x0c, 0x06, 0x06, 0x06, 0x0c, 0x10, 0x00}, /* ) */
|
||||
{0x00, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x00, 0x00}, /* * */
|
||||
{0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00}, /* + */
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x70}, /* , */
|
||||
{0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00}, /* - */
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00}, /* . */
|
||||
{0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00}, /* / */
|
||||
{0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* 0 */
|
||||
{0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* 1 */
|
||||
{0x7c, 0xc6, 0x06, 0x0c, 0x30, 0x60, 0xfe, 0x00}, /* 2 */
|
||||
{0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00}, /* 3 */
|
||||
{0x0e, 0x1e, 0x36, 0x66, 0xfe, 0x06, 0x06, 0x00}, /* 4 */
|
||||
{0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xfc, 0x00}, /* 5 */
|
||||
{0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00}, /* 6 */
|
||||
{0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x00}, /* 7 */
|
||||
{0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* 8 */
|
||||
{0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00}, /* 9 */
|
||||
{0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00}, /* : */
|
||||
{0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x20, 0x00}, /* }, */
|
||||
{0x00, 0x1c, 0x30, 0x60, 0x30, 0x1c, 0x00, 0x00}, /* < */
|
||||
{0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00}, /* = */
|
||||
{0x00, 0x70, 0x18, 0x0c, 0x18, 0x70, 0x00, 0x00}, /* > */
|
||||
{0x7c, 0xc6, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00}, /* ? */
|
||||
{0x7c, 0x82, 0x9a, 0xaa, 0xaa, 0x9e, 0x7c, 0x00}, /* @ */
|
||||
{0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00}, /* A */
|
||||
{0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0x00}, /* B */
|
||||
{0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00}, /* C */
|
||||
{0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00}, /* D */
|
||||
{0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xfe, 0x00}, /* E */
|
||||
{0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* F */
|
||||
{0x7c, 0xc6, 0xc0, 0xce, 0xc6, 0xc6, 0x7e, 0x00}, /* G */
|
||||
{0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00}, /* H */
|
||||
{0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, /* I */
|
||||
{0x1e, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00}, /* J */
|
||||
{0xc6, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xc6, 0x00}, /* K */
|
||||
{0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00}, /* L */
|
||||
{0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0x00}, /* M */
|
||||
{0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00}, /* N */
|
||||
{0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* O */
|
||||
{0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* P */
|
||||
{0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x06}, /* Q */
|
||||
{0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0x00}, /* R */
|
||||
{0x78, 0xcc, 0x60, 0x30, 0x18, 0xcc, 0x78, 0x00}, /* S */
|
||||
{0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00}, /* T */
|
||||
{0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* U */
|
||||
{0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* V */
|
||||
{0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00}, /* W */
|
||||
{0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00}, /* X */
|
||||
{0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00}, /* Y */
|
||||
{0xfe, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00}, /* Z */
|
||||
{0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00}, /* [ */
|
||||
{0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x00}, /* \ */
|
||||
{0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00}, /* ] */
|
||||
{0x00, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00}, /* ^ */
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}, /* _ */
|
||||
{0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ` */
|
||||
{0x00, 0x00, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00}, /* a */
|
||||
{0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0x00}, /* b */
|
||||
{0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0x7e, 0x00}, /* c */
|
||||
{0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x00}, /* d */
|
||||
{0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7e, 0x00}, /* e */
|
||||
{0x1e, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x30, 0x00}, /* f */
|
||||
{0x00, 0x00, 0x7e, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* g */
|
||||
{0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* */
|
||||
{0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* i */
|
||||
{0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0xf0}, /* j */
|
||||
{0xc0, 0xc0, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0x00}, /* k */
|
||||
{0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* l */
|
||||
{0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xc6, 0xc6, 0x00}, /* m */
|
||||
{0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* n */
|
||||
{0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* o */
|
||||
{0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0xc0}, /* p */
|
||||
{0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x06}, /* q */
|
||||
{0x00, 0x00, 0x6e, 0x70, 0x60, 0x60, 0x60, 0x00}, /* r */
|
||||
{0x00, 0x00, 0x7c, 0xc0, 0x7c, 0x06, 0xfc, 0x00}, /* s */
|
||||
{0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x1c, 0x00}, /* t */
|
||||
{0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00}, /* u */
|
||||
{0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* v */
|
||||
{0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00}, /* w */
|
||||
{0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00}, /* x */
|
||||
{0x00, 0x00, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* y */
|
||||
{0x00, 0x00, 0xfc, 0x18, 0x30, 0x60, 0xfc, 0x00}, /* z */
|
||||
{0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00}, /* { */
|
||||
{0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, /* | */
|
||||
{0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00}, /* } */
|
||||
{0x00, 0x00, 0x70, 0x9a, 0x0e, 0x00, 0x00, 0x00}, /* ~ */
|
||||
{0x00, 0x00, 0x18, 0x3c, 0x66, 0xff, 0x00, 0x00} /* Ascii 127 */
|
||||
};
|
||||
/*@=charint@*/
|
||||
|
||||
void
|
||||
GifDrawText8x8(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const char *legend,
|
||||
const int color)
|
||||
{
|
||||
int i, j;
|
||||
int base;
|
||||
const char *cp;
|
||||
|
||||
for (i = 0; i < GIF_FONT_HEIGHT; i++) {
|
||||
base = Image->ImageDesc.Width * (y + i) + x;
|
||||
|
||||
for (cp = legend; *cp; cp++)
|
||||
for (j = 0; j < GIF_FONT_WIDTH; j++) {
|
||||
if (GifAsciiTable8x8[(short)(*cp)][i] & (1 << (GIF_FONT_WIDTH - j)))
|
||||
Image->RasterBits[base] = color;
|
||||
base++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GifDrawBox(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const int w, const int d,
|
||||
const int color)
|
||||
{
|
||||
int j, base = Image->ImageDesc.Width * y + x;
|
||||
|
||||
for (j = 0; j < w; j++)
|
||||
Image->RasterBits[base + j] =
|
||||
Image->RasterBits[base + (d * Image->ImageDesc.Width) + j] = color;
|
||||
|
||||
for (j = 0; j < d; j++)
|
||||
Image->RasterBits[base + j * Image->ImageDesc.Width] =
|
||||
Image->RasterBits[base + j * Image->ImageDesc.Width + w] = color;
|
||||
}
|
||||
|
||||
void
|
||||
GifDrawRectangle(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const int w, const int d,
|
||||
const int color)
|
||||
{
|
||||
unsigned char *bp = Image->RasterBits + Image->ImageDesc.Width * y + x;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < d; i++)
|
||||
memset(bp + (i * Image->ImageDesc.Width), color, (size_t)w);
|
||||
}
|
||||
|
||||
void
|
||||
GifDrawBoxedText8x8(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const char *legend,
|
||||
const int border,
|
||||
const int bg, const int fg)
|
||||
{
|
||||
int j = 0, LineCount = 0, TextWidth = 0;
|
||||
const char *cp;
|
||||
char *dup;
|
||||
|
||||
/* compute size of text to box */
|
||||
for (cp = legend; *cp; cp++)
|
||||
if (*cp == '\r') {
|
||||
if (j > TextWidth)
|
||||
TextWidth = j;
|
||||
j = 0;
|
||||
LineCount++;
|
||||
} else if (*cp != '\t')
|
||||
++j;
|
||||
LineCount++; /* count last line */
|
||||
if (j > TextWidth) /* last line might be longer than any previous */
|
||||
TextWidth = j;
|
||||
|
||||
/* draw the text */
|
||||
dup = malloc(strlen(legend)+1);
|
||||
/* FIXME: should return bad status, but that would require API change */
|
||||
if (dup != NULL) {
|
||||
int i = 0;
|
||||
/* fill the box */
|
||||
GifDrawRectangle(Image, x + 1, y + 1,
|
||||
border + TextWidth * GIF_FONT_WIDTH + border - 1,
|
||||
border + LineCount * GIF_FONT_HEIGHT + border - 1, bg);
|
||||
(void)strcpy(dup, (char *)legend);
|
||||
cp = strtok((char *)dup, "\r\n");
|
||||
do {
|
||||
int leadspace = 0;
|
||||
|
||||
if (cp[0] == '\t')
|
||||
leadspace = (TextWidth - strlen(++cp)) / 2;
|
||||
|
||||
GifDrawText8x8(Image, x + border + (leadspace * GIF_FONT_WIDTH),
|
||||
y + border + (GIF_FONT_HEIGHT * i++), cp, fg);
|
||||
cp = strtok((char *)NULL, "\r\n");
|
||||
} while (cp);
|
||||
(void)free((void *)dup);
|
||||
|
||||
/* outline the box */
|
||||
GifDrawBox(Image, x, y, border + TextWidth * GIF_FONT_WIDTH + border,
|
||||
border + LineCount * GIF_FONT_HEIGHT + border, fg);
|
||||
}
|
||||
}
|
||||
|
||||
/* end */
|
||||
131
src/ext/gif/gif_hash.c
Normal file
131
src/ext/gif/gif_hash.c
Normal file
@@ -0,0 +1,131 @@
|
||||
/*****************************************************************************
|
||||
|
||||
gif_hash.c -- module to support the following operations:
|
||||
|
||||
1. InitHashTable - initialize hash table.
|
||||
2. ClearHashTable - clear the hash table to an empty state.
|
||||
2. InsertHashTable - insert one item into data structure.
|
||||
3. ExistsHashTable - test if item exists in data structure.
|
||||
|
||||
This module is used to hash the GIF codes during encoding.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gif_lib.h"
|
||||
#include "gif_hash.h"
|
||||
#include "gif_lib_private.h"
|
||||
|
||||
/* #define DEBUG_HIT_RATE Debug number of misses per hash Insert/Exists. */
|
||||
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
static long NumberOfTests = 0,
|
||||
NumberOfMisses = 0;
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
|
||||
static int KeyItem(uint32_t Item);
|
||||
|
||||
/******************************************************************************
|
||||
Initialize HashTable - allocate the memory needed and clear it. *
|
||||
******************************************************************************/
|
||||
GifHashTableType *_InitHashTable(void)
|
||||
{
|
||||
GifHashTableType *HashTable;
|
||||
|
||||
if ((HashTable = (GifHashTableType *) malloc(sizeof(GifHashTableType)))
|
||||
== NULL)
|
||||
return NULL;
|
||||
|
||||
_ClearHashTable(HashTable);
|
||||
|
||||
return HashTable;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Routine to clear the HashTable to an empty state. *
|
||||
This part is a little machine depended. Use the commented part otherwise. *
|
||||
******************************************************************************/
|
||||
void _ClearHashTable(GifHashTableType *HashTable)
|
||||
{
|
||||
memset(HashTable -> HTable, 0xFF, HT_SIZE * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Routine to insert a new Item into the HashTable. The data is assumed to be *
|
||||
new one. *
|
||||
******************************************************************************/
|
||||
void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code)
|
||||
{
|
||||
int HKey = KeyItem(Key);
|
||||
uint32_t *HTable = HashTable -> HTable;
|
||||
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
NumberOfTests++;
|
||||
NumberOfMisses++;
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
|
||||
while (HT_GET_KEY(HTable[HKey]) != 0xFFFFFL) {
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
NumberOfMisses++;
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
HKey = (HKey + 1) & HT_KEY_MASK;
|
||||
}
|
||||
HTable[HKey] = HT_PUT_KEY(Key) | HT_PUT_CODE(Code);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Routine to test if given Key exists in HashTable and if so returns its code *
|
||||
Returns the Code if key was found, -1 if not. *
|
||||
******************************************************************************/
|
||||
int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key)
|
||||
{
|
||||
int HKey = KeyItem(Key);
|
||||
uint32_t *HTable = HashTable -> HTable, HTKey;
|
||||
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
NumberOfTests++;
|
||||
NumberOfMisses++;
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
|
||||
while ((HTKey = HT_GET_KEY(HTable[HKey])) != 0xFFFFFL) {
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
NumberOfMisses++;
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
if (Key == HTKey) return HT_GET_CODE(HTable[HKey]);
|
||||
HKey = (HKey + 1) & HT_KEY_MASK;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Routine to generate an HKey for the hashtable out of the given unique key. *
|
||||
The given Key is assumed to be 20 bits as follows: lower 8 bits are the *
|
||||
new postfix character, while the upper 12 bits are the prefix code. *
|
||||
Because the average hit ratio is only 2 (2 hash references per entry), *
|
||||
evaluating more complex keys (such as twin prime keys) does not worth it! *
|
||||
******************************************************************************/
|
||||
static int KeyItem(uint32_t Item)
|
||||
{
|
||||
return ((Item >> 12) ^ Item) & HT_KEY_MASK;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
/******************************************************************************
|
||||
Debugging routine to print the hit ratio - number of times the hash table *
|
||||
was tested per operation. This routine was used to test the KeyItem routine *
|
||||
******************************************************************************/
|
||||
void HashTablePrintHitRatio(void)
|
||||
{
|
||||
printf("Hash Table Hit Ratio is %ld/%ld = %ld%%.\n",
|
||||
NumberOfMisses, NumberOfTests,
|
||||
NumberOfMisses * 100 / NumberOfTests);
|
||||
}
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
|
||||
/* end */
|
||||
39
src/ext/gif/gif_hash.h
Normal file
39
src/ext/gif/gif_hash.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/******************************************************************************
|
||||
|
||||
gif_hash.h - magfic constants and declarations for GIF LZW
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _GIF_HASH_H_
|
||||
#define _GIF_HASH_H_
|
||||
|
||||
//#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */
|
||||
#define HT_KEY_MASK 0x1FFF /* 13bits keys */
|
||||
#define HT_KEY_NUM_BITS 13 /* 13bits keys */
|
||||
#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */
|
||||
#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */
|
||||
|
||||
/* The 32 bits of the long are divided into two parts for the key & code: */
|
||||
/* 1. The code is 12 bits as our compression algorithm is limited to 12bits */
|
||||
/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */
|
||||
/* The key is the upper 20 bits. The code is the lower 12. */
|
||||
#define HT_GET_KEY(l) (l >> 12)
|
||||
#define HT_GET_CODE(l) (l & 0x0FFF)
|
||||
#define HT_PUT_KEY(l) (l << 12)
|
||||
#define HT_PUT_CODE(l) (l & 0x0FFF)
|
||||
|
||||
typedef struct GifHashTableType {
|
||||
uint32_t HTable[HT_SIZE];
|
||||
} GifHashTableType;
|
||||
|
||||
GifHashTableType *_InitHashTable(void);
|
||||
void _ClearHashTable(GifHashTableType *HashTable);
|
||||
void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code);
|
||||
int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key);
|
||||
|
||||
#endif /* _GIF_HASH_H_ */
|
||||
|
||||
/* end */
|
||||
312
src/ext/gif/gif_lib.h
Normal file
312
src/ext/gif/gif_lib.h
Normal file
@@ -0,0 +1,312 @@
|
||||
/******************************************************************************
|
||||
|
||||
gif_lib.h - service library for decoding and encoding GIF images
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef _GIF_LIB_H_
|
||||
#define _GIF_LIB_H_ 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define GIFLIB_MAJOR 5
|
||||
#define GIFLIB_MINOR 1
|
||||
#define GIFLIB_RELEASE 4
|
||||
|
||||
#define GIF_ERROR 0
|
||||
#define GIF_OK 1
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */
|
||||
#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1
|
||||
#define GIF_VERSION_POS 3 /* Version first character in stamp. */
|
||||
#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */
|
||||
#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */
|
||||
|
||||
typedef unsigned char GifPixelType;
|
||||
typedef unsigned char *GifRowType;
|
||||
typedef unsigned char GifByteType;
|
||||
typedef unsigned int GifPrefixType;
|
||||
typedef int GifWord;
|
||||
|
||||
typedef struct GifColorType {
|
||||
GifByteType Red, Green, Blue;
|
||||
} GifColorType;
|
||||
|
||||
typedef struct ColorMapObject {
|
||||
int ColorCount;
|
||||
int BitsPerPixel;
|
||||
bool SortFlag;
|
||||
GifColorType *Colors; /* on malloc(3) heap */
|
||||
} ColorMapObject;
|
||||
|
||||
typedef struct GifImageDesc {
|
||||
GifWord Left, Top, Width, Height; /* Current image dimensions. */
|
||||
bool Interlace; /* Sequential/Interlaced lines. */
|
||||
ColorMapObject *ColorMap; /* The local color map */
|
||||
} GifImageDesc;
|
||||
|
||||
typedef struct ExtensionBlock {
|
||||
int ByteCount;
|
||||
GifByteType *Bytes; /* on malloc(3) heap */
|
||||
int Function; /* The block function code */
|
||||
#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */
|
||||
#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */
|
||||
#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */
|
||||
#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */
|
||||
#define APPLICATION_EXT_FUNC_CODE 0xff /* application block */
|
||||
} ExtensionBlock;
|
||||
|
||||
typedef struct SavedImage {
|
||||
GifImageDesc ImageDesc;
|
||||
GifByteType *RasterBits; /* on malloc(3) heap */
|
||||
int ExtensionBlockCount; /* Count of extensions before image */
|
||||
ExtensionBlock *ExtensionBlocks; /* Extensions before image */
|
||||
} SavedImage;
|
||||
|
||||
typedef struct GifFileType {
|
||||
GifWord SWidth, SHeight; /* Size of virtual canvas */
|
||||
GifWord SColorResolution; /* How many colors can we generate? */
|
||||
GifWord SBackGroundColor; /* Background color for virtual canvas */
|
||||
GifByteType AspectByte; /* Used to compute pixel aspect ratio */
|
||||
ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */
|
||||
int ImageCount; /* Number of current image (both APIs) */
|
||||
GifImageDesc Image; /* Current image (low-level API) */
|
||||
SavedImage *SavedImages; /* Image sequence (high-level API) */
|
||||
int ExtensionBlockCount; /* Count extensions past last image */
|
||||
ExtensionBlock *ExtensionBlocks; /* Extensions past last image */
|
||||
int Error; /* Last error condition reported */
|
||||
void *UserData; /* hook to attach user data (TVT) */
|
||||
void *Private; /* Don't mess with this! */
|
||||
} GifFileType;
|
||||
|
||||
#define GIF_ASPECT_RATIO(n) ((n)+15.0/64.0)
|
||||
|
||||
typedef enum {
|
||||
UNDEFINED_RECORD_TYPE,
|
||||
SCREEN_DESC_RECORD_TYPE,
|
||||
IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */
|
||||
EXTENSION_RECORD_TYPE, /* Begin with '!' */
|
||||
TERMINATE_RECORD_TYPE /* Begin with ';' */
|
||||
} GifRecordType;
|
||||
|
||||
/* func type to read gif data from arbitrary sources (TVT) */
|
||||
typedef int (*InputFunc) (GifFileType *, GifByteType *, int);
|
||||
|
||||
/* func type to write gif data to arbitrary targets.
|
||||
* Returns count of bytes written. (MRB)
|
||||
*/
|
||||
typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int);
|
||||
|
||||
/******************************************************************************
|
||||
GIF89 structures
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct GraphicsControlBlock {
|
||||
int DisposalMode;
|
||||
#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */
|
||||
#define DISPOSE_DO_NOT 1 /* Leave image in place */
|
||||
#define DISPOSE_BACKGROUND 2 /* Set area too background color */
|
||||
#define DISPOSE_PREVIOUS 3 /* Restore to previous content */
|
||||
bool UserInputFlag; /* User confirmation required before disposal */
|
||||
int DelayTime; /* pre-display delay in 0.01sec units */
|
||||
int TransparentColor; /* Palette index for transparency, -1 if none */
|
||||
#define NO_TRANSPARENT_COLOR -1
|
||||
} GraphicsControlBlock;
|
||||
|
||||
/******************************************************************************
|
||||
GIF encoding routines
|
||||
******************************************************************************/
|
||||
|
||||
/* Main entry points */
|
||||
GifFileType *EGifOpenFileName(const char *GifFileName,
|
||||
const bool GifTestExistence, int *Error);
|
||||
GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error);
|
||||
GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error);
|
||||
int EGifSpew(GifFileType * GifFile);
|
||||
const char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */
|
||||
int EGifCloseFile(GifFileType *GifFile, int *ErrorCode);
|
||||
|
||||
#define E_GIF_SUCCEEDED 0
|
||||
#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */
|
||||
#define E_GIF_ERR_WRITE_FAILED 2
|
||||
#define E_GIF_ERR_HAS_SCRN_DSCR 3
|
||||
#define E_GIF_ERR_HAS_IMAG_DSCR 4
|
||||
#define E_GIF_ERR_NO_COLOR_MAP 5
|
||||
#define E_GIF_ERR_DATA_TOO_BIG 6
|
||||
#define E_GIF_ERR_NOT_ENOUGH_MEM 7
|
||||
#define E_GIF_ERR_DISK_IS_FULL 8
|
||||
#define E_GIF_ERR_CLOSE_FAILED 9
|
||||
#define E_GIF_ERR_NOT_WRITEABLE 10
|
||||
|
||||
/* These are legacy. You probably do not want to call them directly */
|
||||
int EGifPutScreenDesc(GifFileType *GifFile,
|
||||
const int GifWidth, const int GifHeight,
|
||||
const int GifColorRes,
|
||||
const int GifBackGround,
|
||||
const ColorMapObject *GifColorMap);
|
||||
int EGifPutImageDesc(GifFileType *GifFile,
|
||||
const int GifLeft, const int GifTop,
|
||||
const int GifWidth, const int GifHeight,
|
||||
const bool GifInterlace,
|
||||
const ColorMapObject *GifColorMap);
|
||||
void EGifSetGifVersion(GifFileType *GifFile, const bool gif89);
|
||||
int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine,
|
||||
int GifLineLen);
|
||||
int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel);
|
||||
int EGifPutComment(GifFileType *GifFile, const char *GifComment);
|
||||
int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode);
|
||||
int EGifPutExtensionBlock(GifFileType *GifFile,
|
||||
const int GifExtLen, const void *GifExtension);
|
||||
int EGifPutExtensionTrailer(GifFileType *GifFile);
|
||||
int EGifPutExtension(GifFileType *GifFile, const int GifExtCode,
|
||||
const int GifExtLen,
|
||||
const void *GifExtension);
|
||||
int EGifPutCode(GifFileType *GifFile, int GifCodeSize,
|
||||
const GifByteType *GifCodeBlock);
|
||||
int EGifPutCodeNext(GifFileType *GifFile,
|
||||
const GifByteType *GifCodeBlock);
|
||||
|
||||
/******************************************************************************
|
||||
GIF decoding routines
|
||||
******************************************************************************/
|
||||
|
||||
/* Main entry points */
|
||||
GifFileType *DGifOpenFileName(const char *GifFileName, int *Error);
|
||||
GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error);
|
||||
int DGifSlurp(GifFileType * GifFile);
|
||||
GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */
|
||||
int DGifCloseFile(GifFileType * GifFile, int *ErrorCode);
|
||||
|
||||
#define D_GIF_SUCCEEDED 0
|
||||
#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */
|
||||
#define D_GIF_ERR_READ_FAILED 102
|
||||
#define D_GIF_ERR_NOT_GIF_FILE 103
|
||||
#define D_GIF_ERR_NO_SCRN_DSCR 104
|
||||
#define D_GIF_ERR_NO_IMAG_DSCR 105
|
||||
#define D_GIF_ERR_NO_COLOR_MAP 106
|
||||
#define D_GIF_ERR_WRONG_RECORD 107
|
||||
#define D_GIF_ERR_DATA_TOO_BIG 108
|
||||
#define D_GIF_ERR_NOT_ENOUGH_MEM 109
|
||||
#define D_GIF_ERR_CLOSE_FAILED 110
|
||||
#define D_GIF_ERR_NOT_READABLE 111
|
||||
#define D_GIF_ERR_IMAGE_DEFECT 112
|
||||
#define D_GIF_ERR_EOF_TOO_SOON 113
|
||||
|
||||
/* These are legacy. You probably do not want to call them directly */
|
||||
int DGifGetScreenDesc(GifFileType *GifFile);
|
||||
int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType);
|
||||
int DGifGetImageDesc(GifFileType *GifFile);
|
||||
int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen);
|
||||
int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel);
|
||||
int DGifGetComment(GifFileType *GifFile, char *GifComment);
|
||||
int DGifGetExtension(GifFileType *GifFile, int *GifExtCode,
|
||||
GifByteType **GifExtension);
|
||||
int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension);
|
||||
int DGifGetCode(GifFileType *GifFile, int *GifCodeSize,
|
||||
GifByteType **GifCodeBlock);
|
||||
int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock);
|
||||
int DGifGetLZCodes(GifFileType *GifFile, int *GifCode);
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Color table quantization (deprecated)
|
||||
******************************************************************************/
|
||||
int GifQuantizeBuffer(unsigned int Width, unsigned int Height,
|
||||
int *ColorMapSize, GifByteType * RedInput,
|
||||
GifByteType * GreenInput, GifByteType * BlueInput,
|
||||
GifByteType * OutputBuffer,
|
||||
GifColorType * OutputColorMap);
|
||||
|
||||
/******************************************************************************
|
||||
Error handling and reporting.
|
||||
******************************************************************************/
|
||||
extern const char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */
|
||||
|
||||
/*****************************************************************************
|
||||
Everything below this point is new after version 1.2, supporting `slurp
|
||||
mode' for doing I/O in two big belts with all the image-bashing in core.
|
||||
******************************************************************************/
|
||||
|
||||
/******************************************************************************
|
||||
Color map handling from gif_alloc.c
|
||||
******************************************************************************/
|
||||
|
||||
extern ColorMapObject *GifMakeMapObject(int ColorCount,
|
||||
const GifColorType *ColorMap);
|
||||
extern void GifFreeMapObject(ColorMapObject *Object);
|
||||
extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1,
|
||||
const ColorMapObject *ColorIn2,
|
||||
GifPixelType ColorTransIn2[]);
|
||||
extern int GifBitSize(int n);
|
||||
|
||||
extern void *
|
||||
reallocarray(void *optr, size_t nmemb, size_t size);
|
||||
|
||||
/******************************************************************************
|
||||
Support for the in-core structures allocation (slurp mode).
|
||||
******************************************************************************/
|
||||
|
||||
extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]);
|
||||
extern int GifAddExtensionBlock(int *ExtensionBlock_Count,
|
||||
ExtensionBlock **ExtensionBlocks,
|
||||
int Function,
|
||||
unsigned int Len, unsigned char ExtData[]);
|
||||
extern void GifFreeExtensions(int *ExtensionBlock_Count,
|
||||
ExtensionBlock **ExtensionBlocks);
|
||||
extern SavedImage *GifMakeSavedImage(GifFileType *GifFile,
|
||||
const SavedImage *CopyFrom);
|
||||
extern void GifFreeSavedImages(GifFileType *GifFile);
|
||||
|
||||
/******************************************************************************
|
||||
5.x functions for GIF89 graphics control blocks
|
||||
******************************************************************************/
|
||||
|
||||
int DGifExtensionToGCB(const size_t GifExtensionLength,
|
||||
const GifByteType *GifExtension,
|
||||
GraphicsControlBlock *GCB);
|
||||
size_t EGifGCBToExtension(const GraphicsControlBlock *GCB,
|
||||
GifByteType *GifExtension);
|
||||
|
||||
int DGifSavedExtensionToGCB(GifFileType *GifFile,
|
||||
int ImageIndex,
|
||||
GraphicsControlBlock *GCB);
|
||||
int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB,
|
||||
GifFileType *GifFile,
|
||||
int ImageIndex);
|
||||
|
||||
/******************************************************************************
|
||||
The library's internal utility font
|
||||
******************************************************************************/
|
||||
|
||||
#define GIF_FONT_WIDTH 8
|
||||
#define GIF_FONT_HEIGHT 8
|
||||
extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH];
|
||||
|
||||
extern void GifDrawText8x8(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const char *legend, const int color);
|
||||
|
||||
extern void GifDrawBox(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const int w, const int d, const int color);
|
||||
|
||||
extern void GifDrawRectangle(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const int w, const int d, const int color);
|
||||
|
||||
extern void GifDrawBoxedText8x8(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const char *legend,
|
||||
const int border, const int bg, const int fg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* _GIF_LIB_H */
|
||||
|
||||
/* end */
|
||||
59
src/ext/gif/gif_lib_private.h
Normal file
59
src/ext/gif/gif_lib_private.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/****************************************************************************
|
||||
|
||||
gif_lib_private.h - internal giflib routines and structures
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _GIF_LIB_PRIVATE_H
|
||||
#define _GIF_LIB_PRIVATE_H
|
||||
|
||||
#include "gif_lib.h"
|
||||
#include "gif_hash.h"
|
||||
|
||||
#define EXTENSION_INTRODUCER 0x21
|
||||
#define DESCRIPTOR_INTRODUCER 0x2c
|
||||
#define TERMINATOR_INTRODUCER 0x3b
|
||||
|
||||
#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */
|
||||
#define LZ_BITS 12
|
||||
|
||||
#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */
|
||||
#define FIRST_CODE 4097 /* Impossible code, to signal first. */
|
||||
#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */
|
||||
|
||||
#define FILE_STATE_WRITE 0x01
|
||||
#define FILE_STATE_SCREEN 0x02
|
||||
#define FILE_STATE_IMAGE 0x04
|
||||
#define FILE_STATE_READ 0x08
|
||||
|
||||
#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ)
|
||||
#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE)
|
||||
|
||||
typedef struct GifFilePrivateType {
|
||||
GifWord FileState, FileHandle, /* Where all this data goes to! */
|
||||
BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */
|
||||
ClearCode, /* The CLEAR LZ code. */
|
||||
EOFCode, /* The EOF LZ code. */
|
||||
RunningCode, /* The next code algorithm can generate. */
|
||||
RunningBits, /* The number of bits required to represent RunningCode. */
|
||||
MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */
|
||||
LastCode, /* The code before the current code. */
|
||||
CrntCode, /* Current algorithm code. */
|
||||
StackPtr, /* For character stack (see below). */
|
||||
CrntShiftState; /* Number of bits in CrntShiftDWord. */
|
||||
unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */
|
||||
unsigned long PixelCount; /* Number of pixels in image. */
|
||||
FILE *File; /* File as stream. */
|
||||
InputFunc Read; /* function to read gif input (TVT) */
|
||||
OutputFunc Write; /* function to write gif output (MRB) */
|
||||
GifByteType Buf[256]; /* Compressed input is buffered here. */
|
||||
GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */
|
||||
GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */
|
||||
GifPrefixType Prefix[LZ_MAX_CODE + 1];
|
||||
GifHashTableType *HashTable;
|
||||
bool gif89;
|
||||
} GifFilePrivateType;
|
||||
|
||||
#endif /* _GIF_LIB_PRIVATE_H */
|
||||
|
||||
/* end */
|
||||
412
src/ext/gif/gifalloc.c
Normal file
412
src/ext/gif/gifalloc.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/*****************************************************************************
|
||||
|
||||
GIF construction tools
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gif_lib.h"
|
||||
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
|
||||
/******************************************************************************
|
||||
Miscellaneous utility functions
|
||||
******************************************************************************/
|
||||
|
||||
/* return smallest bitfield size n will fit in */
|
||||
int
|
||||
GifBitSize(int n)
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 1; i <= 8; i++)
|
||||
if ((1 << i) >= n)
|
||||
break;
|
||||
return (i);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Color map object functions
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
* Allocate a color map of given size; initialize with contents of
|
||||
* ColorMap if that pointer is non-NULL.
|
||||
*/
|
||||
ColorMapObject *
|
||||
GifMakeMapObject(int ColorCount, const GifColorType *ColorMap)
|
||||
{
|
||||
ColorMapObject *Object;
|
||||
|
||||
/*** FIXME: Our ColorCount has to be a power of two. Is it necessary to
|
||||
* make the user know that or should we automatically round up instead? */
|
||||
if (ColorCount != (1 << GifBitSize(ColorCount))) {
|
||||
return ((ColorMapObject *) NULL);
|
||||
}
|
||||
|
||||
Object = (ColorMapObject *)malloc(sizeof(ColorMapObject));
|
||||
if (Object == (ColorMapObject *) NULL) {
|
||||
return ((ColorMapObject *) NULL);
|
||||
}
|
||||
|
||||
Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
|
||||
if (Object->Colors == (GifColorType *) NULL) {
|
||||
free(Object);
|
||||
return ((ColorMapObject *) NULL);
|
||||
}
|
||||
|
||||
Object->ColorCount = ColorCount;
|
||||
Object->BitsPerPixel = GifBitSize(ColorCount);
|
||||
Object->SortFlag = false;
|
||||
|
||||
if (ColorMap != NULL) {
|
||||
memcpy((char *)Object->Colors,
|
||||
(char *)ColorMap, ColorCount * sizeof(GifColorType));
|
||||
}
|
||||
|
||||
return (Object);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Free a color map object
|
||||
*******************************************************************************/
|
||||
void
|
||||
GifFreeMapObject(ColorMapObject *Object)
|
||||
{
|
||||
if (Object != NULL) {
|
||||
(void)free(Object->Colors);
|
||||
(void)free(Object);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
DumpColorMap(ColorMapObject *Object,
|
||||
FILE * fp)
|
||||
{
|
||||
if (Object != NULL) {
|
||||
int i, j, Len = Object->ColorCount;
|
||||
|
||||
for (i = 0; i < Len; i += 4) {
|
||||
for (j = 0; j < 4 && j < Len; j++) {
|
||||
(void)fprintf(fp, "%3d: %02x %02x %02x ", i + j,
|
||||
Object->Colors[i + j].Red,
|
||||
Object->Colors[i + j].Green,
|
||||
Object->Colors[i + j].Blue);
|
||||
}
|
||||
(void)fprintf(fp, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
/*******************************************************************************
|
||||
Compute the union of two given color maps and return it. If result can't
|
||||
fit into 256 colors, NULL is returned, the allocated union otherwise.
|
||||
ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are
|
||||
copied iff they didn't exist before. ColorTransIn2 maps the old
|
||||
ColorIn2 into the ColorUnion color map table./
|
||||
*******************************************************************************/
|
||||
ColorMapObject *
|
||||
GifUnionColorMap(const ColorMapObject *ColorIn1,
|
||||
const ColorMapObject *ColorIn2,
|
||||
GifPixelType ColorTransIn2[])
|
||||
{
|
||||
int i, j, CrntSlot, RoundUpTo, NewGifBitSize;
|
||||
ColorMapObject *ColorUnion;
|
||||
|
||||
/*
|
||||
* We don't worry about duplicates within either color map; if
|
||||
* the caller wants to resolve those, he can perform unions
|
||||
* with an empty color map.
|
||||
*/
|
||||
|
||||
/* Allocate table which will hold the result for sure. */
|
||||
ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount,
|
||||
ColorIn2->ColorCount) * 2, NULL);
|
||||
|
||||
if (ColorUnion == NULL)
|
||||
return (NULL);
|
||||
|
||||
/*
|
||||
* Copy ColorIn1 to ColorUnion.
|
||||
*/
|
||||
for (i = 0; i < ColorIn1->ColorCount; i++)
|
||||
ColorUnion->Colors[i] = ColorIn1->Colors[i];
|
||||
CrntSlot = ColorIn1->ColorCount;
|
||||
|
||||
/*
|
||||
* Potentially obnoxious hack:
|
||||
*
|
||||
* Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
|
||||
* of table 1. This is very useful if your display is limited to
|
||||
* 16 colors.
|
||||
*/
|
||||
while (ColorIn1->Colors[CrntSlot - 1].Red == 0
|
||||
&& ColorIn1->Colors[CrntSlot - 1].Green == 0
|
||||
&& ColorIn1->Colors[CrntSlot - 1].Blue == 0)
|
||||
CrntSlot--;
|
||||
|
||||
/* Copy ColorIn2 to ColorUnion (use old colors if they exist): */
|
||||
for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
|
||||
/* Let's see if this color already exists: */
|
||||
for (j = 0; j < ColorIn1->ColorCount; j++)
|
||||
if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i],
|
||||
sizeof(GifColorType)) == 0)
|
||||
break;
|
||||
|
||||
if (j < ColorIn1->ColorCount)
|
||||
ColorTransIn2[i] = j; /* color exists in Color1 */
|
||||
else {
|
||||
/* Color is new - copy it to a new slot: */
|
||||
ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
|
||||
ColorTransIn2[i] = CrntSlot++;
|
||||
}
|
||||
}
|
||||
|
||||
if (CrntSlot > 256) {
|
||||
GifFreeMapObject(ColorUnion);
|
||||
return ((ColorMapObject *) NULL);
|
||||
}
|
||||
|
||||
NewGifBitSize = GifBitSize(CrntSlot);
|
||||
RoundUpTo = (1 << NewGifBitSize);
|
||||
|
||||
if (RoundUpTo != ColorUnion->ColorCount) {
|
||||
register GifColorType *Map = ColorUnion->Colors;
|
||||
|
||||
/*
|
||||
* Zero out slots up to next power of 2.
|
||||
* We know these slots exist because of the way ColorUnion's
|
||||
* start dimension was computed.
|
||||
*/
|
||||
for (j = CrntSlot; j < RoundUpTo; j++)
|
||||
Map[j].Red = Map[j].Green = Map[j].Blue = 0;
|
||||
|
||||
/* perhaps we can shrink the map? */
|
||||
if (RoundUpTo < ColorUnion->ColorCount) {
|
||||
GifColorType *new_map = (GifColorType *)reallocarray(Map,
|
||||
RoundUpTo, sizeof(GifColorType));
|
||||
if( new_map == NULL ) {
|
||||
GifFreeMapObject(ColorUnion);
|
||||
return ((ColorMapObject *) NULL);
|
||||
}
|
||||
ColorUnion->Colors = new_map;
|
||||
}
|
||||
}
|
||||
|
||||
ColorUnion->ColorCount = RoundUpTo;
|
||||
ColorUnion->BitsPerPixel = NewGifBitSize;
|
||||
|
||||
return (ColorUnion);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Apply a given color translation to the raster bits of an image
|
||||
*******************************************************************************/
|
||||
void
|
||||
GifApplyTranslation(SavedImage *Image, GifPixelType Translation[])
|
||||
{
|
||||
register int i;
|
||||
register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width;
|
||||
|
||||
for (i = 0; i < RasterSize; i++)
|
||||
Image->RasterBits[i] = Translation[Image->RasterBits[i]];
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Extension record functions
|
||||
******************************************************************************/
|
||||
int
|
||||
GifAddExtensionBlock(int *ExtensionBlockCount,
|
||||
ExtensionBlock **ExtensionBlocks,
|
||||
int Function,
|
||||
unsigned int Len,
|
||||
unsigned char ExtData[])
|
||||
{
|
||||
ExtensionBlock *ep;
|
||||
|
||||
if (*ExtensionBlocks == NULL)
|
||||
*ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock));
|
||||
else {
|
||||
ExtensionBlock* ep_new = (ExtensionBlock *)reallocarray
|
||||
(*ExtensionBlocks, (*ExtensionBlockCount + 1),
|
||||
sizeof(ExtensionBlock));
|
||||
if( ep_new == NULL )
|
||||
return (GIF_ERROR);
|
||||
*ExtensionBlocks = ep_new;
|
||||
}
|
||||
|
||||
if (*ExtensionBlocks == NULL)
|
||||
return (GIF_ERROR);
|
||||
|
||||
ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
|
||||
|
||||
ep->Function = Function;
|
||||
ep->ByteCount=Len;
|
||||
ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
|
||||
if (ep->Bytes == NULL)
|
||||
return (GIF_ERROR);
|
||||
|
||||
if (ExtData != NULL) {
|
||||
memcpy(ep->Bytes, ExtData, Len);
|
||||
}
|
||||
|
||||
return (GIF_OK);
|
||||
}
|
||||
|
||||
void
|
||||
GifFreeExtensions(int *ExtensionBlockCount,
|
||||
ExtensionBlock **ExtensionBlocks)
|
||||
{
|
||||
ExtensionBlock *ep;
|
||||
|
||||
if (*ExtensionBlocks == NULL)
|
||||
return;
|
||||
|
||||
for (ep = *ExtensionBlocks;
|
||||
ep < (*ExtensionBlocks + *ExtensionBlockCount);
|
||||
ep++)
|
||||
(void)free((char *)ep->Bytes);
|
||||
(void)free((char *)*ExtensionBlocks);
|
||||
*ExtensionBlocks = NULL;
|
||||
*ExtensionBlockCount = 0;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Image block allocation functions
|
||||
******************************************************************************/
|
||||
|
||||
/* Private Function:
|
||||
* Frees the last image in the GifFile->SavedImages array
|
||||
*/
|
||||
void
|
||||
FreeLastSavedImage(GifFileType *GifFile)
|
||||
{
|
||||
SavedImage *sp;
|
||||
|
||||
if ((GifFile == NULL) || (GifFile->SavedImages == NULL))
|
||||
return;
|
||||
|
||||
/* Remove one SavedImage from the GifFile */
|
||||
GifFile->ImageCount--;
|
||||
sp = &GifFile->SavedImages[GifFile->ImageCount];
|
||||
|
||||
/* Deallocate its Colormap */
|
||||
if (sp->ImageDesc.ColorMap != NULL) {
|
||||
GifFreeMapObject(sp->ImageDesc.ColorMap);
|
||||
sp->ImageDesc.ColorMap = NULL;
|
||||
}
|
||||
|
||||
/* Deallocate the image data */
|
||||
if (sp->RasterBits != NULL)
|
||||
free((char *)sp->RasterBits);
|
||||
|
||||
/* Deallocate any extensions */
|
||||
GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
|
||||
|
||||
/*** FIXME: We could realloc the GifFile->SavedImages structure but is
|
||||
* there a point to it? Saves some memory but we'd have to do it every
|
||||
* time. If this is used in GifFreeSavedImages then it would be inefficient
|
||||
* (The whole array is going to be deallocated.) If we just use it when
|
||||
* we want to free the last Image it's convenient to do it here.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Append an image block to the SavedImages array
|
||||
*/
|
||||
SavedImage *
|
||||
GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom)
|
||||
{
|
||||
if (GifFile->SavedImages == NULL)
|
||||
GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
|
||||
else
|
||||
GifFile->SavedImages = (SavedImage *)reallocarray(GifFile->SavedImages,
|
||||
(GifFile->ImageCount + 1), sizeof(SavedImage));
|
||||
|
||||
if (GifFile->SavedImages == NULL)
|
||||
return ((SavedImage *)NULL);
|
||||
else {
|
||||
SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++];
|
||||
memset((char *)sp, '\0', sizeof(SavedImage));
|
||||
|
||||
if (CopyFrom != NULL) {
|
||||
memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
|
||||
|
||||
/*
|
||||
* Make our own allocated copies of the heap fields in the
|
||||
* copied record. This guards against potential aliasing
|
||||
* problems.
|
||||
*/
|
||||
|
||||
/* first, the local color map */
|
||||
if (sp->ImageDesc.ColorMap != NULL) {
|
||||
sp->ImageDesc.ColorMap = GifMakeMapObject(
|
||||
CopyFrom->ImageDesc.ColorMap->ColorCount,
|
||||
CopyFrom->ImageDesc.ColorMap->Colors);
|
||||
if (sp->ImageDesc.ColorMap == NULL) {
|
||||
FreeLastSavedImage(GifFile);
|
||||
return (SavedImage *)(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* next, the raster */
|
||||
sp->RasterBits = (unsigned char *)reallocarray(NULL,
|
||||
(CopyFrom->ImageDesc.Height *
|
||||
CopyFrom->ImageDesc.Width),
|
||||
sizeof(GifPixelType));
|
||||
if (sp->RasterBits == NULL) {
|
||||
FreeLastSavedImage(GifFile);
|
||||
return (SavedImage *)(NULL);
|
||||
}
|
||||
memcpy(sp->RasterBits, CopyFrom->RasterBits,
|
||||
sizeof(GifPixelType) * CopyFrom->ImageDesc.Height *
|
||||
CopyFrom->ImageDesc.Width);
|
||||
|
||||
/* finally, the extension blocks */
|
||||
if (sp->ExtensionBlocks != NULL) {
|
||||
sp->ExtensionBlocks = (ExtensionBlock *)reallocarray(NULL,
|
||||
CopyFrom->ExtensionBlockCount,
|
||||
sizeof(ExtensionBlock));
|
||||
if (sp->ExtensionBlocks == NULL) {
|
||||
FreeLastSavedImage(GifFile);
|
||||
return (SavedImage *)(NULL);
|
||||
}
|
||||
memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks,
|
||||
sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount);
|
||||
}
|
||||
}
|
||||
|
||||
return (sp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GifFreeSavedImages(GifFileType *GifFile)
|
||||
{
|
||||
SavedImage *sp;
|
||||
|
||||
if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
|
||||
return;
|
||||
}
|
||||
for (sp = GifFile->SavedImages;
|
||||
sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
|
||||
if (sp->ImageDesc.ColorMap != NULL) {
|
||||
GifFreeMapObject(sp->ImageDesc.ColorMap);
|
||||
sp->ImageDesc.ColorMap = NULL;
|
||||
}
|
||||
|
||||
if (sp->RasterBits != NULL)
|
||||
free((char *)sp->RasterBits);
|
||||
|
||||
GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
|
||||
}
|
||||
free((char *)GifFile->SavedImages);
|
||||
GifFile->SavedImages = NULL;
|
||||
}
|
||||
|
||||
/* end */
|
||||
42
src/ext/gif/openbsd-reallocarray.c
Normal file
42
src/ext/gif/openbsd-reallocarray.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX ((size_t) - 1)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
|
||||
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
|
||||
*/
|
||||
#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
|
||||
|
||||
void *
|
||||
reallocarray(void *optr, size_t nmemb, size_t size)
|
||||
{
|
||||
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
|
||||
nmemb > 0 && SIZE_MAX / nmemb < size) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
return realloc(optr, size * nmemb);
|
||||
}
|
||||
330
src/ext/gif/quantize.c
Normal file
330
src/ext/gif/quantize.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/*****************************************************************************
|
||||
|
||||
quantize.c - quantize a high resolution image into lower one
|
||||
|
||||
Based on: "Color Image Quantization for frame buffer Display", by
|
||||
Paul Heckbert SIGGRAPH 1982 page 297-307.
|
||||
|
||||
This doesn't really belong in the core library, was undocumented,
|
||||
and was removed in 4.2. Then it turned out some client apps were
|
||||
actually using it, so it was restored in 5.0.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "gif_lib.h"
|
||||
#include "gif_lib_private.h"
|
||||
|
||||
#define ABS(x) ((x) > 0 ? (x) : (-(x)))
|
||||
|
||||
#define COLOR_ARRAY_SIZE 32768
|
||||
#define BITS_PER_PRIM_COLOR 5
|
||||
#define MAX_PRIM_COLOR 0x1f
|
||||
|
||||
static int SortRGBAxis;
|
||||
|
||||
typedef struct QuantizedColorType {
|
||||
GifByteType RGB[3];
|
||||
GifByteType NewColorIndex;
|
||||
long Count;
|
||||
struct QuantizedColorType *Pnext;
|
||||
} QuantizedColorType;
|
||||
|
||||
typedef struct NewColorMapType {
|
||||
GifByteType RGBMin[3], RGBWidth[3];
|
||||
unsigned int NumEntries; /* # of QuantizedColorType in linked list below */
|
||||
unsigned long Count; /* Total number of pixels in all the entries */
|
||||
QuantizedColorType *QuantizedColors;
|
||||
} NewColorMapType;
|
||||
|
||||
static int SubdivColorMap(NewColorMapType * NewColorSubdiv,
|
||||
unsigned int ColorMapSize,
|
||||
unsigned int *NewColorMapSize);
|
||||
static int SortCmpRtn(const void *Entry1, const void *Entry2);
|
||||
|
||||
/******************************************************************************
|
||||
Quantize high resolution image into lower one. Input image consists of a
|
||||
2D array for each of the RGB colors with size Width by Height. There is no
|
||||
Color map for the input. Output is a quantized image with 2D array of
|
||||
indexes into the output color map.
|
||||
Note input image can be 24 bits at the most (8 for red/green/blue) and
|
||||
the output has 256 colors at the most (256 entries in the color map.).
|
||||
ColorMapSize specifies size of color map up to 256 and will be updated to
|
||||
real size before returning.
|
||||
Also non of the parameter are allocated by this routine.
|
||||
This function returns GIF_OK if successful, GIF_ERROR otherwise.
|
||||
******************************************************************************/
|
||||
int
|
||||
GifQuantizeBuffer(unsigned int Width,
|
||||
unsigned int Height,
|
||||
int *ColorMapSize,
|
||||
GifByteType * RedInput,
|
||||
GifByteType * GreenInput,
|
||||
GifByteType * BlueInput,
|
||||
GifByteType * OutputBuffer,
|
||||
GifColorType * OutputColorMap) {
|
||||
|
||||
unsigned int Index, NumOfEntries;
|
||||
int i, j, MaxRGBError[3];
|
||||
unsigned int NewColorMapSize;
|
||||
long Red, Green, Blue;
|
||||
NewColorMapType NewColorSubdiv[256];
|
||||
QuantizedColorType *ColorArrayEntries, *QuantizedColor;
|
||||
|
||||
ColorArrayEntries = (QuantizedColorType *)malloc(
|
||||
sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE);
|
||||
if (ColorArrayEntries == NULL) {
|
||||
return GIF_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
|
||||
ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR);
|
||||
ColorArrayEntries[i].RGB[1] = (i >> BITS_PER_PRIM_COLOR) &
|
||||
MAX_PRIM_COLOR;
|
||||
ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR;
|
||||
ColorArrayEntries[i].Count = 0;
|
||||
}
|
||||
|
||||
/* Sample the colors and their distribution: */
|
||||
for (i = 0; i < (int)(Width * Height); i++) {
|
||||
Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
|
||||
(2 * BITS_PER_PRIM_COLOR)) +
|
||||
((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
|
||||
BITS_PER_PRIM_COLOR) +
|
||||
(BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
|
||||
ColorArrayEntries[Index].Count++;
|
||||
}
|
||||
|
||||
/* Put all the colors in the first entry of the color map, and call the
|
||||
* recursive subdivision process. */
|
||||
for (i = 0; i < 256; i++) {
|
||||
NewColorSubdiv[i].QuantizedColors = NULL;
|
||||
NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0;
|
||||
for (j = 0; j < 3; j++) {
|
||||
NewColorSubdiv[i].RGBMin[j] = 0;
|
||||
NewColorSubdiv[i].RGBWidth[j] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the non empty entries in the color table and chain them: */
|
||||
for (i = 0; i < COLOR_ARRAY_SIZE; i++)
|
||||
if (ColorArrayEntries[i].Count > 0)
|
||||
break;
|
||||
QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i];
|
||||
NumOfEntries = 1;
|
||||
while (++i < COLOR_ARRAY_SIZE)
|
||||
if (ColorArrayEntries[i].Count > 0) {
|
||||
QuantizedColor->Pnext = &ColorArrayEntries[i];
|
||||
QuantizedColor = &ColorArrayEntries[i];
|
||||
NumOfEntries++;
|
||||
}
|
||||
QuantizedColor->Pnext = NULL;
|
||||
|
||||
NewColorSubdiv[0].NumEntries = NumOfEntries; /* Different sampled colors */
|
||||
NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */
|
||||
NewColorMapSize = 1;
|
||||
if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) !=
|
||||
GIF_OK) {
|
||||
free((char *)ColorArrayEntries);
|
||||
return GIF_ERROR;
|
||||
}
|
||||
if (NewColorMapSize < *ColorMapSize) {
|
||||
/* And clear rest of color map: */
|
||||
for (i = NewColorMapSize; i < *ColorMapSize; i++)
|
||||
OutputColorMap[i].Red = OutputColorMap[i].Green =
|
||||
OutputColorMap[i].Blue = 0;
|
||||
}
|
||||
|
||||
/* Average the colors in each entry to be the color to be used in the
|
||||
* output color map, and plug it into the output color map itself. */
|
||||
for (i = 0; i < NewColorMapSize; i++) {
|
||||
if ((j = NewColorSubdiv[i].NumEntries) > 0) {
|
||||
QuantizedColor = NewColorSubdiv[i].QuantizedColors;
|
||||
Red = Green = Blue = 0;
|
||||
while (QuantizedColor) {
|
||||
QuantizedColor->NewColorIndex = i;
|
||||
Red += QuantizedColor->RGB[0];
|
||||
Green += QuantizedColor->RGB[1];
|
||||
Blue += QuantizedColor->RGB[2];
|
||||
QuantizedColor = QuantizedColor->Pnext;
|
||||
}
|
||||
OutputColorMap[i].Red = (Red << (8 - BITS_PER_PRIM_COLOR)) / j;
|
||||
OutputColorMap[i].Green = (Green << (8 - BITS_PER_PRIM_COLOR)) / j;
|
||||
OutputColorMap[i].Blue = (Blue << (8 - BITS_PER_PRIM_COLOR)) / j;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally scan the input buffer again and put the mapped index in the
|
||||
* output buffer. */
|
||||
MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0;
|
||||
for (i = 0; i < (int)(Width * Height); i++) {
|
||||
Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
|
||||
(2 * BITS_PER_PRIM_COLOR)) +
|
||||
((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
|
||||
BITS_PER_PRIM_COLOR) +
|
||||
(BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
|
||||
Index = ColorArrayEntries[Index].NewColorIndex;
|
||||
OutputBuffer[i] = Index;
|
||||
if (MaxRGBError[0] < ABS(OutputColorMap[Index].Red - RedInput[i]))
|
||||
MaxRGBError[0] = ABS(OutputColorMap[Index].Red - RedInput[i]);
|
||||
if (MaxRGBError[1] < ABS(OutputColorMap[Index].Green - GreenInput[i]))
|
||||
MaxRGBError[1] = ABS(OutputColorMap[Index].Green - GreenInput[i]);
|
||||
if (MaxRGBError[2] < ABS(OutputColorMap[Index].Blue - BlueInput[i]))
|
||||
MaxRGBError[2] = ABS(OutputColorMap[Index].Blue - BlueInput[i]);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,
|
||||
"Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n",
|
||||
MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]);
|
||||
#endif /* DEBUG */
|
||||
|
||||
free((char *)ColorArrayEntries);
|
||||
|
||||
*ColorMapSize = NewColorMapSize;
|
||||
|
||||
return GIF_OK;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Routine to subdivide the RGB space recursively using median cut in each
|
||||
axes alternatingly until ColorMapSize different cubes exists.
|
||||
The biggest cube in one dimension is subdivide unless it has only one entry.
|
||||
Returns GIF_ERROR if failed, otherwise GIF_OK.
|
||||
*******************************************************************************/
|
||||
static int
|
||||
SubdivColorMap(NewColorMapType * NewColorSubdiv,
|
||||
unsigned int ColorMapSize,
|
||||
unsigned int *NewColorMapSize) {
|
||||
|
||||
int MaxSize;
|
||||
unsigned int i, j, Index = 0, NumEntries, MinColor, MaxColor;
|
||||
long Sum, Count;
|
||||
QuantizedColorType *QuantizedColor, **SortArray;
|
||||
|
||||
while (ColorMapSize > *NewColorMapSize) {
|
||||
/* Find candidate for subdivision: */
|
||||
MaxSize = -1;
|
||||
for (i = 0; i < *NewColorMapSize; i++) {
|
||||
for (j = 0; j < 3; j++) {
|
||||
if ((((int)NewColorSubdiv[i].RGBWidth[j]) > MaxSize) &&
|
||||
(NewColorSubdiv[i].NumEntries > 1)) {
|
||||
MaxSize = NewColorSubdiv[i].RGBWidth[j];
|
||||
Index = i;
|
||||
SortRGBAxis = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MaxSize == -1)
|
||||
return GIF_OK;
|
||||
|
||||
/* Split the entry Index into two along the axis SortRGBAxis: */
|
||||
|
||||
/* Sort all elements in that entry along the given axis and split at
|
||||
* the median. */
|
||||
SortArray = (QuantizedColorType **)malloc(
|
||||
sizeof(QuantizedColorType *) *
|
||||
NewColorSubdiv[Index].NumEntries);
|
||||
if (SortArray == NULL)
|
||||
return GIF_ERROR;
|
||||
for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors;
|
||||
j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL;
|
||||
j++, QuantizedColor = QuantizedColor->Pnext)
|
||||
SortArray[j] = QuantizedColor;
|
||||
|
||||
/*
|
||||
* Because qsort isn't stable, this can produce differing
|
||||
* results for the order of tuples depending on platform
|
||||
* details of how qsort() is implemented.
|
||||
*
|
||||
* We mitigate this problem by sorting on all three axes rather
|
||||
* than only the one specied by SortRGBAxis; that way the instability
|
||||
* can only become an issue if there are multiple color indices
|
||||
* referring to identical RGB tuples. Older versions of this
|
||||
* sorted on only the one axis.
|
||||
*/
|
||||
qsort(SortArray, NewColorSubdiv[Index].NumEntries,
|
||||
sizeof(QuantizedColorType *), SortCmpRtn);
|
||||
|
||||
/* Relink the sorted list into one: */
|
||||
for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++)
|
||||
SortArray[j]->Pnext = SortArray[j + 1];
|
||||
SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL;
|
||||
NewColorSubdiv[Index].QuantizedColors = QuantizedColor = SortArray[0];
|
||||
free((char *)SortArray);
|
||||
|
||||
/* Now simply add the Counts until we have half of the Count: */
|
||||
Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count;
|
||||
NumEntries = 1;
|
||||
Count = QuantizedColor->Count;
|
||||
while (QuantizedColor->Pnext != NULL &&
|
||||
(Sum -= QuantizedColor->Pnext->Count) >= 0 &&
|
||||
QuantizedColor->Pnext->Pnext != NULL) {
|
||||
QuantizedColor = QuantizedColor->Pnext;
|
||||
NumEntries++;
|
||||
Count += QuantizedColor->Count;
|
||||
}
|
||||
/* Save the values of the last color of the first half, and first
|
||||
* of the second half so we can update the Bounding Boxes later.
|
||||
* Also as the colors are quantized and the BBoxes are full 0..255,
|
||||
* they need to be rescaled.
|
||||
*/
|
||||
MaxColor = QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */
|
||||
/* coverity[var_deref_op] */
|
||||
MinColor = QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */
|
||||
MaxColor <<= (8 - BITS_PER_PRIM_COLOR);
|
||||
MinColor <<= (8 - BITS_PER_PRIM_COLOR);
|
||||
|
||||
/* Partition right here: */
|
||||
NewColorSubdiv[*NewColorMapSize].QuantizedColors =
|
||||
QuantizedColor->Pnext;
|
||||
QuantizedColor->Pnext = NULL;
|
||||
NewColorSubdiv[*NewColorMapSize].Count = Count;
|
||||
NewColorSubdiv[Index].Count -= Count;
|
||||
NewColorSubdiv[*NewColorMapSize].NumEntries =
|
||||
NewColorSubdiv[Index].NumEntries - NumEntries;
|
||||
NewColorSubdiv[Index].NumEntries = NumEntries;
|
||||
for (j = 0; j < 3; j++) {
|
||||
NewColorSubdiv[*NewColorMapSize].RGBMin[j] =
|
||||
NewColorSubdiv[Index].RGBMin[j];
|
||||
NewColorSubdiv[*NewColorMapSize].RGBWidth[j] =
|
||||
NewColorSubdiv[Index].RGBWidth[j];
|
||||
}
|
||||
NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] =
|
||||
NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] +
|
||||
NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - MinColor;
|
||||
NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor;
|
||||
|
||||
NewColorSubdiv[Index].RGBWidth[SortRGBAxis] =
|
||||
MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis];
|
||||
|
||||
(*NewColorMapSize)++;
|
||||
}
|
||||
|
||||
return GIF_OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Routine called by qsort to compare two entries.
|
||||
*****************************************************************************/
|
||||
|
||||
static int
|
||||
SortCmpRtn(const void *Entry1,
|
||||
const void *Entry2) {
|
||||
QuantizedColorType *entry1 = (*((QuantizedColorType **) Entry1));
|
||||
QuantizedColorType *entry2 = (*((QuantizedColorType **) Entry2));
|
||||
|
||||
/* sort on all axes of the color space! */
|
||||
int hash1 = entry1->RGB[SortRGBAxis] * 256 * 256
|
||||
+ entry1->RGB[(SortRGBAxis+1) % 3] * 256
|
||||
+ entry1->RGB[(SortRGBAxis+2) % 3];
|
||||
int hash2 = entry2->RGB[SortRGBAxis] * 256 * 256
|
||||
+ entry2->RGB[(SortRGBAxis+1) % 3] * 256
|
||||
+ entry2->RGB[(SortRGBAxis+2) % 3];
|
||||
|
||||
return hash1 - hash2;
|
||||
}
|
||||
|
||||
/* end */
|
||||
96
src/ext/lpeg/HISTORY
Normal file
96
src/ext/lpeg/HISTORY
Normal file
@@ -0,0 +1,96 @@
|
||||
HISTORY for LPeg 1.0
|
||||
|
||||
* Changes from version 0.12 to 1.0
|
||||
---------------------------------
|
||||
+ group "names" can be any Lua value
|
||||
+ some bugs fixed
|
||||
+ other small improvements
|
||||
|
||||
* Changes from version 0.11 to 0.12
|
||||
---------------------------------
|
||||
+ no "unsigned short" limit for pattern sizes
|
||||
+ mathtime captures considered nullable
|
||||
+ some bugs fixed
|
||||
|
||||
* Changes from version 0.10 to 0.11
|
||||
-------------------------------
|
||||
+ complete reimplementation of the code generator
|
||||
+ new syntax for table captures
|
||||
+ new functions in module 're'
|
||||
+ other small improvements
|
||||
|
||||
* Changes from version 0.9 to 0.10
|
||||
-------------------------------
|
||||
+ backtrack stack has configurable size
|
||||
+ better error messages
|
||||
+ Notation for non-terminals in 're' back to A instead o <A>
|
||||
+ experimental look-behind pattern
|
||||
+ support for external extensions
|
||||
+ works with Lua 5.2
|
||||
+ consumes less C stack
|
||||
|
||||
- "and" predicates do not keep captures
|
||||
|
||||
* Changes from version 0.8 to 0.9
|
||||
-------------------------------
|
||||
+ The accumulator capture was replaced by a fold capture;
|
||||
programs that used the old 'lpeg.Ca' will need small changes.
|
||||
+ Some support for character classes from old C locales.
|
||||
+ A new named-group capture.
|
||||
|
||||
* Changes from version 0.7 to 0.8
|
||||
-------------------------------
|
||||
+ New "match-time" capture.
|
||||
+ New "argument capture" that allows passing arguments into the pattern.
|
||||
+ Better documentation for 're'.
|
||||
+ Several small improvements for 're'.
|
||||
+ The 're' module has an incompatibility with previous versions:
|
||||
now, any use of a non-terminal must be enclosed in angle brackets
|
||||
(like <B>).
|
||||
|
||||
* Changes from version 0.6 to 0.7
|
||||
-------------------------------
|
||||
+ Several improvements in module 're':
|
||||
- better documentation;
|
||||
- support for most captures (all but accumulator);
|
||||
- limited repetitions p{n,m}.
|
||||
+ Small improvements in efficiency.
|
||||
+ Several small bugs corrected (special thanks to Hans Hagen
|
||||
and Taco Hoekwater).
|
||||
|
||||
* Changes from version 0.5 to 0.6
|
||||
-------------------------------
|
||||
+ Support for non-numeric indices in grammars.
|
||||
+ Some bug fixes (thanks to the luatex team).
|
||||
+ Some new optimizations; (thanks to Mike Pall).
|
||||
+ A new page layout (thanks to Andre Carregal).
|
||||
+ Minimal documentation for module 're'.
|
||||
|
||||
* Changes from version 0.4 to 0.5
|
||||
-------------------------------
|
||||
+ Several optimizations.
|
||||
+ lpeg.P now accepts booleans.
|
||||
+ Some new examples.
|
||||
+ A proper license.
|
||||
+ Several small improvements.
|
||||
|
||||
* Changes from version 0.3 to 0.4
|
||||
-------------------------------
|
||||
+ Static check for loops in repetitions and grammars.
|
||||
+ Removed label option in captures.
|
||||
+ The implementation of captures uses less memory.
|
||||
|
||||
* Changes from version 0.2 to 0.3
|
||||
-------------------------------
|
||||
+ User-defined patterns in Lua.
|
||||
+ Several new captures.
|
||||
|
||||
* Changes from version 0.1 to 0.2
|
||||
-------------------------------
|
||||
+ Several small corrections.
|
||||
+ Handles embedded zeros like any other character.
|
||||
+ Capture "name" can be any Lua value.
|
||||
+ Unlimited number of captures.
|
||||
+ Match gets an optional initial position.
|
||||
|
||||
(end of HISTORY)
|
||||
537
src/ext/lpeg/lpcap.c
Normal file
537
src/ext/lpeg/lpcap.c
Normal file
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
** $Id: lpcap.c,v 1.6 2015/06/15 16:09:57 roberto Exp $
|
||||
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
*/
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lpcap.h"
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
#define captype(cap) ((cap)->kind)
|
||||
|
||||
#define isclosecap(cap) (captype(cap) == Cclose)
|
||||
|
||||
#define closeaddr(c) ((c)->s + (c)->siz - 1)
|
||||
|
||||
#define isfullcap(cap) ((cap)->siz != 0)
|
||||
|
||||
#define getfromktable(cs,v) lua_rawgeti((cs)->L, ktableidx((cs)->ptop), v)
|
||||
|
||||
#define pushluaval(cs) getfromktable(cs, (cs)->cap->idx)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Put at the cache for Lua values the value indexed by 'v' in ktable
|
||||
** of the running pattern (if it is not there yet); returns its index.
|
||||
*/
|
||||
static int updatecache (CapState *cs, int v) {
|
||||
int idx = cs->ptop + 1; /* stack index of cache for Lua values */
|
||||
if (v != cs->valuecached) { /* not there? */
|
||||
getfromktable(cs, v); /* get value from 'ktable' */
|
||||
lua_replace(cs->L, idx); /* put it at reserved stack position */
|
||||
cs->valuecached = v; /* keep track of what is there */
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
static int pushcapture (CapState *cs);
|
||||
|
||||
|
||||
/*
|
||||
** Goes back in a list of captures looking for an open capture
|
||||
** corresponding to a close
|
||||
*/
|
||||
static Capture *findopen (Capture *cap) {
|
||||
int n = 0; /* number of closes waiting an open */
|
||||
for (;;) {
|
||||
cap--;
|
||||
if (isclosecap(cap)) n++; /* one more open to skip */
|
||||
else if (!isfullcap(cap))
|
||||
if (n-- == 0) return cap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Go to the next capture
|
||||
*/
|
||||
static void nextcap (CapState *cs) {
|
||||
Capture *cap = cs->cap;
|
||||
if (!isfullcap(cap)) { /* not a single capture? */
|
||||
int n = 0; /* number of opens waiting a close */
|
||||
for (;;) { /* look for corresponding close */
|
||||
cap++;
|
||||
if (isclosecap(cap)) {
|
||||
if (n-- == 0) break;
|
||||
}
|
||||
else if (!isfullcap(cap)) n++;
|
||||
}
|
||||
}
|
||||
cs->cap = cap + 1; /* + 1 to skip last close (or entire single capture) */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push on the Lua stack all values generated by nested captures inside
|
||||
** the current capture. Returns number of values pushed. 'addextra'
|
||||
** makes it push the entire match after all captured values. The
|
||||
** entire match is pushed also if there are no other nested values,
|
||||
** so the function never returns zero.
|
||||
*/
|
||||
static int pushnestedvalues (CapState *cs, int addextra) {
|
||||
Capture *co = cs->cap;
|
||||
if (isfullcap(cs->cap++)) { /* no nested captures? */
|
||||
lua_pushlstring(cs->L, co->s, co->siz - 1); /* push whole match */
|
||||
return 1; /* that is it */
|
||||
}
|
||||
else {
|
||||
int n = 0;
|
||||
while (!isclosecap(cs->cap)) /* repeat for all nested patterns */
|
||||
n += pushcapture(cs);
|
||||
if (addextra || n == 0) { /* need extra? */
|
||||
lua_pushlstring(cs->L, co->s, cs->cap->s - co->s); /* push whole match */
|
||||
n++;
|
||||
}
|
||||
cs->cap++; /* skip close entry */
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push only the first value generated by nested captures
|
||||
*/
|
||||
static void pushonenestedvalue (CapState *cs) {
|
||||
int n = pushnestedvalues(cs, 0);
|
||||
if (n > 1)
|
||||
lua_pop(cs->L, n - 1); /* pop extra values */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to find a named group capture with the name given at the top of
|
||||
** the stack; goes backward from 'cap'.
|
||||
*/
|
||||
static Capture *findback (CapState *cs, Capture *cap) {
|
||||
lua_State *L = cs->L;
|
||||
while (cap-- > cs->ocap) { /* repeat until end of list */
|
||||
if (isclosecap(cap))
|
||||
cap = findopen(cap); /* skip nested captures */
|
||||
else if (!isfullcap(cap))
|
||||
continue; /* opening an enclosing capture: skip and get previous */
|
||||
if (captype(cap) == Cgroup) {
|
||||
getfromktable(cs, cap->idx); /* get group name */
|
||||
if (lp_equal(L, -2, -1)) { /* right group? */
|
||||
lua_pop(L, 2); /* remove reference name and group name */
|
||||
return cap;
|
||||
}
|
||||
else lua_pop(L, 1); /* remove group name */
|
||||
}
|
||||
}
|
||||
luaL_error(L, "back reference '%s' not found", lua_tostring(L, -1));
|
||||
return NULL; /* to avoid warnings */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Back-reference capture. Return number of values pushed.
|
||||
*/
|
||||
static int backrefcap (CapState *cs) {
|
||||
int n;
|
||||
Capture *curr = cs->cap;
|
||||
pushluaval(cs); /* reference name */
|
||||
cs->cap = findback(cs, curr); /* find corresponding group */
|
||||
n = pushnestedvalues(cs, 0); /* push group's values */
|
||||
cs->cap = curr + 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Table capture: creates a new table and populates it with nested
|
||||
** captures.
|
||||
*/
|
||||
static int tablecap (CapState *cs) {
|
||||
lua_State *L = cs->L;
|
||||
int n = 0;
|
||||
lua_newtable(L);
|
||||
if (isfullcap(cs->cap++))
|
||||
return 1; /* table is empty */
|
||||
while (!isclosecap(cs->cap)) {
|
||||
if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) { /* named group? */
|
||||
pushluaval(cs); /* push group name */
|
||||
pushonenestedvalue(cs);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
else { /* not a named group */
|
||||
int i;
|
||||
int k = pushcapture(cs);
|
||||
for (i = k; i > 0; i--) /* store all values into table */
|
||||
lua_rawseti(L, -(i + 1), n + i);
|
||||
n += k;
|
||||
}
|
||||
}
|
||||
cs->cap++; /* skip close entry */
|
||||
return 1; /* number of values pushed (only the table) */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Table-query capture
|
||||
*/
|
||||
static int querycap (CapState *cs) {
|
||||
int idx = cs->cap->idx;
|
||||
pushonenestedvalue(cs); /* get nested capture */
|
||||
lua_gettable(cs->L, updatecache(cs, idx)); /* query cap. value at table */
|
||||
if (!lua_isnil(cs->L, -1))
|
||||
return 1;
|
||||
else { /* no value */
|
||||
lua_pop(cs->L, 1); /* remove nil */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Fold capture
|
||||
*/
|
||||
static int foldcap (CapState *cs) {
|
||||
int n;
|
||||
lua_State *L = cs->L;
|
||||
int idx = cs->cap->idx;
|
||||
if (isfullcap(cs->cap++) || /* no nested captures? */
|
||||
isclosecap(cs->cap) || /* no nested captures (large subject)? */
|
||||
(n = pushcapture(cs)) == 0) /* nested captures with no values? */
|
||||
return luaL_error(L, "no initial value for fold capture");
|
||||
if (n > 1)
|
||||
lua_pop(L, n - 1); /* leave only one result for accumulator */
|
||||
while (!isclosecap(cs->cap)) {
|
||||
lua_pushvalue(L, updatecache(cs, idx)); /* get folding function */
|
||||
lua_insert(L, -2); /* put it before accumulator */
|
||||
n = pushcapture(cs); /* get next capture's values */
|
||||
lua_call(L, n + 1, 1); /* call folding function */
|
||||
}
|
||||
cs->cap++; /* skip close entry */
|
||||
return 1; /* only accumulator left on the stack */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Function capture
|
||||
*/
|
||||
static int functioncap (CapState *cs) {
|
||||
int n;
|
||||
int top = lua_gettop(cs->L);
|
||||
pushluaval(cs); /* push function */
|
||||
n = pushnestedvalues(cs, 0); /* push nested captures */
|
||||
lua_call(cs->L, n, LUA_MULTRET); /* call function */
|
||||
return lua_gettop(cs->L) - top; /* return function's results */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Select capture
|
||||
*/
|
||||
static int numcap (CapState *cs) {
|
||||
int idx = cs->cap->idx; /* value to select */
|
||||
if (idx == 0) { /* no values? */
|
||||
nextcap(cs); /* skip entire capture */
|
||||
return 0; /* no value produced */
|
||||
}
|
||||
else {
|
||||
int n = pushnestedvalues(cs, 0);
|
||||
if (n < idx) /* invalid index? */
|
||||
return luaL_error(cs->L, "no capture '%d'", idx);
|
||||
else {
|
||||
lua_pushvalue(cs->L, -(n - idx + 1)); /* get selected capture */
|
||||
lua_replace(cs->L, -(n + 1)); /* put it in place of 1st capture */
|
||||
lua_pop(cs->L, n - 1); /* remove other captures */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return the stack index of the first runtime capture in the given
|
||||
** list of captures (or zero if no runtime captures)
|
||||
*/
|
||||
int finddyncap (Capture *cap, Capture *last) {
|
||||
for (; cap < last; cap++) {
|
||||
if (cap->kind == Cruntime)
|
||||
return cap->idx; /* stack position of first capture */
|
||||
}
|
||||
return 0; /* no dynamic captures in this segment */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Calls a runtime capture. Returns number of captures removed by
|
||||
** the call, including the initial Cgroup. (Captures to be added are
|
||||
** on the Lua stack.)
|
||||
*/
|
||||
int runtimecap (CapState *cs, Capture *close, const char *s, int *rem) {
|
||||
int n, id;
|
||||
lua_State *L = cs->L;
|
||||
int otop = lua_gettop(L);
|
||||
Capture *open = findopen(close);
|
||||
assert(captype(open) == Cgroup);
|
||||
id = finddyncap(open, close); /* get first dynamic capture argument */
|
||||
close->kind = Cclose; /* closes the group */
|
||||
close->s = s;
|
||||
cs->cap = open; cs->valuecached = 0; /* prepare capture state */
|
||||
luaL_checkstack(L, 4, "too many runtime captures");
|
||||
pushluaval(cs); /* push function to be called */
|
||||
lua_pushvalue(L, SUBJIDX); /* push original subject */
|
||||
lua_pushinteger(L, s - cs->s + 1); /* push current position */
|
||||
n = pushnestedvalues(cs, 0); /* push nested captures */
|
||||
lua_call(L, n + 2, LUA_MULTRET); /* call dynamic function */
|
||||
if (id > 0) { /* are there old dynamic captures to be removed? */
|
||||
int i;
|
||||
for (i = id; i <= otop; i++)
|
||||
lua_remove(L, id); /* remove old dynamic captures */
|
||||
*rem = otop - id + 1; /* total number of dynamic captures removed */
|
||||
}
|
||||
else
|
||||
*rem = 0; /* no dynamic captures removed */
|
||||
return close - open; /* number of captures of all kinds removed */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Auxiliary structure for substitution and string captures: keep
|
||||
** information about nested captures for future use, avoiding to push
|
||||
** string results into Lua
|
||||
*/
|
||||
typedef struct StrAux {
|
||||
int isstring; /* whether capture is a string */
|
||||
union {
|
||||
Capture *cp; /* if not a string, respective capture */
|
||||
struct { /* if it is a string... */
|
||||
const char *s; /* ... starts here */
|
||||
const char *e; /* ... ends here */
|
||||
} s;
|
||||
} u;
|
||||
} StrAux;
|
||||
|
||||
#define MAXSTRCAPS 10
|
||||
|
||||
/*
|
||||
** Collect values from current capture into array 'cps'. Current
|
||||
** capture must be Cstring (first call) or Csimple (recursive calls).
|
||||
** (In first call, fills %0 with whole match for Cstring.)
|
||||
** Returns number of elements in the array that were filled.
|
||||
*/
|
||||
static int getstrcaps (CapState *cs, StrAux *cps, int n) {
|
||||
int k = n++;
|
||||
cps[k].isstring = 1; /* get string value */
|
||||
cps[k].u.s.s = cs->cap->s; /* starts here */
|
||||
if (!isfullcap(cs->cap++)) { /* nested captures? */
|
||||
while (!isclosecap(cs->cap)) { /* traverse them */
|
||||
if (n >= MAXSTRCAPS) /* too many captures? */
|
||||
nextcap(cs); /* skip extra captures (will not need them) */
|
||||
else if (captype(cs->cap) == Csimple) /* string? */
|
||||
n = getstrcaps(cs, cps, n); /* put info. into array */
|
||||
else {
|
||||
cps[n].isstring = 0; /* not a string */
|
||||
cps[n].u.cp = cs->cap; /* keep original capture */
|
||||
nextcap(cs);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
cs->cap++; /* skip close */
|
||||
}
|
||||
cps[k].u.s.e = closeaddr(cs->cap - 1); /* ends here */
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** add next capture value (which should be a string) to buffer 'b'
|
||||
*/
|
||||
static int addonestring (luaL_Buffer *b, CapState *cs, const char *what);
|
||||
|
||||
|
||||
/*
|
||||
** String capture: add result to buffer 'b' (instead of pushing
|
||||
** it into the stack)
|
||||
*/
|
||||
static void stringcap (luaL_Buffer *b, CapState *cs) {
|
||||
StrAux cps[MAXSTRCAPS];
|
||||
int n;
|
||||
size_t len, i;
|
||||
const char *fmt; /* format string */
|
||||
fmt = lua_tolstring(cs->L, updatecache(cs, cs->cap->idx), &len);
|
||||
n = getstrcaps(cs, cps, 0) - 1; /* collect nested captures */
|
||||
for (i = 0; i < len; i++) { /* traverse them */
|
||||
if (fmt[i] != '%') /* not an escape? */
|
||||
luaL_addchar(b, fmt[i]); /* add it to buffer */
|
||||
else if (fmt[++i] < '0' || fmt[i] > '9') /* not followed by a digit? */
|
||||
luaL_addchar(b, fmt[i]); /* add to buffer */
|
||||
else {
|
||||
int l = fmt[i] - '0'; /* capture index */
|
||||
if (l > n)
|
||||
luaL_error(cs->L, "invalid capture index (%d)", l);
|
||||
else if (cps[l].isstring)
|
||||
luaL_addlstring(b, cps[l].u.s.s, cps[l].u.s.e - cps[l].u.s.s);
|
||||
else {
|
||||
Capture *curr = cs->cap;
|
||||
cs->cap = cps[l].u.cp; /* go back to evaluate that nested capture */
|
||||
if (!addonestring(b, cs, "capture"))
|
||||
luaL_error(cs->L, "no values in capture index %d", l);
|
||||
cs->cap = curr; /* continue from where it stopped */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Substitution capture: add result to buffer 'b'
|
||||
*/
|
||||
static void substcap (luaL_Buffer *b, CapState *cs) {
|
||||
const char *curr = cs->cap->s;
|
||||
if (isfullcap(cs->cap)) /* no nested captures? */
|
||||
luaL_addlstring(b, curr, cs->cap->siz - 1); /* keep original text */
|
||||
else {
|
||||
cs->cap++; /* skip open entry */
|
||||
while (!isclosecap(cs->cap)) { /* traverse nested captures */
|
||||
const char *next = cs->cap->s;
|
||||
luaL_addlstring(b, curr, next - curr); /* add text up to capture */
|
||||
if (addonestring(b, cs, "replacement"))
|
||||
curr = closeaddr(cs->cap - 1); /* continue after match */
|
||||
else /* no capture value */
|
||||
curr = next; /* keep original text in final result */
|
||||
}
|
||||
luaL_addlstring(b, curr, cs->cap->s - curr); /* add last piece of text */
|
||||
}
|
||||
cs->cap++; /* go to next capture */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Evaluates a capture and adds its first value to buffer 'b'; returns
|
||||
** whether there was a value
|
||||
*/
|
||||
static int addonestring (luaL_Buffer *b, CapState *cs, const char *what) {
|
||||
switch (captype(cs->cap)) {
|
||||
case Cstring:
|
||||
stringcap(b, cs); /* add capture directly to buffer */
|
||||
return 1;
|
||||
case Csubst:
|
||||
substcap(b, cs); /* add capture directly to buffer */
|
||||
return 1;
|
||||
default: {
|
||||
lua_State *L = cs->L;
|
||||
int n = pushcapture(cs);
|
||||
if (n > 0) {
|
||||
if (n > 1) lua_pop(L, n - 1); /* only one result */
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "invalid %s value (a %s)", what, luaL_typename(L, -1));
|
||||
luaL_addvalue(b);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push all values of the current capture into the stack; returns
|
||||
** number of values pushed
|
||||
*/
|
||||
static int pushcapture (CapState *cs) {
|
||||
lua_State *L = cs->L;
|
||||
luaL_checkstack(L, 4, "too many captures");
|
||||
switch (captype(cs->cap)) {
|
||||
case Cposition: {
|
||||
lua_pushinteger(L, cs->cap->s - cs->s + 1);
|
||||
cs->cap++;
|
||||
return 1;
|
||||
}
|
||||
case Cconst: {
|
||||
pushluaval(cs);
|
||||
cs->cap++;
|
||||
return 1;
|
||||
}
|
||||
case Carg: {
|
||||
int arg = (cs->cap++)->idx;
|
||||
if (arg + FIXEDARGS > cs->ptop)
|
||||
return luaL_error(L, "reference to absent extra argument #%d", arg);
|
||||
lua_pushvalue(L, arg + FIXEDARGS);
|
||||
return 1;
|
||||
}
|
||||
case Csimple: {
|
||||
int k = pushnestedvalues(cs, 1);
|
||||
lua_insert(L, -k); /* make whole match be first result */
|
||||
return k;
|
||||
}
|
||||
case Cruntime: {
|
||||
lua_pushvalue(L, (cs->cap++)->idx); /* value is in the stack */
|
||||
return 1;
|
||||
}
|
||||
case Cstring: {
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
stringcap(&b, cs);
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
case Csubst: {
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
substcap(&b, cs);
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
case Cgroup: {
|
||||
if (cs->cap->idx == 0) /* anonymous group? */
|
||||
return pushnestedvalues(cs, 0); /* add all nested values */
|
||||
else { /* named group: add no values */
|
||||
nextcap(cs); /* skip capture */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
case Cbackref: return backrefcap(cs);
|
||||
case Ctable: return tablecap(cs);
|
||||
case Cfunction: return functioncap(cs);
|
||||
case Cnum: return numcap(cs);
|
||||
case Cquery: return querycap(cs);
|
||||
case Cfold: return foldcap(cs);
|
||||
default: assert(0); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare a CapState structure and traverse the entire list of
|
||||
** captures in the stack pushing its results. 's' is the subject
|
||||
** string, 'r' is the final position of the match, and 'ptop'
|
||||
** the index in the stack where some useful values were pushed.
|
||||
** Returns the number of results pushed. (If the list produces no
|
||||
** results, push the final position of the match.)
|
||||
*/
|
||||
int getcaptures (lua_State *L, const char *s, const char *r, int ptop) {
|
||||
Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop));
|
||||
int n = 0;
|
||||
if (!isclosecap(capture)) { /* is there any capture? */
|
||||
CapState cs;
|
||||
cs.ocap = cs.cap = capture; cs.L = L;
|
||||
cs.s = s; cs.valuecached = 0; cs.ptop = ptop;
|
||||
do { /* collect their values */
|
||||
n += pushcapture(&cs);
|
||||
} while (!isclosecap(cs.cap));
|
||||
}
|
||||
if (n == 0) { /* no capture values? */
|
||||
lua_pushinteger(L, r - s + 1); /* return only end position */
|
||||
n = 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
56
src/ext/lpeg/lpcap.h
Normal file
56
src/ext/lpeg/lpcap.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
** $Id: lpcap.h,v 1.3 2016/09/13 17:45:58 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lpcap_h)
|
||||
#define lpcap_h
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
/* kinds of captures */
|
||||
typedef enum CapKind {
|
||||
Cclose, /* not used in trees */
|
||||
Cposition,
|
||||
Cconst, /* ktable[key] is Lua constant */
|
||||
Cbackref, /* ktable[key] is "name" of group to get capture */
|
||||
Carg, /* 'key' is arg's number */
|
||||
Csimple, /* next node is pattern */
|
||||
Ctable, /* next node is pattern */
|
||||
Cfunction, /* ktable[key] is function; next node is pattern */
|
||||
Cquery, /* ktable[key] is table; next node is pattern */
|
||||
Cstring, /* ktable[key] is string; next node is pattern */
|
||||
Cnum, /* numbered capture; 'key' is number of value to return */
|
||||
Csubst, /* substitution capture; next node is pattern */
|
||||
Cfold, /* ktable[key] is function; next node is pattern */
|
||||
Cruntime, /* not used in trees (is uses another type for tree) */
|
||||
Cgroup /* ktable[key] is group's "name" */
|
||||
} CapKind;
|
||||
|
||||
|
||||
typedef struct Capture {
|
||||
const char *s; /* subject position */
|
||||
unsigned short idx; /* extra info (group name, arg index, etc.) */
|
||||
byte kind; /* kind of capture */
|
||||
byte siz; /* size of full capture + 1 (0 = not a full capture) */
|
||||
} Capture;
|
||||
|
||||
|
||||
typedef struct CapState {
|
||||
Capture *cap; /* current capture */
|
||||
Capture *ocap; /* (original) capture list */
|
||||
lua_State *L;
|
||||
int ptop; /* index of last argument to 'match' */
|
||||
const char *s; /* original string */
|
||||
int valuecached; /* value stored in cache slot */
|
||||
} CapState;
|
||||
|
||||
|
||||
int runtimecap (CapState *cs, Capture *close, const char *s, int *rem);
|
||||
int getcaptures (lua_State *L, const char *s, const char *r, int ptop);
|
||||
int finddyncap (Capture *cap, Capture *last);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
1014
src/ext/lpeg/lpcode.c
Normal file
1014
src/ext/lpeg/lpcode.c
Normal file
File diff suppressed because it is too large
Load Diff
40
src/ext/lpeg/lpcode.h
Normal file
40
src/ext/lpeg/lpcode.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
** $Id: lpcode.h,v 1.8 2016/09/15 17:46:13 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lpcode_h)
|
||||
#define lpcode_h
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lptypes.h"
|
||||
#include "lptree.h"
|
||||
#include "lpvm.h"
|
||||
|
||||
int tocharset (TTree *tree, Charset *cs);
|
||||
int checkaux (TTree *tree, int pred);
|
||||
int fixedlen (TTree *tree);
|
||||
int hascaptures (TTree *tree);
|
||||
int lp_gc (lua_State *L);
|
||||
Instruction *compile (lua_State *L, Pattern *p);
|
||||
void realloccode (lua_State *L, Pattern *p, int nsize);
|
||||
int sizei (const Instruction *i);
|
||||
|
||||
|
||||
#define PEnullable 0
|
||||
#define PEnofail 1
|
||||
|
||||
/*
|
||||
** nofail(t) implies that 't' cannot fail with any input
|
||||
*/
|
||||
#define nofail(t) checkaux(t, PEnofail)
|
||||
|
||||
/*
|
||||
** (not nullable(t)) implies 't' cannot match without consuming
|
||||
** something
|
||||
*/
|
||||
#define nullable(t) checkaux(t, PEnullable)
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
BIN
src/ext/lpeg/lpeg-128.gif
Normal file
BIN
src/ext/lpeg/lpeg-128.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
1445
src/ext/lpeg/lpeg.html
Normal file
1445
src/ext/lpeg/lpeg.html
Normal file
File diff suppressed because it is too large
Load Diff
244
src/ext/lpeg/lpprint.c
Normal file
244
src/ext/lpeg/lpprint.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
** $Id: lpprint.c,v 1.10 2016/09/13 16:06:03 roberto Exp $
|
||||
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
#include "lpprint.h"
|
||||
#include "lpcode.h"
|
||||
|
||||
|
||||
#if defined(LPEG_DEBUG)
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Printing patterns (for debugging)
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
void printcharset (const byte *st) {
|
||||
int i;
|
||||
printf("[");
|
||||
for (i = 0; i <= UCHAR_MAX; i++) {
|
||||
int first = i;
|
||||
while (testchar(st, i) && i <= UCHAR_MAX) i++;
|
||||
if (i - 1 == first) /* unary range? */
|
||||
printf("(%02x)", first);
|
||||
else if (i - 1 > first) /* non-empty range? */
|
||||
printf("(%02x-%02x)", first, i - 1);
|
||||
}
|
||||
printf("]");
|
||||
}
|
||||
|
||||
|
||||
static const char *capkind (int kind) {
|
||||
const char *const modes[] = {
|
||||
"close", "position", "constant", "backref",
|
||||
"argument", "simple", "table", "function",
|
||||
"query", "string", "num", "substitution", "fold",
|
||||
"runtime", "group"};
|
||||
return modes[kind];
|
||||
}
|
||||
|
||||
|
||||
static void printjmp (const Instruction *op, const Instruction *p) {
|
||||
printf("-> %d", (int)(p + (p + 1)->offset - op));
|
||||
}
|
||||
|
||||
|
||||
void printinst (const Instruction *op, const Instruction *p) {
|
||||
const char *const names[] = {
|
||||
"any", "char", "set",
|
||||
"testany", "testchar", "testset",
|
||||
"span", "behind",
|
||||
"ret", "end",
|
||||
"choice", "jmp", "call", "open_call",
|
||||
"commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup",
|
||||
"fullcapture", "opencapture", "closecapture", "closeruntime"
|
||||
};
|
||||
printf("%02ld: %s ", (long)(p - op), names[p->i.code]);
|
||||
switch ((Opcode)p->i.code) {
|
||||
case IChar: {
|
||||
printf("'%c'", p->i.aux);
|
||||
break;
|
||||
}
|
||||
case ITestChar: {
|
||||
printf("'%c'", p->i.aux); printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
case IFullCapture: {
|
||||
printf("%s (size = %d) (idx = %d)",
|
||||
capkind(getkind(p)), getoff(p), p->i.key);
|
||||
break;
|
||||
}
|
||||
case IOpenCapture: {
|
||||
printf("%s (idx = %d)", capkind(getkind(p)), p->i.key);
|
||||
break;
|
||||
}
|
||||
case ISet: {
|
||||
printcharset((p+1)->buff);
|
||||
break;
|
||||
}
|
||||
case ITestSet: {
|
||||
printcharset((p+2)->buff); printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
case ISpan: {
|
||||
printcharset((p+1)->buff);
|
||||
break;
|
||||
}
|
||||
case IOpenCall: {
|
||||
printf("-> %d", (p + 1)->offset);
|
||||
break;
|
||||
}
|
||||
case IBehind: {
|
||||
printf("%d", p->i.aux);
|
||||
break;
|
||||
}
|
||||
case IJmp: case ICall: case ICommit: case IChoice:
|
||||
case IPartialCommit: case IBackCommit: case ITestAny: {
|
||||
printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
void printpatt (Instruction *p, int n) {
|
||||
Instruction *op = p;
|
||||
while (p < op + n) {
|
||||
printinst(op, p);
|
||||
p += sizei(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(LPEG_DEBUG)
|
||||
static void printcap (Capture *cap) {
|
||||
printf("%s (idx: %d - size: %d) -> %p\n",
|
||||
capkind(cap->kind), cap->idx, cap->siz, cap->s);
|
||||
}
|
||||
|
||||
|
||||
void printcaplist (Capture *cap, Capture *limit) {
|
||||
printf(">======\n");
|
||||
for (; cap->s && (limit == NULL || cap < limit); cap++)
|
||||
printcap(cap);
|
||||
printf("=======\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Printing trees (for debugging)
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static const char *tagnames[] = {
|
||||
"char", "set", "any",
|
||||
"true", "false",
|
||||
"rep",
|
||||
"seq", "choice",
|
||||
"not", "and",
|
||||
"call", "opencall", "rule", "grammar",
|
||||
"behind",
|
||||
"capture", "run-time"
|
||||
};
|
||||
|
||||
|
||||
void printtree (TTree *tree, int ident) {
|
||||
int i;
|
||||
for (i = 0; i < ident; i++) printf(" ");
|
||||
printf("%s", tagnames[tree->tag]);
|
||||
switch (tree->tag) {
|
||||
case TChar: {
|
||||
int c = tree->u.n;
|
||||
if (isprint(c))
|
||||
printf(" '%c'\n", c);
|
||||
else
|
||||
printf(" (%02X)\n", c);
|
||||
break;
|
||||
}
|
||||
case TSet: {
|
||||
printcharset(treebuffer(tree));
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
case TOpenCall: case TCall: {
|
||||
assert(sib2(tree)->tag == TRule);
|
||||
printf(" key: %d (rule: %d)\n", tree->key, sib2(tree)->cap);
|
||||
break;
|
||||
}
|
||||
case TBehind: {
|
||||
printf(" %d\n", tree->u.n);
|
||||
printtree(sib1(tree), ident + 2);
|
||||
break;
|
||||
}
|
||||
case TCapture: {
|
||||
printf(" kind: '%s' key: %d\n", capkind(tree->cap), tree->key);
|
||||
printtree(sib1(tree), ident + 2);
|
||||
break;
|
||||
}
|
||||
case TRule: {
|
||||
printf(" n: %d key: %d\n", tree->cap, tree->key);
|
||||
printtree(sib1(tree), ident + 2);
|
||||
break; /* do not print next rule as a sibling */
|
||||
}
|
||||
case TGrammar: {
|
||||
TTree *rule = sib1(tree);
|
||||
printf(" %d\n", tree->u.n); /* number of rules */
|
||||
for (i = 0; i < tree->u.n; i++) {
|
||||
printtree(rule, ident + 2);
|
||||
rule = sib2(rule);
|
||||
}
|
||||
assert(rule->tag == TTrue); /* sentinel */
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
int sibs = numsiblings[tree->tag];
|
||||
printf("\n");
|
||||
if (sibs >= 1) {
|
||||
printtree(sib1(tree), ident + 2);
|
||||
if (sibs >= 2)
|
||||
printtree(sib2(tree), ident + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printktable (lua_State *L, int idx) {
|
||||
int n, i;
|
||||
lua_getuservalue(L, idx);
|
||||
if (lua_isnil(L, -1)) /* no ktable? */
|
||||
return;
|
||||
n = lua_rawlen(L, -1);
|
||||
printf("[");
|
||||
for (i = 1; i <= n; i++) {
|
||||
printf("%d = ", i);
|
||||
lua_rawgeti(L, -1, i);
|
||||
if (lua_isstring(L, -1))
|
||||
printf("%s ", lua_tostring(L, -1));
|
||||
else
|
||||
printf("%s ", lua_typename(L, lua_type(L, -1)));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
printf("]\n");
|
||||
/* leave ktable at the stack */
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
#endif
|
||||
36
src/ext/lpeg/lpprint.h
Normal file
36
src/ext/lpeg/lpprint.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
** $Id: lpprint.h,v 1.2 2015/06/12 18:18:08 roberto Exp $
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(lpprint_h)
|
||||
#define lpprint_h
|
||||
|
||||
|
||||
#include "lptree.h"
|
||||
#include "lpvm.h"
|
||||
|
||||
|
||||
#if defined(LPEG_DEBUG)
|
||||
|
||||
void printpatt (Instruction *p, int n);
|
||||
void printtree (TTree *tree, int ident);
|
||||
void printktable (lua_State *L, int idx);
|
||||
void printcharset (const byte *st);
|
||||
void printcaplist (Capture *cap, Capture *limit);
|
||||
void printinst (const Instruction *op, const Instruction *p);
|
||||
|
||||
#else
|
||||
|
||||
#define printktable(L,idx) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
#define printtree(tree,i) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
#define printpatt(p,n) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
1303
src/ext/lpeg/lptree.c
Normal file
1303
src/ext/lpeg/lptree.c
Normal file
File diff suppressed because it is too large
Load Diff
82
src/ext/lpeg/lptree.h
Normal file
82
src/ext/lpeg/lptree.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
** $Id: lptree.h,v 1.3 2016/09/13 18:07:51 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lptree_h)
|
||||
#define lptree_h
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
/*
|
||||
** types of trees
|
||||
*/
|
||||
typedef enum TTag {
|
||||
TChar = 0, /* 'n' = char */
|
||||
TSet, /* the set is stored in next CHARSETSIZE bytes */
|
||||
TAny,
|
||||
TTrue,
|
||||
TFalse,
|
||||
TRep, /* 'sib1'* */
|
||||
TSeq, /* 'sib1' 'sib2' */
|
||||
TChoice, /* 'sib1' / 'sib2' */
|
||||
TNot, /* !'sib1' */
|
||||
TAnd, /* &'sib1' */
|
||||
TCall, /* ktable[key] is rule's key; 'sib2' is rule being called */
|
||||
TOpenCall, /* ktable[key] is rule's key */
|
||||
TRule, /* ktable[key] is rule's key (but key == 0 for unused rules);
|
||||
'sib1' is rule's pattern;
|
||||
'sib2' is next rule; 'cap' is rule's sequential number */
|
||||
TGrammar, /* 'sib1' is initial (and first) rule */
|
||||
TBehind, /* 'sib1' is pattern, 'n' is how much to go back */
|
||||
TCapture, /* captures: 'cap' is kind of capture (enum 'CapKind');
|
||||
ktable[key] is Lua value associated with capture;
|
||||
'sib1' is capture body */
|
||||
TRunTime /* run-time capture: 'key' is Lua function;
|
||||
'sib1' is capture body */
|
||||
} TTag;
|
||||
|
||||
|
||||
/*
|
||||
** Tree trees
|
||||
** The first child of a tree (if there is one) is immediately after
|
||||
** the tree. A reference to a second child (ps) is its position
|
||||
** relative to the position of the tree itself.
|
||||
*/
|
||||
typedef struct TTree {
|
||||
byte tag;
|
||||
byte cap; /* kind of capture (if it is a capture) */
|
||||
unsigned short key; /* key in ktable for Lua data (0 if no key) */
|
||||
union {
|
||||
int ps; /* occasional second child */
|
||||
int n; /* occasional counter */
|
||||
} u;
|
||||
} TTree;
|
||||
|
||||
|
||||
/*
|
||||
** A complete pattern has its tree plus, if already compiled,
|
||||
** its corresponding code
|
||||
*/
|
||||
typedef struct Pattern {
|
||||
union Instruction *code;
|
||||
int codesize;
|
||||
TTree tree[1];
|
||||
} Pattern;
|
||||
|
||||
|
||||
/* number of children for each tree */
|
||||
extern const byte numsiblings[];
|
||||
|
||||
/* access to children */
|
||||
#define sib1(t) ((t) + 1)
|
||||
#define sib2(t) ((t) + (t)->u.ps)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
149
src/ext/lpeg/lptypes.h
Normal file
149
src/ext/lpeg/lptypes.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
** $Id: lptypes.h,v 1.16 2017/01/13 13:33:17 roberto Exp $
|
||||
** LPeg - PEG pattern matching for Lua
|
||||
** Copyright 2007-2017, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
** written by Roberto Ierusalimschy
|
||||
*/
|
||||
|
||||
#if !defined(lptypes_h)
|
||||
#define lptypes_h
|
||||
|
||||
|
||||
#if !defined(LPEG_DEBUG)
|
||||
#define NDEBUG
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
#define VERSION "1.0.1"
|
||||
|
||||
|
||||
#define PATTERN_T "lpeg-pattern"
|
||||
#define MAXSTACKIDX "lpeg-maxstack"
|
||||
|
||||
|
||||
/*
|
||||
** compatibility with Lua 5.1
|
||||
*/
|
||||
#if (LUA_VERSION_NUM == 501)
|
||||
|
||||
#define lp_equal lua_equal
|
||||
|
||||
#define lua_getuservalue lua_getfenv
|
||||
#define lua_setuservalue lua_setfenv
|
||||
|
||||
#define lua_rawlen lua_objlen
|
||||
|
||||
#define luaL_setfuncs(L,f,n) luaL_register(L,NULL,f)
|
||||
#define luaL_newlib(L,f) luaL_register(L,"lpeg",f)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(lp_equal)
|
||||
#define lp_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
|
||||
#endif
|
||||
|
||||
|
||||
/* default maximum size for call/backtrack stack */
|
||||
#if !defined(MAXBACK)
|
||||
#define MAXBACK 400
|
||||
#endif
|
||||
|
||||
|
||||
/* maximum number of rules in a grammar (limited by 'unsigned char') */
|
||||
#if !defined(MAXRULES)
|
||||
#define MAXRULES 250
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* initial size for capture's list */
|
||||
#define INITCAPSIZE 32
|
||||
|
||||
|
||||
/* index, on Lua stack, for subject */
|
||||
#define SUBJIDX 2
|
||||
|
||||
/* number of fixed arguments to 'match' (before capture arguments) */
|
||||
#define FIXEDARGS 3
|
||||
|
||||
/* index, on Lua stack, for capture list */
|
||||
#define caplistidx(ptop) ((ptop) + 2)
|
||||
|
||||
/* index, on Lua stack, for pattern's ktable */
|
||||
#define ktableidx(ptop) ((ptop) + 3)
|
||||
|
||||
/* index, on Lua stack, for backtracking stack */
|
||||
#define stackidx(ptop) ((ptop) + 4)
|
||||
|
||||
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
|
||||
#define BITSPERCHAR 8
|
||||
|
||||
#define CHARSETSIZE ((UCHAR_MAX/BITSPERCHAR) + 1)
|
||||
|
||||
|
||||
|
||||
typedef struct Charset {
|
||||
byte cs[CHARSETSIZE];
|
||||
} Charset;
|
||||
|
||||
|
||||
|
||||
#define loopset(v,b) { int v; for (v = 0; v < CHARSETSIZE; v++) {b;} }
|
||||
|
||||
/* access to charset */
|
||||
#define treebuffer(t) ((byte *)((t) + 1))
|
||||
|
||||
/* number of slots needed for 'n' bytes */
|
||||
#define bytes2slots(n) (((n) - 1) / sizeof(TTree) + 1)
|
||||
|
||||
/* set 'b' bit in charset 'cs' */
|
||||
#define setchar(cs,b) ((cs)[(b) >> 3] |= (1 << ((b) & 7)))
|
||||
|
||||
|
||||
/*
|
||||
** in capture instructions, 'kind' of capture and its offset are
|
||||
** packed in field 'aux', 4 bits for each
|
||||
*/
|
||||
#define getkind(op) ((op)->i.aux & 0xF)
|
||||
#define getoff(op) (((op)->i.aux >> 4) & 0xF)
|
||||
#define joinkindoff(k,o) ((k) | ((o) << 4))
|
||||
|
||||
#define MAXOFF 0xF
|
||||
#define MAXAUX 0xFF
|
||||
|
||||
|
||||
/* maximum number of bytes to look behind */
|
||||
#define MAXBEHIND MAXAUX
|
||||
|
||||
|
||||
/* maximum size (in elements) for a pattern */
|
||||
#define MAXPATTSIZE (SHRT_MAX - 10)
|
||||
|
||||
|
||||
/* size (in elements) for an instruction plus extra l bytes */
|
||||
#define instsize(l) (((l) + sizeof(Instruction) - 1)/sizeof(Instruction) + 1)
|
||||
|
||||
|
||||
/* size (in elements) for a ISet instruction */
|
||||
#define CHARSETINSTSIZE instsize(CHARSETSIZE)
|
||||
|
||||
/* size (in elements) for a IFunc instruction */
|
||||
#define funcinstsize(p) ((p)->i.aux + 2)
|
||||
|
||||
|
||||
|
||||
#define testchar(st,c) (((int)(st)[((c) >> 3)] & (1 << ((c) & 7))))
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
364
src/ext/lpeg/lpvm.c
Normal file
364
src/ext/lpeg/lpvm.c
Normal file
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
** $Id: lpvm.c,v 1.9 2016/06/03 20:11:18 roberto Exp $
|
||||
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lpcap.h"
|
||||
#include "lptypes.h"
|
||||
#include "lpvm.h"
|
||||
#include "lpprint.h"
|
||||
|
||||
|
||||
/* initial size for call/backtrack stack */
|
||||
#if !defined(INITBACK)
|
||||
#define INITBACK MAXBACK
|
||||
#endif
|
||||
|
||||
|
||||
#define getoffset(p) (((p) + 1)->offset)
|
||||
|
||||
static const Instruction giveup = {{IGiveup, 0, 0}};
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Virtual Machine
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
typedef struct Stack {
|
||||
const char *s; /* saved position (or NULL for calls) */
|
||||
const Instruction *p; /* next instruction */
|
||||
int caplevel;
|
||||
} Stack;
|
||||
|
||||
|
||||
#define getstackbase(L, ptop) ((Stack *)lua_touserdata(L, stackidx(ptop)))
|
||||
|
||||
|
||||
/*
|
||||
** Make the size of the array of captures 'cap' twice as large as needed
|
||||
** (which is 'captop'). ('n' is the number of new elements.)
|
||||
*/
|
||||
static Capture *doublecap (lua_State *L, Capture *cap, int captop,
|
||||
int n, int ptop) {
|
||||
Capture *newc;
|
||||
if (captop >= INT_MAX/((int)sizeof(Capture) * 2))
|
||||
luaL_error(L, "too many captures");
|
||||
newc = (Capture *)lua_newuserdata(L, captop * 2 * sizeof(Capture));
|
||||
memcpy(newc, cap, (captop - n) * sizeof(Capture));
|
||||
lua_replace(L, caplistidx(ptop));
|
||||
return newc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Double the size of the stack
|
||||
*/
|
||||
static Stack *doublestack (lua_State *L, Stack **stacklimit, int ptop) {
|
||||
Stack *stack = getstackbase(L, ptop);
|
||||
Stack *newstack;
|
||||
int n = *stacklimit - stack; /* current stack size */
|
||||
int max, newn;
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
|
||||
max = lua_tointeger(L, -1); /* maximum allowed size */
|
||||
lua_pop(L, 1);
|
||||
if (n >= max) /* already at maximum size? */
|
||||
luaL_error(L, "backtrack stack overflow (current limit is %d)", max);
|
||||
newn = 2 * n; /* new size */
|
||||
if (newn > max) newn = max;
|
||||
newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack));
|
||||
memcpy(newstack, stack, n * sizeof(Stack));
|
||||
lua_replace(L, stackidx(ptop));
|
||||
*stacklimit = newstack + newn;
|
||||
return newstack + n; /* return next position */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Interpret the result of a dynamic capture: false -> fail;
|
||||
** true -> keep current position; number -> next position.
|
||||
** Return new subject position. 'fr' is stack index where
|
||||
** is the result; 'curr' is current subject position; 'limit'
|
||||
** is subject's size.
|
||||
*/
|
||||
static int resdyncaptures (lua_State *L, int fr, int curr, int limit) {
|
||||
lua_Integer res;
|
||||
if (!lua_toboolean(L, fr)) { /* false value? */
|
||||
lua_settop(L, fr - 1); /* remove results */
|
||||
return -1; /* and fail */
|
||||
}
|
||||
else if (lua_isboolean(L, fr)) /* true? */
|
||||
res = curr; /* keep current position */
|
||||
else {
|
||||
res = lua_tointeger(L, fr) - 1; /* new position */
|
||||
if (res < curr || res > limit)
|
||||
luaL_error(L, "invalid position returned by match-time capture");
|
||||
}
|
||||
lua_remove(L, fr); /* remove first result (offset) */
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Add capture values returned by a dynamic capture to the capture list
|
||||
** 'base', nested inside a group capture. 'fd' indexes the first capture
|
||||
** value, 'n' is the number of values (at least 1).
|
||||
*/
|
||||
static void adddyncaptures (const char *s, Capture *base, int n, int fd) {
|
||||
int i;
|
||||
base[0].kind = Cgroup; /* create group capture */
|
||||
base[0].siz = 0;
|
||||
base[0].idx = 0; /* make it an anonymous group */
|
||||
for (i = 1; i <= n; i++) { /* add runtime captures */
|
||||
base[i].kind = Cruntime;
|
||||
base[i].siz = 1; /* mark it as closed */
|
||||
base[i].idx = fd + i - 1; /* stack index of capture value */
|
||||
base[i].s = s;
|
||||
}
|
||||
base[i].kind = Cclose; /* close group */
|
||||
base[i].siz = 1;
|
||||
base[i].s = s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Remove dynamic captures from the Lua stack (called in case of failure)
|
||||
*/
|
||||
static int removedyncap (lua_State *L, Capture *capture,
|
||||
int level, int last) {
|
||||
int id = finddyncap(capture + level, capture + last); /* index of 1st cap. */
|
||||
int top = lua_gettop(L);
|
||||
if (id == 0) return 0; /* no dynamic captures? */
|
||||
lua_settop(L, id - 1); /* remove captures */
|
||||
return top - id + 1; /* number of values removed */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Opcode interpreter
|
||||
*/
|
||||
const char *match (lua_State *L, const char *o, const char *s, const char *e,
|
||||
Instruction *op, Capture *capture, int ptop) {
|
||||
Stack stackbase[INITBACK];
|
||||
Stack *stacklimit = stackbase + INITBACK;
|
||||
Stack *stack = stackbase; /* point to first empty slot in stack */
|
||||
int capsize = INITCAPSIZE;
|
||||
int captop = 0; /* point to first empty slot in captures */
|
||||
int ndyncap = 0; /* number of dynamic captures (in Lua stack) */
|
||||
const Instruction *p = op; /* current instruction */
|
||||
stack->p = &giveup; stack->s = s; stack->caplevel = 0; stack++;
|
||||
lua_pushlightuserdata(L, stackbase);
|
||||
for (;;) {
|
||||
#if defined(DEBUG)
|
||||
printf("-------------------------------------\n");
|
||||
printcaplist(capture, capture + captop);
|
||||
printf("s: |%s| stck:%d, dyncaps:%d, caps:%d ",
|
||||
s, (int)(stack - getstackbase(L, ptop)), ndyncap, captop);
|
||||
printinst(op, p);
|
||||
#endif
|
||||
assert(stackidx(ptop) + ndyncap == lua_gettop(L) && ndyncap <= captop);
|
||||
switch ((Opcode)p->i.code) {
|
||||
case IEnd: {
|
||||
assert(stack == getstackbase(L, ptop) + 1);
|
||||
capture[captop].kind = Cclose;
|
||||
capture[captop].s = NULL;
|
||||
return s;
|
||||
}
|
||||
case IGiveup: {
|
||||
assert(stack == getstackbase(L, ptop));
|
||||
return NULL;
|
||||
}
|
||||
case IRet: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s == NULL);
|
||||
p = (--stack)->p;
|
||||
continue;
|
||||
}
|
||||
case IAny: {
|
||||
if (s < e) { p++; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestAny: {
|
||||
if (s < e) p += 2;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IChar: {
|
||||
if ((byte)*s == p->i.aux && s < e) { p++; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestChar: {
|
||||
if ((byte)*s == p->i.aux && s < e) p += 2;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case ISet: {
|
||||
int c = (byte)*s;
|
||||
if (testchar((p+1)->buff, c) && s < e)
|
||||
{ p += CHARSETINSTSIZE; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestSet: {
|
||||
int c = (byte)*s;
|
||||
if (testchar((p + 2)->buff, c) && s < e)
|
||||
p += 1 + CHARSETINSTSIZE;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IBehind: {
|
||||
int n = p->i.aux;
|
||||
if (n > s - o) goto fail;
|
||||
s -= n; p++;
|
||||
continue;
|
||||
}
|
||||
case ISpan: {
|
||||
for (; s < e; s++) {
|
||||
int c = (byte)*s;
|
||||
if (!testchar((p+1)->buff, c)) break;
|
||||
}
|
||||
p += CHARSETINSTSIZE;
|
||||
continue;
|
||||
}
|
||||
case IJmp: {
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IChoice: {
|
||||
if (stack == stacklimit)
|
||||
stack = doublestack(L, &stacklimit, ptop);
|
||||
stack->p = p + getoffset(p);
|
||||
stack->s = s;
|
||||
stack->caplevel = captop;
|
||||
stack++;
|
||||
p += 2;
|
||||
continue;
|
||||
}
|
||||
case ICall: {
|
||||
if (stack == stacklimit)
|
||||
stack = doublestack(L, &stacklimit, ptop);
|
||||
stack->s = NULL;
|
||||
stack->p = p + 2; /* save return address */
|
||||
stack++;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case ICommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
stack--;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IPartialCommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
(stack - 1)->s = s;
|
||||
(stack - 1)->caplevel = captop;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IBackCommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
s = (--stack)->s;
|
||||
captop = stack->caplevel;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IFailTwice:
|
||||
assert(stack > getstackbase(L, ptop));
|
||||
stack--;
|
||||
/* go through */
|
||||
case IFail:
|
||||
fail: { /* pattern failed: try to backtrack */
|
||||
do { /* remove pending calls */
|
||||
assert(stack > getstackbase(L, ptop));
|
||||
s = (--stack)->s;
|
||||
} while (s == NULL);
|
||||
if (ndyncap > 0) /* is there matchtime captures? */
|
||||
ndyncap -= removedyncap(L, capture, stack->caplevel, captop);
|
||||
captop = stack->caplevel;
|
||||
p = stack->p;
|
||||
#if defined(DEBUG)
|
||||
printf("**FAIL**\n");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
case ICloseRunTime: {
|
||||
CapState cs;
|
||||
int rem, res, n;
|
||||
int fr = lua_gettop(L) + 1; /* stack index of first result */
|
||||
cs.s = o; cs.L = L; cs.ocap = capture; cs.ptop = ptop;
|
||||
n = runtimecap(&cs, capture + captop, s, &rem); /* call function */
|
||||
captop -= n; /* remove nested captures */
|
||||
ndyncap -= rem; /* update number of dynamic captures */
|
||||
fr -= rem; /* 'rem' items were popped from Lua stack */
|
||||
res = resdyncaptures(L, fr, s - o, e - o); /* get result */
|
||||
if (res == -1) /* fail? */
|
||||
goto fail;
|
||||
s = o + res; /* else update current position */
|
||||
n = lua_gettop(L) - fr + 1; /* number of new captures */
|
||||
ndyncap += n; /* update number of dynamic captures */
|
||||
if (n > 0) { /* any new capture? */
|
||||
if (fr + n >= SHRT_MAX)
|
||||
luaL_error(L, "too many results in match-time capture");
|
||||
if ((captop += n + 2) >= capsize) {
|
||||
capture = doublecap(L, capture, captop, n + 2, ptop);
|
||||
capsize = 2 * captop;
|
||||
}
|
||||
/* add new captures to 'capture' list */
|
||||
adddyncaptures(s, capture + captop - n - 2, n, fr);
|
||||
}
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
case ICloseCapture: {
|
||||
const char *s1 = s;
|
||||
assert(captop > 0);
|
||||
/* if possible, turn capture into a full capture */
|
||||
if (capture[captop - 1].siz == 0 &&
|
||||
s1 - capture[captop - 1].s < UCHAR_MAX) {
|
||||
capture[captop - 1].siz = s1 - capture[captop - 1].s + 1;
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
capture[captop].siz = 1; /* mark entry as closed */
|
||||
capture[captop].s = s;
|
||||
goto pushcapture;
|
||||
}
|
||||
}
|
||||
case IOpenCapture:
|
||||
capture[captop].siz = 0; /* mark entry as open */
|
||||
capture[captop].s = s;
|
||||
goto pushcapture;
|
||||
case IFullCapture:
|
||||
capture[captop].siz = getoff(p) + 1; /* save capture size */
|
||||
capture[captop].s = s - getoff(p);
|
||||
/* goto pushcapture; */
|
||||
pushcapture: {
|
||||
capture[captop].idx = p->i.key;
|
||||
capture[captop].kind = getkind(p);
|
||||
if (++captop >= capsize) {
|
||||
capture = doublecap(L, capture, captop, 0, ptop);
|
||||
capsize = 2 * captop;
|
||||
}
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
default: assert(0); return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
58
src/ext/lpeg/lpvm.h
Normal file
58
src/ext/lpeg/lpvm.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
** $Id: lpvm.h,v 1.3 2014/02/21 13:06:41 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lpvm_h)
|
||||
#define lpvm_h
|
||||
|
||||
#include "lpcap.h"
|
||||
|
||||
|
||||
/* Virtual Machine's instructions */
|
||||
typedef enum Opcode {
|
||||
IAny, /* if no char, fail */
|
||||
IChar, /* if char != aux, fail */
|
||||
ISet, /* if char not in buff, fail */
|
||||
ITestAny, /* in no char, jump to 'offset' */
|
||||
ITestChar, /* if char != aux, jump to 'offset' */
|
||||
ITestSet, /* if char not in buff, jump to 'offset' */
|
||||
ISpan, /* read a span of chars in buff */
|
||||
IBehind, /* walk back 'aux' characters (fail if not possible) */
|
||||
IRet, /* return from a rule */
|
||||
IEnd, /* end of pattern */
|
||||
IChoice, /* stack a choice; next fail will jump to 'offset' */
|
||||
IJmp, /* jump to 'offset' */
|
||||
ICall, /* call rule at 'offset' */
|
||||
IOpenCall, /* call rule number 'key' (must be closed to a ICall) */
|
||||
ICommit, /* pop choice and jump to 'offset' */
|
||||
IPartialCommit, /* update top choice to current position and jump */
|
||||
IBackCommit, /* "fails" but jump to its own 'offset' */
|
||||
IFailTwice, /* pop one choice and then fail */
|
||||
IFail, /* go back to saved state on choice and jump to saved offset */
|
||||
IGiveup, /* internal use */
|
||||
IFullCapture, /* complete capture of last 'off' chars */
|
||||
IOpenCapture, /* start a capture */
|
||||
ICloseCapture,
|
||||
ICloseRunTime
|
||||
} Opcode;
|
||||
|
||||
|
||||
|
||||
typedef union Instruction {
|
||||
struct Inst {
|
||||
byte code;
|
||||
byte aux;
|
||||
short key;
|
||||
} i;
|
||||
int offset;
|
||||
byte buff[1];
|
||||
} Instruction;
|
||||
|
||||
|
||||
void printpatt (Instruction *p, int n);
|
||||
const char *match (lua_State *L, const char *o, const char *s, const char *e,
|
||||
Instruction *op, Capture *capture, int ptop);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
55
src/ext/lpeg/makefile
Normal file
55
src/ext/lpeg/makefile
Normal file
@@ -0,0 +1,55 @@
|
||||
LIBNAME = lpeg
|
||||
LUADIR = ../lua/
|
||||
|
||||
COPT = -O2
|
||||
# COPT = -DLPEG_DEBUG -g
|
||||
|
||||
CWARNS = -Wall -Wextra -pedantic \
|
||||
-Waggregate-return \
|
||||
-Wcast-align \
|
||||
-Wcast-qual \
|
||||
-Wdisabled-optimization \
|
||||
-Wpointer-arith \
|
||||
-Wshadow \
|
||||
-Wsign-compare \
|
||||
-Wundef \
|
||||
-Wwrite-strings \
|
||||
-Wbad-function-cast \
|
||||
-Wdeclaration-after-statement \
|
||||
-Wmissing-prototypes \
|
||||
-Wnested-externs \
|
||||
-Wstrict-prototypes \
|
||||
# -Wunreachable-code \
|
||||
|
||||
|
||||
CFLAGS = $(CWARNS) $(COPT) -std=c99 -I$(LUADIR) -fPIC
|
||||
CC = gcc
|
||||
|
||||
FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o
|
||||
|
||||
# For Linux
|
||||
linux:
|
||||
make lpeg.so "DLLFLAGS = -shared -fPIC"
|
||||
|
||||
# For Mac OS
|
||||
macosx:
|
||||
make lpeg.so "DLLFLAGS = -bundle -undefined dynamic_lookup"
|
||||
|
||||
lpeg.so: $(FILES)
|
||||
env $(CC) $(DLLFLAGS) $(FILES) -o lpeg.so
|
||||
|
||||
$(FILES): makefile
|
||||
|
||||
test: test.lua re.lua lpeg.so
|
||||
./test.lua
|
||||
|
||||
clean:
|
||||
rm -f $(FILES) lpeg.so
|
||||
|
||||
|
||||
lpcap.o: lpcap.c lpcap.h lptypes.h
|
||||
lpcode.o: lpcode.c lptypes.h lpcode.h lptree.h lpvm.h lpcap.h
|
||||
lpprint.o: lpprint.c lptypes.h lpprint.h lptree.h lpvm.h lpcap.h
|
||||
lptree.o: lptree.c lptypes.h lpcap.h lpcode.h lptree.h lpvm.h lpprint.h
|
||||
lpvm.o: lpvm.c lpcap.h lptypes.h lpvm.h lpprint.h lptree.h
|
||||
|
||||
498
src/ext/lpeg/re.html
Normal file
498
src/ext/lpeg/re.html
Normal file
@@ -0,0 +1,498 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>LPeg.re - Regex syntax for LPEG</title>
|
||||
<link rel="stylesheet"
|
||||
href="http://www.inf.puc-rio.br/~roberto/lpeg/doc.css"
|
||||
type="text/css"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- $Id: re.html,v 1.24 2016/09/20 17:41:27 roberto Exp $ -->
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo">
|
||||
<a href="http://www.inf.puc-rio.br/~roberto/lpeg/">
|
||||
<img alt="LPeg logo" src="lpeg-128.gif"/>
|
||||
</a>
|
||||
</div>
|
||||
<div id="product_name"><big><strong>LPeg.re</strong></big></div>
|
||||
<div id="product_description">
|
||||
Regex syntax for LPEG
|
||||
</div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
<div id="main">
|
||||
|
||||
<div id="navigation">
|
||||
<h1>re</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="#basic">Basic Constructions</a></li>
|
||||
<li><a href="#func">Functions</a></li>
|
||||
<li><a href="#ex">Some Examples</a></li>
|
||||
<li><a href="#license">License</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div> <!-- id="navigation" -->
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h2><a name="basic"></a>The <code>re</code> Module</h2>
|
||||
|
||||
<p>
|
||||
The <code>re</code> module
|
||||
(provided by file <code>re.lua</code> in the distribution)
|
||||
supports a somewhat conventional regex syntax
|
||||
for pattern usage within <a href="lpeg.html">LPeg</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The next table summarizes <code>re</code>'s syntax.
|
||||
A <code>p</code> represents an arbitrary pattern;
|
||||
<code>num</code> represents a number (<code>[0-9]+</code>);
|
||||
<code>name</code> represents an identifier
|
||||
(<code>[a-zA-Z][a-zA-Z0-9_]*</code>).
|
||||
Constructions are listed in order of decreasing precedence.
|
||||
<table border="1">
|
||||
<tbody><tr><td><b>Syntax</b></td><td><b>Description</b></td></tr>
|
||||
<tr><td><code>( p )</code></td> <td>grouping</td></tr>
|
||||
<tr><td><code>'string'</code></td> <td>literal string</td></tr>
|
||||
<tr><td><code>"string"</code></td> <td>literal string</td></tr>
|
||||
<tr><td><code>[class]</code></td> <td>character class</td></tr>
|
||||
<tr><td><code>.</code></td> <td>any character</td></tr>
|
||||
<tr><td><code>%name</code></td>
|
||||
<td>pattern <code>defs[name]</code> or a pre-defined pattern</td></tr>
|
||||
<tr><td><code>name</code></td><td>non terminal</td></tr>
|
||||
<tr><td><code><name></code></td><td>non terminal</td></tr>
|
||||
<tr><td><code>{}</code></td> <td>position capture</td></tr>
|
||||
<tr><td><code>{ p }</code></td> <td>simple capture</td></tr>
|
||||
<tr><td><code>{: p :}</code></td> <td>anonymous group capture</td></tr>
|
||||
<tr><td><code>{:name: p :}</code></td> <td>named group capture</td></tr>
|
||||
<tr><td><code>{~ p ~}</code></td> <td>substitution capture</td></tr>
|
||||
<tr><td><code>{| p |}</code></td> <td>table capture</td></tr>
|
||||
<tr><td><code>=name</code></td> <td>back reference
|
||||
</td></tr>
|
||||
<tr><td><code>p ?</code></td> <td>optional match</td></tr>
|
||||
<tr><td><code>p *</code></td> <td>zero or more repetitions</td></tr>
|
||||
<tr><td><code>p +</code></td> <td>one or more repetitions</td></tr>
|
||||
<tr><td><code>p^num</code></td> <td>exactly <code>n</code> repetitions</td></tr>
|
||||
<tr><td><code>p^+num</code></td>
|
||||
<td>at least <code>n</code> repetitions</td></tr>
|
||||
<tr><td><code>p^-num</code></td>
|
||||
<td>at most <code>n</code> repetitions</td></tr>
|
||||
<tr><td><code>p -> 'string'</code></td> <td>string capture</td></tr>
|
||||
<tr><td><code>p -> "string"</code></td> <td>string capture</td></tr>
|
||||
<tr><td><code>p -> num</code></td> <td>numbered capture</td></tr>
|
||||
<tr><td><code>p -> name</code></td> <td>function/query/string capture
|
||||
equivalent to <code>p / defs[name]</code></td></tr>
|
||||
<tr><td><code>p => name</code></td> <td>match-time capture
|
||||
equivalent to <code>lpeg.Cmt(p, defs[name])</code></td></tr>
|
||||
<tr><td><code>& p</code></td> <td>and predicate</td></tr>
|
||||
<tr><td><code>! p</code></td> <td>not predicate</td></tr>
|
||||
<tr><td><code>p1 p2</code></td> <td>concatenation</td></tr>
|
||||
<tr><td><code>p1 / p2</code></td> <td>ordered choice</td></tr>
|
||||
<tr><td>(<code>name <- p</code>)<sup>+</sup></td> <td>grammar</td></tr>
|
||||
</tbody></table>
|
||||
<p>
|
||||
Any space appearing in a syntax description can be
|
||||
replaced by zero or more space characters and Lua-style comments
|
||||
(<code>--</code> until end of line).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Character classes define sets of characters.
|
||||
An initial <code>^</code> complements the resulting set.
|
||||
A range <em>x</em><code>-</code><em>y</em> includes in the set
|
||||
all characters with codes between the codes of <em>x</em> and <em>y</em>.
|
||||
A pre-defined class <code>%</code><em>name</em> includes all
|
||||
characters of that class.
|
||||
A simple character includes itself in the set.
|
||||
The only special characters inside a class are <code>^</code>
|
||||
(special only if it is the first character);
|
||||
<code>]</code>
|
||||
(can be included in the set as the first character,
|
||||
after the optional <code>^</code>);
|
||||
<code>%</code> (special only if followed by a letter);
|
||||
and <code>-</code>
|
||||
(can be included in the set as the first or the last character).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Currently the pre-defined classes are similar to those from the
|
||||
Lua's string library
|
||||
(<code>%a</code> for letters,
|
||||
<code>%A</code> for non letters, etc.).
|
||||
There is also a class <code>%nl</code>
|
||||
containing only the newline character,
|
||||
which is particularly handy for grammars written inside long strings,
|
||||
as long strings do not interpret escape sequences like <code>\n</code>.
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a name="func">Functions</a></h2>
|
||||
|
||||
<h3><code>re.compile (string, [, defs])</code></h3>
|
||||
<p>
|
||||
Compiles the given string and
|
||||
returns an equivalent LPeg pattern.
|
||||
The given string may define either an expression or a grammar.
|
||||
The optional <code>defs</code> table provides extra Lua values
|
||||
to be used by the pattern.
|
||||
</p>
|
||||
|
||||
<h3><code>re.find (subject, pattern [, init])</code></h3>
|
||||
<p>
|
||||
Searches the given pattern in the given subject.
|
||||
If it finds a match,
|
||||
returns the index where this occurrence starts and
|
||||
the index where it ends.
|
||||
Otherwise, returns nil.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
An optional numeric argument <code>init</code> makes the search
|
||||
starts at that position in the subject string.
|
||||
As usual in Lua libraries,
|
||||
a negative value counts from the end.
|
||||
</p>
|
||||
|
||||
<h3><code>re.gsub (subject, pattern, replacement)</code></h3>
|
||||
<p>
|
||||
Does a <em>global substitution</em>,
|
||||
replacing all occurrences of <code>pattern</code>
|
||||
in the given <code>subject</code> by <code>replacement</code>.
|
||||
|
||||
<h3><code>re.match (subject, pattern)</code></h3>
|
||||
<p>
|
||||
Matches the given pattern against the given subject,
|
||||
returning all captures.
|
||||
</p>
|
||||
|
||||
<h3><code>re.updatelocale ()</code></h3>
|
||||
<p>
|
||||
Updates the pre-defined character classes to the current locale.
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a name="ex">Some Examples</a></h2>
|
||||
|
||||
<h3>A complete simple program</h3>
|
||||
<p>
|
||||
The next code shows a simple complete Lua program using
|
||||
the <code>re</code> module:
|
||||
</p>
|
||||
<pre class="example">
|
||||
local re = require"re"
|
||||
|
||||
-- find the position of the first numeral in a string
|
||||
print(re.find("the number 423 is odd", "[0-9]+")) --> 12 14
|
||||
|
||||
-- returns all words in a string
|
||||
print(re.match("the number 423 is odd", "({%a+} / .)*"))
|
||||
--> the number is odd
|
||||
|
||||
-- returns the first numeral in a string
|
||||
print(re.match("the number 423 is odd", "s <- {%d+} / . s"))
|
||||
--> 423
|
||||
|
||||
print(re.gsub("hello World", "[aeiou]", "."))
|
||||
--> h.ll. W.rld
|
||||
</pre>
|
||||
|
||||
|
||||
<h3>Balanced parentheses</h3>
|
||||
<p>
|
||||
The following call will produce the same pattern produced by the
|
||||
Lua expression in the
|
||||
<a href="lpeg.html#balanced">balanced parentheses</a> example:
|
||||
</p>
|
||||
<pre class="example">
|
||||
b = re.compile[[ balanced <- "(" ([^()] / balanced)* ")" ]]
|
||||
</pre>
|
||||
|
||||
<h3>String reversal</h3>
|
||||
<p>
|
||||
The next example reverses a string:
|
||||
</p>
|
||||
<pre class="example">
|
||||
rev = re.compile[[ R <- (!.) -> '' / ({.} R) -> '%2%1']]
|
||||
print(rev:match"0123456789") --> 9876543210
|
||||
</pre>
|
||||
|
||||
<h3>CSV decoder</h3>
|
||||
<p>
|
||||
The next example replicates the <a href="lpeg.html#CSV">CSV decoder</a>:
|
||||
</p>
|
||||
<pre class="example">
|
||||
record = re.compile[[
|
||||
record <- {| field (',' field)* |} (%nl / !.)
|
||||
field <- escaped / nonescaped
|
||||
nonescaped <- { [^,"%nl]* }
|
||||
escaped <- '"' {~ ([^"] / '""' -> '"')* ~} '"'
|
||||
]]
|
||||
</pre>
|
||||
|
||||
<h3>Lua's long strings</h3>
|
||||
<p>
|
||||
The next example matches Lua long strings:
|
||||
</p>
|
||||
<pre class="example">
|
||||
c = re.compile([[
|
||||
longstring <- ('[' {:eq: '='* :} '[' close)
|
||||
close <- ']' =eq ']' / . close
|
||||
]])
|
||||
|
||||
print(c:match'[==[]]===]]]]==]===[]') --> 17
|
||||
</pre>
|
||||
|
||||
<h3>Abstract Syntax Trees</h3>
|
||||
<p>
|
||||
This example shows a simple way to build an
|
||||
abstract syntax tree (AST) for a given grammar.
|
||||
To keep our example simple,
|
||||
let us consider the following grammar
|
||||
for lists of names:
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = re.compile[[
|
||||
listname <- (name s)*
|
||||
name <- [a-z][a-z]*
|
||||
s <- %s*
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
Now, we will add captures to build a corresponding AST.
|
||||
As a first step, the pattern will build a table to
|
||||
represent each non terminal;
|
||||
terminals will be represented by their corresponding strings:
|
||||
</p>
|
||||
<pre class="example">
|
||||
c = re.compile[[
|
||||
listname <- {| (name s)* |}
|
||||
name <- {| {[a-z][a-z]*} |}
|
||||
s <- %s*
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
Now, a match against <code>"hi hello bye"</code>
|
||||
results in the table
|
||||
<code>{{"hi"}, {"hello"}, {"bye"}}</code>.
|
||||
</p>
|
||||
<p>
|
||||
For such a simple grammar,
|
||||
this AST is more than enough;
|
||||
actually, the tables around each single name
|
||||
are already overkilling.
|
||||
More complex grammars,
|
||||
however, may need some more structure.
|
||||
Specifically,
|
||||
it would be useful if each table had
|
||||
a <code>tag</code> field telling what non terminal
|
||||
that table represents.
|
||||
We can add such a tag using
|
||||
<a href="lpeg.html#cap-g">named group captures</a>:
|
||||
</p>
|
||||
<pre class="example">
|
||||
x = re.compile[[
|
||||
listname <- {| {:tag: '' -> 'list':} (name s)* |}
|
||||
name <- {| {:tag: '' -> 'id':} {[a-z][a-z]*} |}
|
||||
s <- ' '*
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
With these group captures,
|
||||
a match against <code>"hi hello bye"</code>
|
||||
results in the following table:
|
||||
</p>
|
||||
<pre class="example">
|
||||
{tag="list",
|
||||
{tag="id", "hi"},
|
||||
{tag="id", "hello"},
|
||||
{tag="id", "bye"}
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
<h3>Indented blocks</h3>
|
||||
<p>
|
||||
This example breaks indented blocks into tables,
|
||||
respecting the indentation:
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = re.compile[[
|
||||
block <- {| {:ident:' '*:} line
|
||||
((=ident !' ' line) / &(=ident ' ') block)* |}
|
||||
line <- {[^%nl]*} %nl
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
As an example,
|
||||
consider the following text:
|
||||
</p>
|
||||
<pre class="example">
|
||||
t = p:match[[
|
||||
first line
|
||||
subline 1
|
||||
subline 2
|
||||
second line
|
||||
third line
|
||||
subline 3.1
|
||||
subline 3.1.1
|
||||
subline 3.2
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
The resulting table <code>t</code> will be like this:
|
||||
</p>
|
||||
<pre class="example">
|
||||
{'first line'; {'subline 1'; 'subline 2'; ident = ' '};
|
||||
'second line';
|
||||
'third line'; { 'subline 3.1'; {'subline 3.1.1'; ident = ' '};
|
||||
'subline 3.2'; ident = ' '};
|
||||
ident = ''}
|
||||
</pre>
|
||||
|
||||
<h3>Macro expander</h3>
|
||||
<p>
|
||||
This example implements a simple macro expander.
|
||||
Macros must be defined as part of the pattern,
|
||||
following some simple rules:
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = re.compile[[
|
||||
text <- {~ item* ~}
|
||||
item <- macro / [^()] / '(' item* ')'
|
||||
arg <- ' '* {~ (!',' item)* ~}
|
||||
args <- '(' arg (',' arg)* ')'
|
||||
-- now we define some macros
|
||||
macro <- ('apply' args) -> '%1(%2)'
|
||||
/ ('add' args) -> '%1 + %2'
|
||||
/ ('mul' args) -> '%1 * %2'
|
||||
]]
|
||||
|
||||
print(p:match"add(mul(a,b), apply(f,x))") --> a * b + f(x)
|
||||
</pre>
|
||||
<p>
|
||||
A <code>text</code> is a sequence of items,
|
||||
wherein we apply a substitution capture to expand any macros.
|
||||
An <code>item</code> is either a macro,
|
||||
any character different from parentheses,
|
||||
or a parenthesized expression.
|
||||
A macro argument (<code>arg</code>) is a sequence
|
||||
of items different from a comma.
|
||||
(Note that a comma may appear inside an item,
|
||||
e.g., inside a parenthesized expression.)
|
||||
Again we do a substitution capture to expand any macro
|
||||
in the argument before expanding the outer macro.
|
||||
<code>args</code> is a list of arguments separated by commas.
|
||||
Finally we define the macros.
|
||||
Each macro is a string substitution;
|
||||
it replaces the macro name and its arguments by its corresponding string,
|
||||
with each <code>%</code><em>n</em> replaced by the <em>n</em>-th argument.
|
||||
</p>
|
||||
|
||||
<h3>Patterns</h3>
|
||||
<p>
|
||||
This example shows the complete syntax
|
||||
of patterns accepted by <code>re</code>.
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = [=[
|
||||
|
||||
pattern <- exp !.
|
||||
exp <- S (grammar / alternative)
|
||||
|
||||
alternative <- seq ('/' S seq)*
|
||||
seq <- prefix*
|
||||
prefix <- '&' S prefix / '!' S prefix / suffix
|
||||
suffix <- primary S (([+*?]
|
||||
/ '^' [+-]? num
|
||||
/ '->' S (string / '{}' / name)
|
||||
/ '=>' S name) S)*
|
||||
|
||||
primary <- '(' exp ')' / string / class / defined
|
||||
/ '{:' (name ':')? exp ':}'
|
||||
/ '=' name
|
||||
/ '{}'
|
||||
/ '{~' exp '~}'
|
||||
/ '{' exp '}'
|
||||
/ '.'
|
||||
/ name S !arrow
|
||||
/ '<' name '>' -- old-style non terminals
|
||||
|
||||
grammar <- definition+
|
||||
definition <- name S arrow exp
|
||||
|
||||
class <- '[' '^'? item (!']' item)* ']'
|
||||
item <- defined / range / .
|
||||
range <- . '-' [^]]
|
||||
|
||||
S <- (%s / '--' [^%nl]*)* -- spaces and comments
|
||||
name <- [A-Za-z][A-Za-z0-9_]*
|
||||
arrow <- '<-'
|
||||
num <- [0-9]+
|
||||
string <- '"' [^"]* '"' / "'" [^']* "'"
|
||||
defined <- '%' name
|
||||
|
||||
]=]
|
||||
|
||||
print(re.match(p, p)) -- a self description must match itself
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
<h2><a name="license">License</a></h2>
|
||||
|
||||
<p>
|
||||
Copyright © 2008-2015 Lua.org, PUC-Rio.
|
||||
</p>
|
||||
<p>
|
||||
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:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions of the Software.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
|
||||
</div> <!-- id="main" -->
|
||||
|
||||
<div id="about">
|
||||
<p><small>
|
||||
$Id: re.html,v 1.24 2016/09/20 17:41:27 roberto Exp $
|
||||
</small></p>
|
||||
</div> <!-- id="about" -->
|
||||
|
||||
</div> <!-- id="container" -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
259
src/ext/lpeg/re.lua
Normal file
259
src/ext/lpeg/re.lua
Normal file
@@ -0,0 +1,259 @@
|
||||
-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $
|
||||
|
||||
-- imported functions and modules
|
||||
local tonumber, type, print, error = tonumber, type, print, error
|
||||
local setmetatable = setmetatable
|
||||
local m = require"lpeg"
|
||||
|
||||
-- 'm' will be used to parse expressions, and 'mm' will be used to
|
||||
-- create expressions; that is, 're' runs on 'm', creating patterns
|
||||
-- on 'mm'
|
||||
local mm = m
|
||||
|
||||
-- pattern's metatable
|
||||
local mt = getmetatable(mm.P(0))
|
||||
|
||||
|
||||
|
||||
-- No more global accesses after this point
|
||||
local version = _VERSION
|
||||
if version == "Lua 5.2" then _ENV = nil end
|
||||
|
||||
|
||||
local any = m.P(1)
|
||||
|
||||
|
||||
-- Pre-defined names
|
||||
local Predef = { nl = m.P"\n" }
|
||||
|
||||
|
||||
local mem
|
||||
local fmem
|
||||
local gmem
|
||||
|
||||
|
||||
local function updatelocale ()
|
||||
mm.locale(Predef)
|
||||
Predef.a = Predef.alpha
|
||||
Predef.c = Predef.cntrl
|
||||
Predef.d = Predef.digit
|
||||
Predef.g = Predef.graph
|
||||
Predef.l = Predef.lower
|
||||
Predef.p = Predef.punct
|
||||
Predef.s = Predef.space
|
||||
Predef.u = Predef.upper
|
||||
Predef.w = Predef.alnum
|
||||
Predef.x = Predef.xdigit
|
||||
Predef.A = any - Predef.a
|
||||
Predef.C = any - Predef.c
|
||||
Predef.D = any - Predef.d
|
||||
Predef.G = any - Predef.g
|
||||
Predef.L = any - Predef.l
|
||||
Predef.P = any - Predef.p
|
||||
Predef.S = any - Predef.s
|
||||
Predef.U = any - Predef.u
|
||||
Predef.W = any - Predef.w
|
||||
Predef.X = any - Predef.x
|
||||
mem = {} -- restart memoization
|
||||
fmem = {}
|
||||
gmem = {}
|
||||
local mt = {__mode = "v"}
|
||||
setmetatable(mem, mt)
|
||||
setmetatable(fmem, mt)
|
||||
setmetatable(gmem, mt)
|
||||
end
|
||||
|
||||
|
||||
updatelocale()
|
||||
|
||||
|
||||
|
||||
local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
|
||||
|
||||
|
||||
local function getdef (id, defs)
|
||||
local c = defs and defs[id]
|
||||
if not c then error("undefined name: " .. id) end
|
||||
return c
|
||||
end
|
||||
|
||||
|
||||
local function patt_error (s, i)
|
||||
local msg = (#s < i + 20) and s:sub(i)
|
||||
or s:sub(i,i+20) .. "..."
|
||||
msg = ("pattern error near '%s'"):format(msg)
|
||||
error(msg, 2)
|
||||
end
|
||||
|
||||
local function mult (p, n)
|
||||
local np = mm.P(true)
|
||||
while n >= 1 do
|
||||
if n%2 >= 1 then np = np * p end
|
||||
p = p * p
|
||||
n = n/2
|
||||
end
|
||||
return np
|
||||
end
|
||||
|
||||
local function equalcap (s, i, c)
|
||||
if type(c) ~= "string" then return nil end
|
||||
local e = #c + i
|
||||
if s:sub(i, e - 1) == c then return e else return nil end
|
||||
end
|
||||
|
||||
|
||||
local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
|
||||
|
||||
local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0
|
||||
|
||||
local arrow = S * "<-"
|
||||
|
||||
local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1
|
||||
|
||||
name = m.C(name)
|
||||
|
||||
|
||||
-- a defined name only have meaning in a given environment
|
||||
local Def = name * m.Carg(1)
|
||||
|
||||
local num = m.C(m.R"09"^1) * S / tonumber
|
||||
|
||||
local String = "'" * m.C((any - "'")^0) * "'" +
|
||||
'"' * m.C((any - '"')^0) * '"'
|
||||
|
||||
|
||||
local defined = "%" * Def / function (c,Defs)
|
||||
local cat = Defs and Defs[c] or Predef[c]
|
||||
if not cat then error ("name '" .. c .. "' undefined") end
|
||||
return cat
|
||||
end
|
||||
|
||||
local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
|
||||
|
||||
local item = defined + Range + m.C(any)
|
||||
|
||||
local Class =
|
||||
"["
|
||||
* (m.C(m.P"^"^-1)) -- optional complement symbol
|
||||
* m.Cf(item * (item - "]")^0, mt.__add) /
|
||||
function (c, p) return c == "^" and any - p or p end
|
||||
* "]"
|
||||
|
||||
local function adddef (t, k, exp)
|
||||
if t[k] then
|
||||
error("'"..k.."' already defined as a rule")
|
||||
else
|
||||
t[k] = exp
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function firstdef (n, r) return adddef({n}, n, r) end
|
||||
|
||||
|
||||
local function NT (n, b)
|
||||
if not b then
|
||||
error("rule '"..n.."' used outside a grammar")
|
||||
else return mm.V(n)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local exp = m.P{ "Exp",
|
||||
Exp = S * ( m.V"Grammar"
|
||||
+ m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) );
|
||||
Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul)
|
||||
* (#seq_follow + patt_error);
|
||||
Prefix = "&" * S * m.V"Prefix" / mt.__len
|
||||
+ "!" * S * m.V"Prefix" / mt.__unm
|
||||
+ m.V"Suffix";
|
||||
Suffix = m.Cf(m.V"Primary" * S *
|
||||
( ( m.P"+" * m.Cc(1, mt.__pow)
|
||||
+ m.P"*" * m.Cc(0, mt.__pow)
|
||||
+ m.P"?" * m.Cc(-1, mt.__pow)
|
||||
+ "^" * ( m.Cg(num * m.Cc(mult))
|
||||
+ m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow))
|
||||
)
|
||||
+ "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div))
|
||||
+ m.P"{}" * m.Cc(nil, m.Ct)
|
||||
+ m.Cg(Def / getdef * m.Cc(mt.__div))
|
||||
)
|
||||
+ "=>" * S * m.Cg(Def / getdef * m.Cc(m.Cmt))
|
||||
) * S
|
||||
)^0, function (a,b,f) return f(a,b) end );
|
||||
Primary = "(" * m.V"Exp" * ")"
|
||||
+ String / mm.P
|
||||
+ Class
|
||||
+ defined
|
||||
+ "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" /
|
||||
function (n, p) return mm.Cg(p, n) end
|
||||
+ "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
|
||||
+ m.P"{}" / mm.Cp
|
||||
+ "{~" * m.V"Exp" * "~}" / mm.Cs
|
||||
+ "{|" * m.V"Exp" * "|}" / mm.Ct
|
||||
+ "{" * m.V"Exp" * "}" / mm.C
|
||||
+ m.P"." * m.Cc(any)
|
||||
+ (name * -arrow + "<" * name * ">") * m.Cb("G") / NT;
|
||||
Definition = name * arrow * m.V"Exp";
|
||||
Grammar = m.Cg(m.Cc(true), "G") *
|
||||
m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0,
|
||||
adddef) / mm.P
|
||||
}
|
||||
|
||||
local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error)
|
||||
|
||||
|
||||
local function compile (p, defs)
|
||||
if mm.type(p) == "pattern" then return p end -- already compiled
|
||||
local cp = pattern:match(p, 1, defs)
|
||||
if not cp then error("incorrect pattern", 3) end
|
||||
return cp
|
||||
end
|
||||
|
||||
local function match (s, p, i)
|
||||
local cp = mem[p]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
mem[p] = cp
|
||||
end
|
||||
return cp:match(s, i or 1)
|
||||
end
|
||||
|
||||
local function find (s, p, i)
|
||||
local cp = fmem[p]
|
||||
if not cp then
|
||||
cp = compile(p) / 0
|
||||
cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
|
||||
fmem[p] = cp
|
||||
end
|
||||
local i, e = cp:match(s, i or 1)
|
||||
if i then return i, e - 1
|
||||
else return i
|
||||
end
|
||||
end
|
||||
|
||||
local function gsub (s, p, rep)
|
||||
local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
|
||||
gmem[p] = g
|
||||
local cp = g[rep]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
cp = mm.Cs((cp / rep + 1)^0)
|
||||
g[rep] = cp
|
||||
end
|
||||
return cp:match(s)
|
||||
end
|
||||
|
||||
|
||||
-- exported names
|
||||
local re = {
|
||||
compile = compile,
|
||||
match = match,
|
||||
find = find,
|
||||
gsub = gsub,
|
||||
updatelocale = updatelocale,
|
||||
}
|
||||
|
||||
if version == "Lua 5.1" then _G.re = re end
|
||||
|
||||
return re
|
||||
1503
src/ext/lpeg/test.lua
Normal file
1503
src/ext/lpeg/test.lua
Normal file
File diff suppressed because it is too large
Load Diff
178
src/fs.c
178
src/fs.c
@@ -29,12 +29,11 @@
|
||||
#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>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
@@ -540,18 +539,95 @@ static void makeDir(const char* name)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool fsExistsFile(FileSystem* fs, const char* name)
|
||||
const char* fsFilename(const char *path)
|
||||
{
|
||||
const char* path = getFilePath(fs, name);
|
||||
FILE* file = tic_fopen(UTF8ToString(path), UTF8ToString("rb"));
|
||||
const char* full = fsFullname(path);
|
||||
const char* base = fsBasename(path);
|
||||
|
||||
return base ? full + strlen(fsBasename(path)) : NULL;
|
||||
}
|
||||
|
||||
const char* fsFullname(const char *path)
|
||||
{
|
||||
char* result = NULL;
|
||||
|
||||
#if defined(__WINDOWS__) || defined(__WINRT__)
|
||||
static wchar_t wpath[FILENAME_MAX];
|
||||
GetFullPathNameW(UTF8ToString(path), sizeof(wpath), wpath, NULL);
|
||||
|
||||
result = StringToUTF8(wpath);
|
||||
#else
|
||||
|
||||
result = realpath(path, NULL);
|
||||
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* fsBasename(const char *path)
|
||||
{
|
||||
char* result = NULL;
|
||||
|
||||
#if defined(__WINDOWS__) || defined(__WINRT__)
|
||||
#define SEP "\\"
|
||||
#else
|
||||
#define SEP "/"
|
||||
#endif
|
||||
|
||||
if(file)
|
||||
{
|
||||
fclose(file);
|
||||
return true;
|
||||
char* full = (char*)fsFullname(path);
|
||||
|
||||
struct tic_stat_struct s;
|
||||
if(tic_stat(UTF8ToString(full), &s) == 0)
|
||||
{
|
||||
result = full;
|
||||
|
||||
if(S_ISREG(s.st_mode))
|
||||
{
|
||||
const char* ptr = result + strlen(result);
|
||||
|
||||
while(ptr >= result)
|
||||
{
|
||||
if(*ptr == SEP[0])
|
||||
{
|
||||
result[ptr-result] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
ptr--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
if(result && result[strlen(result)-1] != SEP[0])
|
||||
strcat(result, SEP);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fsExists(const char* name)
|
||||
{
|
||||
struct tic_stat_struct s;
|
||||
return tic_stat(UTF8ToString(name), &s) == 0;
|
||||
}
|
||||
|
||||
bool fsExistsFile(FileSystem* fs, const char* name)
|
||||
{
|
||||
return fsExists(getFilePath(fs, name));
|
||||
}
|
||||
|
||||
u64 fsMDate(FileSystem* fs, const char* name)
|
||||
{
|
||||
struct tic_stat_struct s;
|
||||
|
||||
if(tic_stat(UTF8ToString(getFilePath(fs, name)), &s) == 0 && S_ISREG(s.st_mode))
|
||||
{
|
||||
return s.st_mtime;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool fsSaveFile(FileSystem* fs, const char* name, const void* data, size_t size, bool overwrite)
|
||||
@@ -674,7 +750,7 @@ void fsMakeDir(FileSystem* fs, const char* name)
|
||||
|
||||
#if defined(__WINDOWS__) || defined(__LINUX__) || defined(__MACOSX__)
|
||||
|
||||
int fsOpenSystemPath(FileSystem* fs, const char* path)
|
||||
s32 fsOpenSystemPath(FileSystem* fs, const char* path)
|
||||
{
|
||||
char command[FILENAME_MAX];
|
||||
|
||||
@@ -697,7 +773,10 @@ int fsOpenSystemPath(FileSystem* fs, const char* path)
|
||||
|
||||
#else
|
||||
|
||||
void fsOpenSystemPath(FileSystem* fs, const char* path) {}
|
||||
s32 fsOpenSystemPath(FileSystem* fs, const char* path)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -711,55 +790,64 @@ void fsOpenWorkingFolder(FileSystem* fs)
|
||||
fsOpenSystemPath(fs, path);
|
||||
}
|
||||
|
||||
void createFileSystem(void(*callback)(FileSystem*))
|
||||
void createFileSystem(const char* path, void(*callback)(FileSystem*))
|
||||
{
|
||||
FileSystem* fs = (FileSystem*)SDL_malloc(sizeof(FileSystem));
|
||||
memset(fs, 0, sizeof(FileSystem));
|
||||
|
||||
fs->net = createNet();
|
||||
|
||||
if(path)
|
||||
{
|
||||
strcpy(fs->dir, path);
|
||||
callback(fs);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
|
||||
strcpy(fs->dir, "/" TIC_PACKAGE "/" TIC_NAME "/");
|
||||
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);
|
||||
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);
|
||||
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)
|
||||
EM_ASM_
|
||||
(
|
||||
{
|
||||
if(val.length)
|
||||
var dir = "";
|
||||
Module.Pointer_stringify($0).split("/").forEach(function(val)
|
||||
{
|
||||
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
|
||||
);
|
||||
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);
|
||||
callback(fs);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
9
src/fs.h
9
src/fs.h
@@ -47,7 +47,7 @@ typedef void(*OpenCallback)(const char* name, const void* buffer, size_t size, v
|
||||
|
||||
typedef struct FileSystem FileSystem;
|
||||
|
||||
void createFileSystem(void(*callback)(FileSystem*));
|
||||
void createFileSystem(const char* path, void(*callback)(FileSystem*));
|
||||
|
||||
void fsEnumFiles(FileSystem* fs, ListCallback callback, void* data);
|
||||
void fsAddFile(FileSystem* fs, AddCallback callback, void* data);
|
||||
@@ -60,14 +60,19 @@ 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);
|
||||
u64 fsMDate(FileSystem* fs, const char* name);
|
||||
|
||||
const char* fsBasename(const char *path);
|
||||
const char* fsFilename(const char *path);
|
||||
const char* fsFullname(const char *path);
|
||||
bool fsExists(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);
|
||||
int fsOpenSystemPath(FileSystem* fs, const char* path);
|
||||
s32 fsOpenSystemPath(FileSystem* fs, const char* path);
|
||||
bool fsIsDir(FileSystem* fs, const char* dir);
|
||||
bool fsIsInPublicDir(FileSystem* fs);
|
||||
bool fsChangeDir(FileSystem* fs, const char* dir);
|
||||
|
||||
18
src/jsapi.c
18
src/jsapi.c
@@ -512,7 +512,7 @@ static duk_ret_t duk_pmem(duk_context* duk)
|
||||
|
||||
u32 index = duk_to_int(duk, 0);
|
||||
|
||||
if(index < TIC_PERSISTENT_SIZE)
|
||||
if(index >= 0 && index < TIC_PERSISTENT_SIZE)
|
||||
{
|
||||
s32 val = memory->ram.persistent.data[index];
|
||||
|
||||
@@ -645,10 +645,10 @@ static duk_ret_t duk_tri(duk_context* duk)
|
||||
|
||||
static duk_ret_t duk_textri(duk_context* duk)
|
||||
{
|
||||
s32 pt[12];
|
||||
float pt[12];
|
||||
|
||||
for (s32 i = 0; i < COUNT_OF(pt); i++)
|
||||
pt[i] = duk_to_int(duk, i);
|
||||
pt[i] = (float)duk_to_number(duk, i);
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
bool use_map = duk_is_null_or_undefined(duk, 12) ? false : duk_to_boolean(duk, 12);
|
||||
u8 chroma = duk_is_null_or_undefined(duk, 13) ? 0xff : duk_to_int(duk, 13);
|
||||
@@ -703,13 +703,10 @@ static duk_ret_t duk_sync(duk_context* duk)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)getDukMachine(duk);
|
||||
|
||||
memory->api.sync(memory, true);
|
||||
bool toCart = duk_is_null_or_undefined(duk, 0) ? true : duk_to_boolean(duk, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
memory->api.sync(memory, toCart);
|
||||
|
||||
static duk_ret_t duk_dofile(duk_context* duk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -771,11 +768,6 @@ static void initDuktape(tic_machine* machine)
|
||||
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)
|
||||
|
||||
32
src/luaapi.c
32
src/luaapi.c
@@ -299,7 +299,7 @@ static s32 lua_textri(lua_State* lua)
|
||||
float pt[12];
|
||||
|
||||
for (s32 i = 0; i < COUNT_OF(pt); i++)
|
||||
pt[i] = getLuaNumber(lua, i + 1);
|
||||
pt[i] = (float)lua_tonumber(lua, i + 1);
|
||||
|
||||
tic_mem* memory = (tic_mem*)getLuaMachine(lua);
|
||||
u8 chroma = 0xff;
|
||||
@@ -745,7 +745,12 @@ static s32 lua_sync(lua_State* lua)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)getLuaMachine(lua);
|
||||
|
||||
memory->api.sync(memory, true);
|
||||
bool toCart = true;
|
||||
|
||||
if(lua_gettop(lua) >= 1)
|
||||
toCart = lua_toboolean(lua, 1);
|
||||
|
||||
memory->api.sync(memory, toCart);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -956,18 +961,21 @@ static s32 lua_pmem(lua_State *lua)
|
||||
if(top >= 1)
|
||||
{
|
||||
u32 index = getLuaNumber(lua, 1);
|
||||
index %= TIC_PERSISTENT_SIZE;
|
||||
|
||||
s32 val = memory->ram.persistent.data[index];
|
||||
|
||||
if(top >= 2)
|
||||
if(index >= 0 && index < TIC_PERSISTENT_SIZE)
|
||||
{
|
||||
memory->ram.persistent.data[index] = getLuaNumber(lua, 2);
|
||||
s32 val = memory->ram.persistent.data[index];
|
||||
|
||||
if(top >= 2)
|
||||
{
|
||||
memory->ram.persistent.data[index] = getLuaNumber(lua, 2);
|
||||
}
|
||||
|
||||
lua_pushinteger(lua, val);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
lua_pushinteger(lua, val);
|
||||
|
||||
return 1;
|
||||
luaL_error(lua, "invalid persistent memory index\n");
|
||||
}
|
||||
else luaL_error(lua, "invalid params, pmem(index [val]) -> val\n");
|
||||
|
||||
@@ -1013,7 +1021,7 @@ static s32 lua_mouse(lua_State *lua)
|
||||
|
||||
static s32 lua_dofile(lua_State *lua)
|
||||
{
|
||||
luaL_error(lua, "you can use 'dofile' only on first line\n");
|
||||
luaL_error(lua, "unknown method: \"dofile\"\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -999,7 +999,7 @@ static void copyFromClipboard(Map* map)
|
||||
{
|
||||
u8* data = SDL_malloc(size);
|
||||
|
||||
str2buf(clipboard, data, true);
|
||||
str2buf(clipboard, strlen(clipboard), data, true);
|
||||
|
||||
if(data[0] * data[1] == size - 2)
|
||||
{
|
||||
|
||||
@@ -598,7 +598,7 @@ static void copyFromClipboard(Music* music)
|
||||
{
|
||||
u8* data = SDL_malloc(size);
|
||||
|
||||
str2buf(clipboard, data, true);
|
||||
str2buf(clipboard, strlen(clipboard), data, true);
|
||||
|
||||
ClipboardHeader header = {0};
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ static void tick(Run* run)
|
||||
return;
|
||||
|
||||
if(!run->init)
|
||||
{
|
||||
{
|
||||
run->tickData.start = run->tickData.counter(),
|
||||
run->init = true;
|
||||
}
|
||||
|
||||
12
src/sprite.c
12
src/sprite.c
@@ -637,6 +637,12 @@ static void drawRGBSlider(Sprite* sprite, s32 x, s32 y, u8* value)
|
||||
}
|
||||
}
|
||||
|
||||
static void pasteColor(Sprite* sprite)
|
||||
{
|
||||
fromClipboard(sprite->tic->cart.palette.data, sizeof(tic_palette), false);
|
||||
fromClipboard(&sprite->tic->cart.palette.colors[sprite->color], sizeof(tic_rgb), false);
|
||||
}
|
||||
|
||||
static void drawRGBTools(Sprite* sprite, s32 x, s32 y)
|
||||
{
|
||||
{
|
||||
@@ -713,8 +719,7 @@ static void drawRGBTools(Sprite* sprite, s32 x, s32 y)
|
||||
|
||||
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||||
{
|
||||
fromClipboard(sprite->tic->cart.palette.data, sizeof(tic_palette), false);
|
||||
sprite->tic->api.reset(sprite->tic);
|
||||
pasteColor(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1194,6 +1199,9 @@ static void cutToClipboard(Sprite* sprite)
|
||||
|
||||
static void copyFromClipboard(Sprite* sprite)
|
||||
{
|
||||
if(sprite->editPalette)
|
||||
pasteColor(sprite);
|
||||
|
||||
s32 size = sprite->size * sprite->size * TIC_PALETTE_BPP / BITS_IN_BYTE;
|
||||
u8* buffer = SDL_malloc(size);
|
||||
|
||||
|
||||
340
src/studio.c
340
src/studio.c
@@ -47,14 +47,16 @@
|
||||
#include "ext/md5.h"
|
||||
|
||||
#define STUDIO_UI_SCALE 3
|
||||
#define STUDIO_UI_BORDER 16
|
||||
|
||||
#define TEXTURE_SIZE (TIC80_FULLWIDTH)
|
||||
|
||||
#define MAX_CONTROLLERS 4
|
||||
#define STUDIO_PIXEL_FORMAT SDL_PIXELFORMAT_ARGB8888
|
||||
|
||||
#define MAX_OFFSET 128
|
||||
#define FULL_WIDTH (TIC80_WIDTH + MAX_OFFSET*2)
|
||||
#define FRAME_SIZE (TIC80_WIDTH * TIC80_HEIGHT * sizeof(u32))
|
||||
#define FRAME_SIZE (TIC80_FULLWIDTH * TIC80_FULLHEIGHT * sizeof(u32))
|
||||
|
||||
#define OFFSET_LEFT ((TIC80_FULLWIDTH-TIC80_WIDTH)/2)
|
||||
#define OFFSET_TOP ((TIC80_FULLHEIGHT-TIC80_HEIGHT)/2)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -76,12 +78,15 @@ static struct
|
||||
tic80_local* tic80local;
|
||||
tic_mem* tic;
|
||||
|
||||
CartHash hash;
|
||||
struct
|
||||
{
|
||||
CartHash hash;
|
||||
u64 mdate;
|
||||
}cart;
|
||||
|
||||
SDL_Window* window;
|
||||
SDL_Renderer* renderer;
|
||||
SDL_Texture* texture;
|
||||
SDL_Texture* borderTexture;
|
||||
|
||||
SDL_AudioSpec audioSpec;
|
||||
SDL_AudioDeviceID audioDevice;
|
||||
@@ -196,9 +201,13 @@ static struct
|
||||
.window = NULL,
|
||||
.renderer = NULL,
|
||||
.texture = NULL,
|
||||
.borderTexture = NULL,
|
||||
.audioDevice = 0,
|
||||
|
||||
.cart =
|
||||
{
|
||||
.mdate = 0,
|
||||
},
|
||||
|
||||
.joysticks = {NULL, NULL, NULL, NULL},
|
||||
|
||||
.mode = TIC_START_MODE,
|
||||
@@ -341,13 +350,11 @@ void toClipboard(const void* data, s32 size, bool flip)
|
||||
}
|
||||
}
|
||||
|
||||
void str2buf(const char* str, void* buf, bool flip)
|
||||
void str2buf(const char* str, s32 size, void* buf, bool flip)
|
||||
{
|
||||
char val[] = "0x00";
|
||||
const char* ptr = str;
|
||||
|
||||
s32 size = (s32)strlen(str);
|
||||
|
||||
for(s32 i = 0; i < size/2; i++)
|
||||
{
|
||||
if(flip)
|
||||
@@ -377,7 +384,7 @@ bool fromClipboard(void* data, s32 size, bool flip)
|
||||
{
|
||||
bool valid = strlen(clipboard) == size * 2;
|
||||
|
||||
if(valid) str2buf(clipboard, data, flip);
|
||||
if(valid) str2buf(clipboard, strlen(clipboard), data, flip);
|
||||
|
||||
SDL_free(clipboard);
|
||||
|
||||
@@ -720,6 +727,11 @@ void gotoSurf()
|
||||
setStudioMode(TIC_SURF_MODE);
|
||||
}
|
||||
|
||||
void gotoCode()
|
||||
{
|
||||
setStudioMode(TIC_CODE_MODE);
|
||||
}
|
||||
|
||||
static void initMenuMode()
|
||||
{
|
||||
initMenu(&studio.menu, studio.tic, studio.fs);
|
||||
@@ -757,6 +769,8 @@ void exitFromGameMenu()
|
||||
{
|
||||
setStudioMode(TIC_CONSOLE_MODE);
|
||||
}
|
||||
|
||||
studio.console.showGameMenu = false;
|
||||
}
|
||||
|
||||
void setStudioMode(EditorMode mode)
|
||||
@@ -884,7 +898,12 @@ void setCursor(SDL_SystemCursor id)
|
||||
|
||||
void hideDialog()
|
||||
{
|
||||
setStudioMode(studio.dialogMode);
|
||||
if(studio.dialogMode == TIC_RUN_MODE)
|
||||
{
|
||||
studio.tic->api.resume(studio.tic);
|
||||
studio.mode = TIC_RUN_MODE;
|
||||
}
|
||||
else setStudioMode(studio.dialogMode);
|
||||
}
|
||||
|
||||
void showDialog(const char** text, s32 rows, DialogCallback callback, void* data)
|
||||
@@ -909,7 +928,12 @@ static void initModules()
|
||||
|
||||
static void updateHash()
|
||||
{
|
||||
md5(&studio.tic->cart, sizeof(tic_cartridge), studio.hash.data);
|
||||
md5(&studio.tic->cart, sizeof(tic_cartridge), studio.cart.hash.data);
|
||||
}
|
||||
|
||||
static void updateMDate()
|
||||
{
|
||||
studio.cart.mdate = fsMDate(studio.console.fs, studio.console.romName);
|
||||
}
|
||||
|
||||
static void updateTitle()
|
||||
@@ -926,6 +950,7 @@ void studioRomSaved()
|
||||
{
|
||||
updateTitle();
|
||||
updateHash();
|
||||
updateMDate();
|
||||
}
|
||||
|
||||
void studioRomLoaded()
|
||||
@@ -934,6 +959,7 @@ void studioRomLoaded()
|
||||
|
||||
updateTitle();
|
||||
updateHash();
|
||||
updateMDate();
|
||||
}
|
||||
|
||||
bool studioCartChanged()
|
||||
@@ -941,7 +967,7 @@ bool studioCartChanged()
|
||||
CartHash hash;
|
||||
md5(&studio.tic->cart, sizeof(tic_cartridge), hash.data);
|
||||
|
||||
return memcmp(hash.data, studio.hash.data, sizeof(CartHash)) != 0;
|
||||
return memcmp(hash.data, studio.cart.hash.data, sizeof(CartHash)) != 0;
|
||||
}
|
||||
|
||||
static void updateGamepadParts();
|
||||
@@ -957,7 +983,9 @@ static void calcTextureRect(SDL_Rect* rect)
|
||||
|
||||
rect->x = (rect->w - discreteWidth) / 2;
|
||||
|
||||
rect->y = rect->w > rect->h ? (rect->h - discreteHeight) / 2 : 0;
|
||||
rect->y = rect->w > rect->h
|
||||
? (rect->h - discreteHeight) / 2
|
||||
: OFFSET_LEFT*discreteWidth/TIC80_WIDTH;
|
||||
|
||||
rect->w = discreteWidth;
|
||||
rect->h = discreteHeight;
|
||||
@@ -1401,78 +1429,38 @@ static u32* paletteBlit()
|
||||
return srcPaletteBlit(studio.tic->ram.vram.palette.data);
|
||||
}
|
||||
|
||||
static void blit(u32* out, u32* bgOut, s32 pitch, s32 bgPitch)
|
||||
static void screen2buffer(u32* buffer, const u32* pixels, SDL_Rect rect)
|
||||
{
|
||||
const s32 pitchWidth = pitch/sizeof *out;
|
||||
const s32 bgPitchWidth = bgPitch/sizeof *bgOut;
|
||||
u32* row = out;
|
||||
const u32* pal = paletteBlit();
|
||||
pixels += rect.y * TIC80_FULLWIDTH;
|
||||
|
||||
void(*scanline)(tic_mem* memory, s32 row) = NULL;
|
||||
|
||||
switch(studio.mode)
|
||||
for(s32 i = 0; i < rect.h; i++)
|
||||
{
|
||||
case TIC_RUN_MODE:
|
||||
scanline = studio.tic->api.scanline;
|
||||
break;
|
||||
case TIC_SPRITE_MODE:
|
||||
scanline = studio.sprite.scanline;
|
||||
break;
|
||||
case TIC_MAP_MODE:
|
||||
scanline = studio.map.scanline;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for(s32 r = 0, pos = 0; r < TIC80_HEIGHT; r++, row += pitchWidth)
|
||||
{
|
||||
if(scanline)
|
||||
{
|
||||
scanline(studio.tic, r);
|
||||
pal = paletteBlit();
|
||||
}
|
||||
|
||||
if(bgOut)
|
||||
{
|
||||
u8 border = tic_tool_peek4(studio.tic->ram.vram.mapping, studio.tic->ram.vram.vars.border & 0xf);
|
||||
SDL_memset4(bgOut, pal[border], TIC80_WIDTH);
|
||||
bgOut += bgPitchWidth;
|
||||
}
|
||||
|
||||
SDL_memset4(row, 0, pitchWidth);
|
||||
|
||||
for(u32* ptr = row + MAX_OFFSET + studio.tic->ram.vram.vars.offset.x, c = 0; c < TIC80_WIDTH; c++, ptr++)
|
||||
*ptr = pal[tic_tool_peek4(studio.tic->ram.vram.screen.data, pos++)];
|
||||
}
|
||||
}
|
||||
|
||||
static void screen2buffer(u32* buffer, const u8* pixels, s32 pitch)
|
||||
{
|
||||
for(s32 i = 0; i < TIC80_HEIGHT; i++)
|
||||
{
|
||||
SDL_memcpy(buffer, pixels+MAX_OFFSET * sizeof(u32), TIC80_WIDTH * sizeof(u32));
|
||||
pixels += pitch;
|
||||
buffer += TIC80_WIDTH;
|
||||
SDL_memcpy(buffer, pixels + rect.x, rect.w * sizeof(pixels[0]));
|
||||
pixels += TIC80_FULLWIDTH;
|
||||
buffer += rect.w;
|
||||
}
|
||||
}
|
||||
|
||||
static void setCoverImage()
|
||||
{
|
||||
tic_mem* tic = studio.tic;
|
||||
|
||||
if(studio.mode == TIC_RUN_MODE)
|
||||
{
|
||||
enum {Pitch = FULL_WIDTH*sizeof(u32)};
|
||||
u32* pixels = SDL_malloc(Pitch * TIC80_HEIGHT);
|
||||
enum {Pitch = TIC80_FULLWIDTH*sizeof(u32)};
|
||||
u32* pixels = SDL_malloc(Pitch * TIC80_FULLHEIGHT);
|
||||
|
||||
if(pixels)
|
||||
{
|
||||
blit(pixels, NULL, Pitch, 0);
|
||||
tic->api.blit(tic, pixels, tic->api.scanline);
|
||||
|
||||
u32* buffer = SDL_malloc(TIC80_WIDTH * TIC80_HEIGHT * sizeof(u32));
|
||||
|
||||
if(buffer)
|
||||
{
|
||||
screen2buffer(buffer, (const u8*)pixels, Pitch);
|
||||
SDL_Rect rect = {OFFSET_LEFT, OFFSET_TOP, TIC80_WIDTH, TIC80_HEIGHT};
|
||||
|
||||
screen2buffer(buffer, pixels, rect);
|
||||
|
||||
gif_write_animation(studio.tic->cart.cover.data, &studio.tic->cart.cover.size,
|
||||
TIC80_WIDTH, TIC80_HEIGHT, (const u8*)buffer, 1, TIC_FRAMERATE, 1);
|
||||
@@ -1503,7 +1491,7 @@ static void stopVideoRecord()
|
||||
s32 size = 0;
|
||||
u8* data = SDL_malloc(FRAME_SIZE * studio.video.frame);
|
||||
|
||||
gif_write_animation(data, &size, TIC80_WIDTH, TIC80_HEIGHT, (const u8*)studio.video.buffer, studio.video.frame, TIC_FRAMERATE, getConfig()->gifScale);
|
||||
gif_write_animation(data, &size, TIC80_FULLWIDTH, TIC80_FULLHEIGHT, (const u8*)studio.video.buffer, studio.video.frame, TIC_FRAMERATE, getConfig()->gifScale);
|
||||
|
||||
fsGetFileData(onVideoExported, "screen.gif", data, size, DEFAULT_CHMOD, NULL);
|
||||
}
|
||||
@@ -1710,6 +1698,16 @@ static void processMouseInput()
|
||||
studio.tic->ram.vram.input.gamepad.pressed = studio.mouse.state->down ? 1 : 0;
|
||||
}
|
||||
|
||||
#if defined(TIC80_PRO)
|
||||
|
||||
static void reloadConfirm(bool yes, void* data)
|
||||
{
|
||||
if(yes)
|
||||
studio.console.updateProject(&studio.console);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
SDL_Event* pollEvent()
|
||||
{
|
||||
static SDL_Event event;
|
||||
@@ -1751,9 +1749,38 @@ SDL_Event* pollEvent()
|
||||
{
|
||||
case SDL_WINDOWEVENT_RESIZED: updateGamepadParts(); break;
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
|
||||
#if defined(TIC80_PRO)
|
||||
|
||||
if(studio.mode != TIC_START_MODE)
|
||||
{
|
||||
studio.console.codeLiveReload.reload(&studio.console,studio.code.data);
|
||||
if(studio.code.update)
|
||||
Console* console = &studio.console;
|
||||
|
||||
u64 mdate = fsMDate(console->fs, console->romName);
|
||||
|
||||
if(mdate > studio.cart.mdate)
|
||||
{
|
||||
if(studioCartChanged())
|
||||
{
|
||||
static const char* Rows[] =
|
||||
{
|
||||
"",
|
||||
"CART HAS CHANGED!",
|
||||
"",
|
||||
"DO YOU WANT",
|
||||
"TO RELOAD IT?"
|
||||
};
|
||||
|
||||
showDialog(Rows, COUNT_OF(Rows), reloadConfirm, NULL);
|
||||
}
|
||||
else console->updateProject(console);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
{
|
||||
studio.console.codeLiveReload.reload(&studio.console, studio.code.data);
|
||||
if(studio.console.codeLiveReload.active && studio.code.update)
|
||||
studio.code.update(&studio.code);
|
||||
}
|
||||
break;
|
||||
@@ -1830,6 +1857,7 @@ static void blitSound()
|
||||
{
|
||||
s32 samples = studio.audioSpec.freq / TIC_FRAMERATE;
|
||||
|
||||
// TODO: use SDL_ConvertAudio to covert format
|
||||
if(studio.audioSpec.format == AUDIO_F32)
|
||||
{
|
||||
if(!studio.floatSamples)
|
||||
@@ -1847,7 +1875,7 @@ static void blitSound()
|
||||
SDL_QueueAudio(studio.audioDevice, studio.tic->samples.buffer, studio.tic->samples.size);
|
||||
}
|
||||
|
||||
static void drawRecordLabel(u8* frame, s32 pitch, s32 sx, s32 sy, const u32* color)
|
||||
static void drawRecordLabel(u32* frame, s32 pitch, s32 sx, s32 sy, const u32* color)
|
||||
{
|
||||
static const u16 RecLabel[] =
|
||||
{
|
||||
@@ -1863,23 +1891,24 @@ static void drawRecordLabel(u8* frame, s32 pitch, s32 sx, s32 sy, const u32* col
|
||||
for(s32 x = 0; x < sizeof RecLabel[0]*BITS_IN_BYTE; x++)
|
||||
{
|
||||
if(RecLabel[y] & (1 << x))
|
||||
memcpy(&frame[((MAX_OFFSET + sx) + 15 - x + (y+sy)*(pitch/4))*4], color, sizeof *color);
|
||||
memcpy(&frame[sx + 15 - x + (y+sy)*TIC80_FULLWIDTH], color, sizeof *color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void recordFrame(u8* pixels, s32 pitch)
|
||||
static void recordFrame(u32* pixels)
|
||||
{
|
||||
if(studio.video.record)
|
||||
{
|
||||
if(studio.video.frame < studio.video.frames)
|
||||
{
|
||||
screen2buffer(studio.video.buffer + (TIC80_WIDTH*TIC80_HEIGHT) * studio.video.frame, pixels, pitch);
|
||||
SDL_Rect rect = {0, 0, TIC80_FULLWIDTH, TIC80_FULLHEIGHT};
|
||||
screen2buffer(studio.video.buffer + (TIC80_FULLWIDTH*TIC80_FULLHEIGHT) * studio.video.frame, pixels, rect);
|
||||
|
||||
if(studio.video.frame % TIC_FRAMERATE < TIC_FRAMERATE / 2)
|
||||
{
|
||||
const u32* pal = srcPaletteBlit(studio.tic->config.palette.data);
|
||||
drawRecordLabel(pixels, pitch, TIC80_WIDTH-24, 8, &pal[tic_color_red]);
|
||||
drawRecordLabel(pixels, TIC80_FULLWIDTH, TIC80_WIDTH-24, 8, &pal[tic_color_red]);
|
||||
}
|
||||
|
||||
studio.video.frame++;
|
||||
@@ -1894,43 +1923,74 @@ static void recordFrame(u8* pixels, s32 pitch)
|
||||
|
||||
static void blitTexture()
|
||||
{
|
||||
tic_mem* tic = studio.tic;
|
||||
SDL_Rect rect = {0, 0, 0, 0};
|
||||
calcTextureRect(&rect);
|
||||
|
||||
const s32 Pixel = rect.w / TIC80_WIDTH;
|
||||
rect.x -= MAX_OFFSET * Pixel;
|
||||
rect.w = rect.w * FULL_WIDTH / TIC80_WIDTH;
|
||||
|
||||
void* pixels = NULL;
|
||||
s32 pitch = 0;
|
||||
SDL_LockTexture(studio.texture, NULL, &pixels, &pitch);
|
||||
|
||||
if(studio.mode == TIC_RUN_MODE)
|
||||
tic_scanline scanline = NULL;
|
||||
|
||||
switch(studio.mode)
|
||||
{
|
||||
rect.y += studio.tic->ram.vram.vars.offset.y * Pixel;
|
||||
|
||||
{
|
||||
void* bgPixels = NULL;
|
||||
s32 bgPitch = 0;
|
||||
SDL_LockTexture(studio.borderTexture, NULL, &bgPixels, &bgPitch);
|
||||
blit(pixels, bgPixels, pitch, bgPitch);
|
||||
SDL_UnlockTexture(studio.borderTexture);
|
||||
|
||||
{
|
||||
SDL_Rect srcRect = {0, 0, TIC80_WIDTH, TIC80_HEIGHT};
|
||||
SDL_RenderCopy(studio.renderer, studio.borderTexture, &srcRect, NULL);
|
||||
}
|
||||
}
|
||||
case TIC_RUN_MODE:
|
||||
scanline = tic->api.scanline;
|
||||
break;
|
||||
case TIC_SPRITE_MODE:
|
||||
scanline = studio.sprite.scanline;
|
||||
break;
|
||||
case TIC_MAP_MODE:
|
||||
scanline = studio.map.scanline;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
else blit(pixels, NULL, pitch, 0);
|
||||
|
||||
recordFrame(pixels, pitch);
|
||||
tic->api.blit(tic, pixels, scanline);
|
||||
|
||||
recordFrame(pixels);
|
||||
|
||||
SDL_UnlockTexture(studio.texture);
|
||||
|
||||
{
|
||||
SDL_Rect srcRect = {0, 0, FULL_WIDTH, TIC80_HEIGHT};
|
||||
SDL_RenderCopy(studio.renderer, studio.texture, &srcRect, &rect);
|
||||
enum {Header = OFFSET_TOP};
|
||||
SDL_Rect srcRect = {0, 0, TIC80_FULLWIDTH, Header};
|
||||
SDL_Rect dstRect = {0};
|
||||
SDL_GetWindowSize(studio.window, &dstRect.w, &dstRect.h);
|
||||
dstRect.h = rect.y;
|
||||
SDL_RenderCopy(studio.renderer, studio.texture, &srcRect, &dstRect);
|
||||
}
|
||||
|
||||
{
|
||||
enum {Header = OFFSET_TOP};
|
||||
SDL_Rect srcRect = {0, TIC80_FULLHEIGHT - Header, TIC80_FULLWIDTH, Header};
|
||||
SDL_Rect dstRect = {0};
|
||||
SDL_GetWindowSize(studio.window, &dstRect.w, &dstRect.h);
|
||||
dstRect.y = rect.y + rect.h;
|
||||
dstRect.h = rect.y;
|
||||
SDL_RenderCopy(studio.renderer, studio.texture, &srcRect, &dstRect);
|
||||
}
|
||||
|
||||
{
|
||||
enum {Header = OFFSET_TOP};
|
||||
enum {Left = OFFSET_LEFT};
|
||||
SDL_Rect srcRect = {0, Header, Left, TIC80_HEIGHT};
|
||||
SDL_Rect dstRect = {0};
|
||||
SDL_GetWindowSize(studio.window, &dstRect.w, &dstRect.h);
|
||||
dstRect.y = rect.y;
|
||||
dstRect.h = rect.h;
|
||||
SDL_RenderCopy(studio.renderer, studio.texture, &srcRect, &dstRect);
|
||||
}
|
||||
|
||||
{
|
||||
enum {Top = OFFSET_TOP};
|
||||
enum {Left = OFFSET_LEFT};
|
||||
|
||||
SDL_Rect srcRect = {Left, Top, TIC80_WIDTH, TIC80_HEIGHT};
|
||||
|
||||
SDL_RenderCopy(studio.renderer, studio.texture, &srcRect, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2012,6 +2072,8 @@ static void useSystemPalette()
|
||||
|
||||
static void renderStudio()
|
||||
{
|
||||
tic_mem* tic = studio.tic;
|
||||
|
||||
showTooltip("");
|
||||
|
||||
studio.gesture.active = false;
|
||||
@@ -2067,6 +2129,9 @@ static void renderStudio()
|
||||
TIC80_HEIGHT - TIC_FONT_HEIGHT, (tic_color_white));
|
||||
}
|
||||
|
||||
if(getConfig()->noSound)
|
||||
SDL_memset(tic->ram.registers, 0, sizeof tic->ram.registers);
|
||||
|
||||
studio.tic->api.tick_end(studio.tic);
|
||||
|
||||
blitSound();
|
||||
@@ -2175,11 +2240,6 @@ static void tick()
|
||||
SDL_SystemCursor cursor = studio.mouse.system;
|
||||
studio.mouse.system = SDL_SYSTEM_CURSOR_ARROW;
|
||||
|
||||
{
|
||||
const u8* pal = (u8*)(paletteBlit() + tic_tool_peek4(studio.tic->ram.vram.mapping, studio.tic->ram.vram.vars.border & 0xf));
|
||||
SDL_SetRenderDrawColor(studio.renderer, pal[2], pal[1], pal[0], SDL_ALPHA_OPAQUE);
|
||||
}
|
||||
|
||||
SDL_RenderClear(studio.renderer);
|
||||
|
||||
renderStudio();
|
||||
@@ -2212,14 +2272,6 @@ static void initSound()
|
||||
SDL_PauseAudioDevice(studio.audioDevice, 0);
|
||||
}
|
||||
|
||||
static s32 textureLog2(s32 val)
|
||||
{
|
||||
u32 rom = 0;
|
||||
while( val >>= 1 ) rom++;
|
||||
|
||||
return 1 << ++rom;
|
||||
}
|
||||
|
||||
static void initTouchGamepad()
|
||||
{
|
||||
if (!studio.renderer)
|
||||
@@ -2229,8 +2281,7 @@ static void initTouchGamepad()
|
||||
|
||||
if(!studio.gamepad.texture)
|
||||
{
|
||||
studio.gamepad.texture = SDL_CreateTexture(studio.renderer, STUDIO_PIXEL_FORMAT, SDL_TEXTUREACCESS_STREAMING,
|
||||
textureLog2(TIC80_WIDTH), textureLog2(TIC80_HEIGHT));
|
||||
studio.gamepad.texture = SDL_CreateTexture(studio.renderer, STUDIO_PIXEL_FORMAT, SDL_TEXTUREACCESS_STREAMING, TEXTURE_SIZE, TEXTURE_SIZE);
|
||||
SDL_SetTextureBlendMode(studio.gamepad.texture, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
|
||||
@@ -2329,13 +2380,13 @@ static void onFSInitialized(FileSystem* fs)
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK);
|
||||
|
||||
studio.window = SDL_CreateWindow( TIC_TITLE, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
(TIC80_WIDTH+STUDIO_UI_BORDER) * STUDIO_UI_SCALE,
|
||||
(TIC80_HEIGHT+STUDIO_UI_BORDER) * STUDIO_UI_SCALE,
|
||||
(TIC80_FULLWIDTH) * STUDIO_UI_SCALE,
|
||||
(TIC80_FULLHEIGHT) * STUDIO_UI_SCALE,
|
||||
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
|
||||
#if defined(__ARM_LINUX__)
|
||||
| SDL_WINDOW_FULLSCREEN_DESKTOP
|
||||
#if defined(__CHIP__)
|
||||
| SDL_WINDOW_FULLSCREEN_DESKTOP
|
||||
#endif
|
||||
);
|
||||
);
|
||||
|
||||
initSound();
|
||||
|
||||
@@ -2363,22 +2414,15 @@ static void onFSInitialized(FileSystem* fs)
|
||||
// set the window icon before renderer is created (issues on Linux)
|
||||
setWindowIcon();
|
||||
|
||||
#if defined(__ARM_LINUX__)
|
||||
s32 renderFlags = SDL_RENDERER_SOFTWARE;
|
||||
studio.renderer = SDL_CreateRenderer(studio.window, -1,
|
||||
#if defined(__CHIP__)
|
||||
SDL_RENDERER_SOFTWARE
|
||||
#else
|
||||
s32 renderFlags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC;
|
||||
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
|
||||
#endif
|
||||
);
|
||||
|
||||
studio.renderer = SDL_CreateRenderer(studio.window, -1, renderFlags);
|
||||
studio.texture = SDL_CreateTexture(studio.renderer, STUDIO_PIXEL_FORMAT, SDL_TEXTUREACCESS_STREAMING,
|
||||
textureLog2(FULL_WIDTH), textureLog2(TIC80_HEIGHT));
|
||||
|
||||
#if !defined(__ARM_LINUX__)
|
||||
SDL_SetTextureBlendMode(studio.texture, SDL_BLENDMODE_BLEND);
|
||||
#endif
|
||||
|
||||
studio.borderTexture = SDL_CreateTexture(studio.renderer, STUDIO_PIXEL_FORMAT, SDL_TEXTUREACCESS_STREAMING,
|
||||
textureLog2(TIC80_WIDTH), textureLog2(TIC80_HEIGHT));
|
||||
studio.texture = SDL_CreateTexture(studio.renderer, STUDIO_PIXEL_FORMAT, SDL_TEXTUREACCESS_STREAMING, TEXTURE_SIZE, TEXTURE_SIZE);
|
||||
|
||||
initTouchGamepad();
|
||||
}
|
||||
@@ -2390,7 +2434,7 @@ static void onFSInitialized(FileSystem* fs)
|
||||
void onEmscriptenWget(const char* file)
|
||||
{
|
||||
studio.argv[1] = DEFAULT_CART;
|
||||
createFileSystem(onFSInitialized);
|
||||
createFileSystem(NULL, onFSInitialized);
|
||||
}
|
||||
|
||||
void onEmscriptenWgetError(const char* error) {}
|
||||
@@ -2417,29 +2461,30 @@ s32 main(s32 argc, char **argv)
|
||||
{
|
||||
emscripten_async_wget(studio.argv[1], DEFAULT_CART, onEmscriptenWget, onEmscriptenWgetError);
|
||||
}
|
||||
else createFileSystem(onFSInitialized);
|
||||
else createFileSystem(NULL, onFSInitialized);
|
||||
|
||||
emscripten_set_main_loop(tick, TIC_FRAMERATE == 60 ? 0 : TIC_FRAMERATE, 1);
|
||||
#else
|
||||
|
||||
createFileSystem(onFSInitialized);
|
||||
createFileSystem(argc > 1 && fsExists(argv[1]) ? fsBasename(argv[1]) : NULL, onFSInitialized);
|
||||
|
||||
u64 nextTick = SDL_GetPerformanceCounter();
|
||||
const u64 Delta = SDL_GetPerformanceFrequency() / TIC_FRAMERATE;
|
||||
|
||||
while (!studio.quitFlag)
|
||||
{
|
||||
nextTick += Delta;
|
||||
tick();
|
||||
u64 nextTick = SDL_GetPerformanceCounter();
|
||||
const u64 Delta = SDL_GetPerformanceFrequency() / TIC_FRAMERATE;
|
||||
|
||||
while (!studio.quitFlag)
|
||||
{
|
||||
nextTick += Delta;
|
||||
tick();
|
||||
|
||||
s64 delay = nextTick - SDL_GetPerformanceCounter();
|
||||
s64 delay = nextTick - SDL_GetPerformanceCounter();
|
||||
|
||||
if(delay > 0)
|
||||
SDL_Delay((u32)(delay * 1000 / SDL_GetPerformanceFrequency()));
|
||||
else nextTick -= delay;
|
||||
if(delay > 0)
|
||||
SDL_Delay((u32)(delay * 1000 / SDL_GetPerformanceFrequency()));
|
||||
else nextTick -= delay;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
if(studio.tic80local)
|
||||
@@ -2450,7 +2495,6 @@ s32 main(s32 argc, char **argv)
|
||||
|
||||
SDL_DestroyTexture(studio.gamepad.texture);
|
||||
SDL_DestroyTexture(studio.texture);
|
||||
SDL_DestroyTexture(studio.borderTexture);
|
||||
|
||||
if(studio.mouse.texture)
|
||||
SDL_DestroyTexture(studio.mouse.texture);
|
||||
|
||||
@@ -60,6 +60,11 @@
|
||||
#define KEYMAP_DAT "keymap.dat"
|
||||
#define KEYMAP_DAT_PATH TIC_LOCAL KEYMAP_DAT
|
||||
|
||||
#define CART_EXT ".tic"
|
||||
#define PROJECT_LUA_EXT ".lua"
|
||||
#define PROJECT_MOON_EXT ".moon"
|
||||
#define PROJECT_JS_EXT ".js"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
@@ -100,6 +105,7 @@ typedef struct
|
||||
s32 gifLength;
|
||||
|
||||
bool checkNewVersion;
|
||||
bool noSound;
|
||||
|
||||
} StudioConfig;
|
||||
|
||||
@@ -146,7 +152,7 @@ EditorMode getStudioMode();
|
||||
void exitStudio();
|
||||
u32 unzip(u8** dest, const u8* source, size_t size);
|
||||
|
||||
void str2buf(const char* str, void* buf, bool flip);
|
||||
void str2buf(const char* str, s32 size, void* buf, bool flip);
|
||||
void toClipboard(const void* data, s32 size, bool flip);
|
||||
bool fromClipboard(void* data, s32 size, bool flip);
|
||||
|
||||
@@ -189,6 +195,7 @@ bool studioCartChanged();
|
||||
void playSystemSfx(s32 id);
|
||||
|
||||
void runGameFromSurf();
|
||||
void gotoCode();
|
||||
void gotoSurf();
|
||||
void exitFromGameMenu();
|
||||
void runProject();
|
||||
|
||||
62
src/surf.c
62
src/surf.c
@@ -38,7 +38,7 @@
|
||||
#define COVER_Y 5
|
||||
#define COVER_X (TIC80_WIDTH - COVER_WIDTH - COVER_Y)
|
||||
|
||||
#if defined(__WINDOWS__) || (defined(__LINUX__) && !defined(__ARM_LINUX__)) || defined(__MACOSX__)
|
||||
#if defined(__WINDOWS__) || defined(__LINUX__) || defined(__MACOSX__)
|
||||
#define CAN_OPEN_URL 1
|
||||
#endif
|
||||
|
||||
@@ -155,6 +155,7 @@ struct MenuItem
|
||||
s32 id;
|
||||
tic_screen* cover;
|
||||
bool dir;
|
||||
bool project;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
@@ -372,18 +373,35 @@ static void replace(char* src, const char* what, const char* with)
|
||||
}
|
||||
}
|
||||
|
||||
static bool hasExt(const char* name, const char* ext)
|
||||
{
|
||||
return strcmp(name + strlen(name) - strlen(ext), ext) == 0;
|
||||
}
|
||||
|
||||
static void cutExt(char* name, const char* ext)
|
||||
{
|
||||
name[strlen(name)-strlen(ext)] = '\0';
|
||||
}
|
||||
|
||||
static bool addMenuItem(const char* name, const char* info, s32 id, void* ptr, bool dir)
|
||||
{
|
||||
AddMenuItem* data = (AddMenuItem*)ptr;
|
||||
|
||||
static const char CartExt[] = ".tic";
|
||||
static const char CartExt[] = CART_EXT;
|
||||
|
||||
if(dir || (strstr(name, CartExt) == name + strlen(name) - sizeof(CartExt)+1))
|
||||
if(dir
|
||||
|| hasExt(name, CartExt)
|
||||
#if defined(TIC80_PRO)
|
||||
|| hasExt(name, PROJECT_LUA_EXT)
|
||||
|| hasExt(name, PROJECT_MOON_EXT)
|
||||
|| hasExt(name, PROJECT_JS_EXT)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
MenuItem* item = &data->items[data->count++];
|
||||
|
||||
item->name = SDL_strdup(name);
|
||||
|
||||
bool project = false;
|
||||
if(dir)
|
||||
{
|
||||
char folder[FILENAME_MAX];
|
||||
@@ -394,7 +412,14 @@ static bool addMenuItem(const char* name, const char* info, s32 id, void* ptr, b
|
||||
{
|
||||
|
||||
item->label = SDL_strdup(name);
|
||||
item->label[strlen(item->label)-sizeof(CartExt)+1] = '\0';
|
||||
|
||||
if(hasExt(name, CartExt))
|
||||
cutExt(item->label, CartExt);
|
||||
else
|
||||
{
|
||||
project = true;
|
||||
}
|
||||
|
||||
|
||||
replace(item->label, "&", "&");
|
||||
replace(item->label, "'", "'");
|
||||
@@ -404,6 +429,7 @@ static bool addMenuItem(const char* name, const char* info, s32 id, void* ptr, b
|
||||
item->id = id;
|
||||
item->dir = dir;
|
||||
item->cover = NULL;
|
||||
item->project = project;
|
||||
}
|
||||
|
||||
return data->count < MAX_CARTS;
|
||||
@@ -509,7 +535,10 @@ static void loadCover(Surf* surf)
|
||||
|
||||
if(cart)
|
||||
{
|
||||
tic->api.load(cart, data, size, true);
|
||||
if(hasExt(item->name, PROJECT_LUA_EXT))
|
||||
surf->console->loadProject(surf->console, item->name, data, size, cart);
|
||||
else
|
||||
tic->api.load(cart, data, size, true);
|
||||
|
||||
if(cart->cover.size)
|
||||
updateMenuItemCover(surf, cart->cover.data, cart->cover.size);
|
||||
@@ -617,7 +646,26 @@ static void onPlayCart(Surf* surf)
|
||||
{
|
||||
MenuItem* item = &surf->menu.items[surf->menu.pos];
|
||||
|
||||
surf->console->load(surf->console, item->name);
|
||||
if(item->project)
|
||||
{
|
||||
tic_cartridge* cart = SDL_malloc(sizeof(tic_cartridge));
|
||||
|
||||
if(cart)
|
||||
{
|
||||
s32 size = 0;
|
||||
void* data = fsLoadFile(surf->fs, item->name, &size);
|
||||
|
||||
surf->console->loadProject(surf->console, item->name, data, size, cart);
|
||||
|
||||
SDL_memcpy(&surf->tic->cart, cart, sizeof(tic_cartridge));
|
||||
|
||||
studioRomLoaded();
|
||||
|
||||
SDL_free(cart);
|
||||
}
|
||||
}
|
||||
else
|
||||
surf->console->load(surf->console, item->name);
|
||||
|
||||
runGameFromSurf();
|
||||
}
|
||||
|
||||
269
src/tic.c
269
src/tic.c
@@ -53,7 +53,7 @@ typedef enum
|
||||
CHUNK_TEMP3, // 8
|
||||
CHUNK_SOUND, // 9
|
||||
CHUNK_WAVEFORM, // 10
|
||||
CHUNK_OLDMUSIC, // 11
|
||||
CHUNK_TEMP4, // 11
|
||||
CHUNK_PALETTE, // 12
|
||||
CHUNK_PATTERNS, // 13
|
||||
CHUNK_MUSIC, // 14
|
||||
@@ -83,17 +83,17 @@ static void update_amp(blip_buffer_t* blip, tic_sound_register_data* data, s32 n
|
||||
blip_add_delta( blip, data->time, delta );
|
||||
}
|
||||
|
||||
inline s32 freq2note(double freq)
|
||||
static inline s32 freq2note(double freq)
|
||||
{
|
||||
return (s32)round((double)NOTES * log2(freq / BASE_NOTE_FREQ)) + BASE_NOTE_POS;
|
||||
}
|
||||
|
||||
inline double note2freq(s32 note)
|
||||
static inline double note2freq(s32 note)
|
||||
{
|
||||
return pow(2, (note - BASE_NOTE_POS) / (double)NOTES) * BASE_NOTE_FREQ;
|
||||
}
|
||||
|
||||
inline s32 freq2period(double freq)
|
||||
static inline s32 freq2period(double freq)
|
||||
{
|
||||
if(freq == 0.0) return MAX_PERIOD_VALUE;
|
||||
|
||||
@@ -106,7 +106,7 @@ inline s32 freq2period(double freq)
|
||||
return period;
|
||||
}
|
||||
|
||||
inline s32 getAmp(const tic_sound_register* reg, s32 amp)
|
||||
static inline s32 getAmp(const tic_sound_register* reg, s32 amp)
|
||||
{
|
||||
enum {MaxAmp = (u16)-1 / (MAX_VOLUME * TIC_SOUND_CHANNELS)};
|
||||
|
||||
@@ -149,18 +149,18 @@ static void resetPalette(tic_mem* memory)
|
||||
memory->ram.vram.vars.mask.data = TIC_GAMEPAD_MASK;
|
||||
}
|
||||
|
||||
static void setPixel(tic_machine* machine, s32 x, s32 y, u8 color)
|
||||
static inline void setPixel(tic_machine* machine, s32 x, s32 y, u8 color)
|
||||
{
|
||||
if(x < machine->state.clip.l || y < machine->state.clip.t || x >= machine->state.clip.r || y >= machine->state.clip.b) return;
|
||||
|
||||
tic_tool_poke4(machine->memory.ram.vram.screen.data, y * TIC80_WIDTH + x, tic_tool_peek4(machine->memory.ram.vram.mapping, color));
|
||||
tic_tool_poke4(machine->memory.ram.vram.screen.data, y * TIC80_WIDTH + x, tic_tool_peek4(machine->memory.ram.vram.mapping, color & 0xf));
|
||||
}
|
||||
|
||||
static u8 getPixel(tic_machine* machine, s32 x, s32 y)
|
||||
{
|
||||
if(x < 0 || y < 0 || x >= TIC80_WIDTH || y >= TIC80_HEIGHT) return 0;
|
||||
|
||||
return tic_tool_peek4(machine->memory.ram.vram.mapping, tic_tool_peek4(machine->memory.ram.vram.screen.data, y * TIC80_WIDTH + x));
|
||||
return tic_tool_peek4(machine->memory.ram.vram.screen.data, y * TIC80_WIDTH + x);
|
||||
}
|
||||
|
||||
static void drawHLine(tic_machine* machine, s32 x, s32 y, s32 width, u8 color)
|
||||
@@ -200,62 +200,63 @@ static void drawRectBorder(tic_machine* machine, s32 x, s32 y, s32 width, s32 he
|
||||
drawVLine(machine, x + width - 1, y, height, color);
|
||||
}
|
||||
|
||||
static void drawRectRev(tic_machine* machine, s32 y, s32 x, s32 height, s32 width, u8 color)
|
||||
{
|
||||
drawRect(machine, x, y, width, height, color);
|
||||
}
|
||||
|
||||
static void drawTile(tic_machine* machine, const tic_tile* buffer, s32 x, s32 y, u8* colors, s32 count, s32 scale, tic_flip flip, tic_rotate rotate)
|
||||
{
|
||||
static u8 transparent[TIC_PALETTE_SIZE];
|
||||
memset(transparent, 0, sizeof(transparent));
|
||||
for (s32 i = 0; i < count; i++) transparent[colors[i]] = 1;
|
||||
|
||||
flip &= 0b11;
|
||||
rotate &= 0b11;
|
||||
|
||||
s32 a = flip & tic_horz_flip ? -scale : scale;
|
||||
s32 b = flip & tic_vert_flip ? -scale : scale;
|
||||
if (flip == 0 && rotate == 0 && scale == 1) {
|
||||
// the most common path
|
||||
s32 i = 0;
|
||||
for(s32 py=0; py < TIC_SPRITESIZE; py++, y++)
|
||||
{
|
||||
s32 xx = x;
|
||||
for(s32 px=0; px < TIC_SPRITESIZE; px++, xx++)
|
||||
{
|
||||
u8 color = tic_tool_peek4(buffer, i++);
|
||||
if(!transparent[color]) setPixel(machine, xx, y, color);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void(*drawRectFunc)(tic_machine*, s32, s32, s32, s32, u8) = drawRect;
|
||||
s32 a = flip & tic_horz_flip ? -1 : 1;
|
||||
s32 b = flip & tic_vert_flip ? -1 : 1;
|
||||
|
||||
if(rotate == tic_90_rotate) a = -a;
|
||||
else if(rotate == tic_180_rotate) a = -a, b = -b;
|
||||
else if(rotate == tic_270_rotate) b = -b;
|
||||
|
||||
x += (scale - a) * (TIC_SPRITESIZE - 1) >> 1;
|
||||
y += (scale - b) * (TIC_SPRITESIZE - 1) >> 1;
|
||||
bool swap_axis = (rotate == tic_90_rotate || rotate == tic_270_rotate);
|
||||
|
||||
if(rotate == tic_90_rotate || rotate == tic_270_rotate)
|
||||
for(s32 py=0; py < TIC_SPRITESIZE; py++, y+=scale)
|
||||
{
|
||||
s32 t = a; a = b; b = t;
|
||||
t = x; x = y; y = t;
|
||||
drawRectFunc = drawRectRev;
|
||||
}
|
||||
|
||||
for(s32 i = 0, px = 0, xx = x; i < TIC_SPRITESIZE * TIC_SPRITESIZE; px++, xx += a, i++)
|
||||
{
|
||||
if(px == TIC_SPRITESIZE) px = 0, xx = x, y += b;
|
||||
|
||||
u8 color = tic_tool_peek4(buffer, i);
|
||||
|
||||
if(count == 1)
|
||||
s32 xx = x;
|
||||
for(s32 px=0; px < TIC_SPRITESIZE; px++, xx+=scale)
|
||||
{
|
||||
if(*colors != color)
|
||||
drawRectFunc(machine, xx, y, scale, scale, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool draw = true;
|
||||
for(s32 i = 0; i < count; i++)
|
||||
{
|
||||
if(color == colors[i])
|
||||
{
|
||||
draw = false;
|
||||
break;
|
||||
s32 i;
|
||||
s32 ix, iy;
|
||||
ix = a == 1 ? px : TIC_SPRITESIZE - px - 1;
|
||||
iy = b == 1 ? py : TIC_SPRITESIZE - py - 1;
|
||||
if(swap_axis) {
|
||||
i = ix * TIC_SPRITESIZE + iy;
|
||||
} else {
|
||||
i = iy * TIC_SPRITESIZE + ix;
|
||||
}
|
||||
u8 color = tic_tool_peek4(buffer, i);
|
||||
if(!transparent[color]) {
|
||||
if (scale == 1) {
|
||||
setPixel(machine, xx, y, color);
|
||||
} else {
|
||||
drawRect(machine, xx, y, scale, scale, color);
|
||||
}
|
||||
}
|
||||
|
||||
if(draw)
|
||||
drawRectFunc(machine, xx, y, scale, scale, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawMap(tic_machine* machine, const tic_gfx* src, s32 x, s32 y, s32 width, s32 height, s32 sx, s32 sy, u8 chromakey, s32 scale, RemapFunc remap, void* data)
|
||||
@@ -493,6 +494,8 @@ static void api_clear(tic_mem* memory, u8 color)
|
||||
{
|
||||
api_rect(memory, machine->state.clip.l, machine->state.clip.t, machine->state.clip.r - machine->state.clip.l, machine->state.clip.b - machine->state.clip.t, color);
|
||||
}
|
||||
|
||||
memory->ram.vram.vars.bg = color & 0xf;
|
||||
}
|
||||
|
||||
static s32 drawChar(tic_mem* memory, u8 symbol, s32 x, s32 y, s32 width, s32 height, u8 color, s32 scale)
|
||||
@@ -846,6 +849,7 @@ static void ticTexLine(tic_mem* memory, TexVert *v0, TexVert *v1)
|
||||
|
||||
float dy = bot->y - top->y;
|
||||
if ((s32)dy == 0) return;
|
||||
if ((s32)dy > TIC80_HEIGHT) return; // reject large polys
|
||||
|
||||
float step_x = (bot->x - top->x) / dy;
|
||||
float step_u = (bot->u - top->u) / dy;
|
||||
@@ -864,7 +868,7 @@ static void ticTexLine(tic_mem* memory, TexVert *v0, TexVert *v1)
|
||||
}
|
||||
}
|
||||
|
||||
static void api_textri(tic_mem* memory, s32 x1, s32 y1, s32 x2, s32 y2, s32 x3, s32 y3, s32 u1, s32 v1, s32 u2, s32 v2, s32 u3, s32 v3,bool use_map,u8 chroma)
|
||||
static void api_textri(tic_mem* memory, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, bool use_map, u8 chroma)
|
||||
{
|
||||
tic_machine* machine = (tic_machine*)memory;
|
||||
TexVert V0, V1, V2;
|
||||
@@ -1375,77 +1379,15 @@ static void api_tick(tic_mem* memory, tic_tick_data* data)
|
||||
{
|
||||
cart2ram(memory);
|
||||
|
||||
char* code = machine->memory.cart.code.data;
|
||||
if(code && strlen(code))
|
||||
const char* code = machine->memory.cart.code.data;
|
||||
|
||||
if(strlen(code))
|
||||
{
|
||||
static const char DoFileTag[] = "dofile(";
|
||||
enum {Size = sizeof DoFileTag - 1};
|
||||
|
||||
if (memcmp(code, DoFileTag, Size) == 0)
|
||||
{
|
||||
const char* start = code + Size;
|
||||
const char* end = strchr(start, ')');
|
||||
|
||||
if(end && *start == *(end-1) && (*start == '"' || *start == '\''))
|
||||
{
|
||||
char filename[FILENAME_MAX] = {0};
|
||||
memcpy(filename, start + 1, end - start - 2);
|
||||
|
||||
FILE* file = fopen(filename, "rb");
|
||||
|
||||
if(file)
|
||||
{
|
||||
fseek(file, 0, SEEK_END);
|
||||
s32 size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
if(size > 0)
|
||||
{
|
||||
if(size > TIC_CODE_SIZE)
|
||||
{
|
||||
char buffer[256];
|
||||
sprintf(buffer, "code is larger than %i symbols", TIC_CODE_SIZE);
|
||||
machine->data->error(machine->data->data, buffer);
|
||||
|
||||
fclose(file);
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
void* buffer = malloc(size+1);
|
||||
|
||||
if(buffer)
|
||||
{
|
||||
memset(buffer, 0, size+1);
|
||||
|
||||
if(fread(buffer, size, 1, file)) {}
|
||||
|
||||
code = buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
char buffer[256];
|
||||
sprintf(buffer, "dofile: file '%s' not found", filename);
|
||||
machine->data->error(machine->data->data, buffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memory->input = compareMetatag(code, "input", "mouse") ? tic_mouse_input : tic_gamepad_input;
|
||||
|
||||
|
||||
if(memory->input == tic_mouse_input)
|
||||
memory->ram.vram.vars.mask.data = 0;
|
||||
|
||||
//////////////////////////
|
||||
|
||||
memory->script = tic_script_lua;
|
||||
|
||||
if (isMoonscript(code))
|
||||
@@ -1465,13 +1407,13 @@ static void api_tick(tic_mem* memory, tic_tick_data* data)
|
||||
else if(!initLua(machine, code))
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: possible memory leak if script not initialozed
|
||||
if(code != machine->memory.cart.code.data)
|
||||
free(code);
|
||||
else
|
||||
{
|
||||
machine->data->error(machine->data->data, "the code is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
machine->state.scanline = memory->script == tic_script_js ? callJavascriptScanline : callLuaScanline;
|
||||
|
||||
machine->state.initialized = true;
|
||||
}
|
||||
|
||||
@@ -1637,6 +1579,96 @@ static s32 api_save(const tic_cartridge* cart, u8* buffer)
|
||||
return (s32)(buffer - start);
|
||||
}
|
||||
|
||||
// copied from SDL2
|
||||
static inline void memset4(void *dst, u32 val, u32 dwords)
|
||||
{
|
||||
#if defined(__GNUC__) && defined(i386)
|
||||
s32 u0, u1, u2;
|
||||
__asm__ __volatile__ (
|
||||
"cld \n\t"
|
||||
"rep ; stosl \n\t"
|
||||
: "=&D" (u0), "=&a" (u1), "=&c" (u2)
|
||||
: "0" (dst), "1" (val), "2" (dwords)
|
||||
: "memory"
|
||||
);
|
||||
#else
|
||||
u32 _n = (dwords + 3) / 4;
|
||||
u32 *_p = (u32*)dst;
|
||||
u32 _val = (val);
|
||||
if (dwords == 0)
|
||||
return;
|
||||
switch (dwords % 4)
|
||||
{
|
||||
case 0: do { *_p++ = _val;
|
||||
case 3: *_p++ = _val;
|
||||
case 2: *_p++ = _val;
|
||||
case 1: *_p++ = _val;
|
||||
} while ( --_n );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static u32* paletteBlit(tic_mem* tic)
|
||||
{
|
||||
static u32 pal[TIC_PALETTE_SIZE] = {0};
|
||||
|
||||
const u8* src = tic->ram.vram.palette.data;
|
||||
|
||||
memset(pal, 0xff, sizeof pal);
|
||||
|
||||
u8* dst = (u8*)pal;
|
||||
const u8* end = src + sizeof(tic_palette);
|
||||
|
||||
enum{RGB = sizeof(tic_rgb)};
|
||||
|
||||
for(; src != end; dst++, src+=RGB)
|
||||
for(s32 j = 0; j < RGB; j++)
|
||||
*dst++ = *(src+(RGB-1)-j);
|
||||
|
||||
return pal;
|
||||
}
|
||||
|
||||
static void api_blit(tic_mem* tic, u32* out, tic_scanline scanline)
|
||||
{
|
||||
const u32* pal = paletteBlit(tic);
|
||||
|
||||
if(scanline)
|
||||
{
|
||||
scanline(tic, 0);
|
||||
pal = paletteBlit(tic);
|
||||
}
|
||||
|
||||
enum {Top = (TIC80_FULLHEIGHT-TIC80_HEIGHT)/2, Bottom = Top};
|
||||
enum {Left = (TIC80_FULLWIDTH-TIC80_WIDTH)/2, Right = Left};
|
||||
|
||||
memset4(&out[0 * TIC80_FULLWIDTH], pal[tic->ram.vram.vars.border], TIC80_FULLWIDTH*Top);
|
||||
|
||||
u32* rowPtr = out + Top*TIC80_FULLWIDTH;
|
||||
|
||||
for(s32 r = 0, y = tic->ram.vram.vars.offset.y; r < TIC80_HEIGHT; r++, y++, rowPtr += TIC80_FULLWIDTH)
|
||||
{
|
||||
memset4(rowPtr, pal[tic->ram.vram.vars.border], Left);
|
||||
memset4(rowPtr + Left, pal[tic->ram.vram.vars.bg], TIC80_WIDTH);
|
||||
|
||||
u32* colPtr = rowPtr + Left;
|
||||
|
||||
if(y >= 0 && y < TIC80_HEIGHT)
|
||||
for(s32 c = 0, x = tic->ram.vram.vars.offset.x, index = y * TIC80_WIDTH + x; c < TIC80_WIDTH; c++, colPtr++, x++, index++)
|
||||
if(x >= 0 && x < TIC80_WIDTH)
|
||||
*colPtr = pal[tic_tool_peek4(tic->ram.vram.screen.data, index)];
|
||||
|
||||
memset4(rowPtr + (TIC80_FULLWIDTH-Right), pal[tic->ram.vram.vars.border], Right);
|
||||
|
||||
if(scanline && (r < TIC80_HEIGHT-1))
|
||||
{
|
||||
scanline(tic, r+1);
|
||||
pal = paletteBlit(tic);
|
||||
}
|
||||
}
|
||||
|
||||
memset4(&out[(TIC80_FULLHEIGHT-Bottom) * TIC80_FULLWIDTH], pal[tic->ram.vram.vars.border], TIC80_FULLWIDTH*Bottom);
|
||||
}
|
||||
|
||||
static void initApi(tic_api* api)
|
||||
{
|
||||
#define INIT_API(func) api->func = api_##func
|
||||
@@ -1681,6 +1713,7 @@ static void initApi(tic_api* api)
|
||||
INIT_API(save);
|
||||
INIT_API(tick_start);
|
||||
INIT_API(tick_end);
|
||||
INIT_API(blit);
|
||||
|
||||
#undef INIT_API
|
||||
}
|
||||
|
||||
23
src/tic.h
23
src/tic.h
@@ -27,17 +27,23 @@
|
||||
#include "defines.h"
|
||||
|
||||
#define TIC_VERSION_MAJOR 0
|
||||
#define TIC_VERSION_MINOR 47
|
||||
#define TIC_VERSION_MINOR 50
|
||||
#define TIC_VERSION_PATCH 0
|
||||
#define TIC_VERSION_STATUS ""
|
||||
|
||||
#if defined(TIC80_PRO)
|
||||
#define TIC_VERSION_POST " Pro"
|
||||
#else
|
||||
#define TIC_VERSION_POST ""
|
||||
#endif
|
||||
|
||||
#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_VERSION_LABEL DEF2STR(TIC_VERSION_MAJOR) "." DEF2STR(TIC_VERSION_MINOR) "." DEF2STR(TIC_VERSION_PATCH) TIC_VERSION_STATUS TIC_VERSION_POST
|
||||
#define TIC_PACKAGE "com.nesbox.tic"
|
||||
#define TIC_NAME "TIC-80"
|
||||
#define TIC_NAME_FULL TIC_NAME " tiny computer"
|
||||
@@ -302,8 +308,8 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data [TIC80_WIDTH * TIC80_HEIGHT * sizeof(u32)];
|
||||
s32 size;
|
||||
u8 data [TIC80_WIDTH * TIC80_HEIGHT * sizeof(u32)];
|
||||
} tic_cover_image;
|
||||
|
||||
typedef struct
|
||||
@@ -355,7 +361,16 @@ typedef union
|
||||
|
||||
struct
|
||||
{
|
||||
u8 border;
|
||||
union
|
||||
{
|
||||
u8 colors;
|
||||
|
||||
struct
|
||||
{
|
||||
u8 border:TIC_PALETTE_BPP;
|
||||
u8 bg:TIC_PALETTE_BPP;
|
||||
};
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
|
||||
48
src/tic80.c
48
src/tic80.c
@@ -111,52 +111,6 @@ TIC80_API void tic80_load(tic80* tic, void* cart, s32 size)
|
||||
}
|
||||
}
|
||||
|
||||
static u32* srcPaletteBlit(const u8* src)
|
||||
{
|
||||
static u32 pal[TIC_PALETTE_SIZE] = { 0 };
|
||||
|
||||
memset(pal, 0xff, sizeof pal);
|
||||
|
||||
u8* dst = (u8*)pal;
|
||||
const u8* end = src + sizeof(tic_palette);
|
||||
|
||||
enum { RGB = sizeof(tic_rgb) };
|
||||
|
||||
for (; src != end; dst++, src += RGB)
|
||||
for (s32 j = 0; j < RGB; j++)
|
||||
*dst++ = *(src + (RGB - 1) - j);
|
||||
|
||||
return pal;
|
||||
}
|
||||
|
||||
static u32* paletteBlit(tic_mem* memory)
|
||||
{
|
||||
return srcPaletteBlit(memory->ram.vram.palette.data);
|
||||
}
|
||||
|
||||
static void blit(tic80* tic)
|
||||
{
|
||||
tic80_local* tic80 = (tic80_local*)tic;
|
||||
|
||||
u32* screen = tic->screen;
|
||||
u32* border = tic->border;
|
||||
|
||||
tic->offset.x = tic80->memory->ram.vram.vars.offset.x;
|
||||
tic->offset.y = tic80->memory->ram.vram.vars.offset.y;
|
||||
|
||||
for (s32 r = 0, pos = 0; r < TIC80_HEIGHT; r++, screen += TIC80_WIDTH)
|
||||
{
|
||||
tic80->memory->api.scanline(tic80->memory, r);
|
||||
const u32* pal = paletteBlit(tic80->memory);
|
||||
|
||||
tic->offset.rows[r] = tic80->memory->ram.vram.vars.offset.x;
|
||||
|
||||
*border++ = pal[tic_tool_peek4(tic80->memory->ram.vram.mapping, tic80->memory->ram.vram.vars.border & 0xf)];
|
||||
for (u32* ptr = screen, c = 0; c < TIC80_WIDTH; c++, ptr++)
|
||||
*ptr = pal[tic_tool_peek4(tic80->memory->ram.vram.screen.data, pos++)];
|
||||
}
|
||||
}
|
||||
|
||||
TIC80_API void tic80_tick(tic80* tic, tic80_input input)
|
||||
{
|
||||
tic80_local* tic80 = (tic80_local*)tic;
|
||||
@@ -167,7 +121,7 @@ TIC80_API void tic80_tick(tic80* tic, tic80_input input)
|
||||
tic80->memory->api.tick(tic80->memory, &tic80->tickData);
|
||||
tic80->memory->api.tick_end(tic80->memory);
|
||||
|
||||
blit(tic);
|
||||
tic80->memory->api.blit(tic80->memory, tic->screen, tic80->memory->api.scanline);
|
||||
|
||||
TickCounter++;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ typedef struct
|
||||
} tic_tick_data;
|
||||
|
||||
typedef struct tic_mem tic_mem;
|
||||
typedef void(*tic_scanline)(tic_mem* memory, s32 row);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -82,7 +83,7 @@ typedef struct
|
||||
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 (*textri) (tic_mem* memory, s32 x1, s32 y1, s32 x2, s32 y2, s32 x3, s32 y3, s32 u1, s32 v1, s32 u2, s32 v2, s32 u3, s32 v3 ,bool use_map,u8 chroma);
|
||||
void(*textri) (tic_mem* memory, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, bool use_map, u8 chroma);
|
||||
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);
|
||||
@@ -104,6 +105,7 @@ typedef struct
|
||||
|
||||
void (*tick_start) (tic_mem* memory, const tic_sound* src);
|
||||
void (*tick_end) (tic_mem* memory);
|
||||
void (*blit) (tic_mem* tic, u32* out, tic_scanline scanline);
|
||||
|
||||
tic_script_lang (*get_script)(tic_mem* memory);
|
||||
} tic_api;
|
||||
|
||||
24
src/tools.c
24
src/tools.c
@@ -25,28 +25,8 @@
|
||||
|
||||
#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;
|
||||
}
|
||||
extern void tic_tool_poke4(void* addr, u32 index, u8 value);
|
||||
extern u8 tic_tool_peek4(const void* addr, u32 index);
|
||||
|
||||
s32 tic_tool_get_pattern_id(const tic_track* track, s32 frame, s32 channel)
|
||||
{
|
||||
|
||||
25
src/tools.h
25
src/tools.h
@@ -24,8 +24,29 @@
|
||||
|
||||
#include "tic.h"
|
||||
|
||||
void tic_tool_poke4(void* addr, u32 index, u8 value);
|
||||
u8 tic_tool_peek4(const void* addr, u32 index);
|
||||
inline 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;
|
||||
}
|
||||
}
|
||||
|
||||
inline u8 tic_tool_peek4(const void* addr, u32 index)
|
||||
{
|
||||
u8 val = ((u8*)addr)[index >> 1];
|
||||
|
||||
return index & 1 ? val >> 4 : val & 0xf;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user