diff --git a/Makefile b/Makefile index 9983471..b7ef3d3 100644 --- a/Makefile +++ b/Makefile @@ -274,16 +274,20 @@ emscripten: mingw: $(DEMO_ASSETS) $(TIC80_DLL) $(TIC_O) bin/html.o bin/res.o $(CC) $(TIC_O) bin/html.o bin/res.o $(TIC80_A) $(OPT) $(INCLUDES) $(MINGW_LINKER_FLAGS) -o $(MINGW_OUTPUT) -run: mingw +mingw-pro: + $(eval OPT += -DTIC80_PRO) + make mingw OPT="$(OPT)" + +run: mingw-pro $(MINGW_OUTPUT) -linux64-flto: +linux64-lto: $(CC) $(LINUX_INCLUDES) $(SOURCES) $(TIC80_SRC) $(SOURCES_EXT) $(OPT) $(INCLUDES) $(LINUX64_LIBS) $(LINUX_LINKER_FLAGS) -flto -o bin/tic -linux32-flto: +linux32-lto: $(CC) $(LINUX_INCLUDES) $(SOURCES) $(TIC80_SRC) $(SOURCES_EXT) $(OPT) $(INCLUDES) $(LINUX32_LIBS) $(LINUX_LINKER_FLAGS) -flto -o bin/tic -arm-flto: +arm-lto: $(CC) $(OPT_ARM) $(SOURCES) $(TIC80_SRC) $(OPT) $(INCLUDES) $(LINUX_ARM_LIBS) $(LINUX_LINKER_FLAGS) -flto -o bin/tic linux: @@ -339,4 +343,4 @@ bin/assets/moondemo.tic.dat: demos/moondemo.tic $(BIN2TXT) $< $@ -z clean: $(TIC_O) $(TIC80_O) - $(RM) $(TIC_O) $(TIC80_O) + del bin\*.o diff --git a/src/console.c b/src/console.c index fd872e6..fe42a1c 100644 --- a/src/console.c +++ b/src/console.c @@ -68,21 +68,26 @@ 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, CartExt); +} + static void scrollBuffer(char* buffer) { memmove(buffer, buffer + CONSOLE_BUFFER_WIDTH, CONSOLE_BUFFER_SIZE - CONSOLE_BUFFER_WIDTH); @@ -343,7 +348,7 @@ static bool onConsoleLoadSectionCommand(Console* console, const char* param) if(pos) { pos[sizeof(CartExt) - 1] = 0; - const char* name = getRomName(param); + const char* name = getCartName(param); s32 size = 0; void* data = fsLoadFile(console->fs, name, &size); @@ -475,7 +480,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 +495,20 @@ 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 void onConsoleLoadCommandConfirmed(Console* console, const char* param) { if(onConsoleLoadSectionCommand(console, param)) return; @@ -497,7 +516,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 +526,16 @@ 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 + { + printBack(console, "\ncart loading error"); + } } else printBack(console, "\ncart name is missing"); @@ -676,6 +690,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 +698,7 @@ static bool printFilename(const char* name, const char* info, s32 id, void* data if(dir) { + printBack(console, "["); printBack(console, name); printBack(console, "]"); @@ -1546,6 +1562,345 @@ static void onConsoleExportCommand(Console* console, const char* param) #endif +#if defined(TIC80_PRO) + +static const char* getProjectName(const char* name) +{ + return getName(name, ".ticp"); +} + +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* tag, const char* data) +{ + if(strlen(data) == 0) + return ptr; + + sprintf(ptr, "-- <%s>\n%s\n-- \n", tag, data, tag); + ptr += strlen(ptr); + + return ptr; +} + +static char* saveBinaryBuffer(char* ptr, const void* data, s32 size, s32 row, bool flip) +{ + if(bufferEmpty(data, size)) + return ptr; + + sprintf(ptr, "-- %03i:", 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* tag, s32 count, const void* data, s32 size, bool flip) +{ + if(bufferEmpty(data, size * count)) + return ptr; + + sprintf(ptr, "\n-- <%s>\n", tag); + ptr += strlen(ptr); + + for(s32 i = 0; i < count; i++, data = (u8*)data + size) + ptr = saveBinaryBuffer(ptr, data, size, i, flip); + + sprintf(ptr, "-- \n", 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 CartSaveResult saveProject(Console* console, const char* name) +{ + tic_mem* tic = console->tic; + + bool success = false; + + if(name && strlen(name)) + { + char* stream = (char*)SDL_malloc(sizeof(tic_cartridge) * 3); + + if(stream) + { + char* ptr = saveTextSection(stream, "CODE", tic->cart.code.data); + + for(s32 i = 0; i < COUNT_OF(BinarySections); i++) + { + const BinarySection* section = &BinarySections[i]; + ptr = saveBinarySection(ptr, section->tag, section->count, (u8*)&tic->cart + section->offset, section->size, section->flip); + } + + saveBinarySection(ptr, "COVER", 1, &tic->cart.cover, tic->cart.cover.size + sizeof(s32), true); + + name = getProjectName(name); + + s32 size = strlen(stream); + if(size && fsSaveFile(console->fs, name, stream, size, true)) + { + strcpy(console->romName, name); + success = true; + studioRomSaved(); + } + + SDL_free(stream); + } + } + else if (strlen(console->romName)) + { + return saveProject(console, console->romName); + } + else return CART_SAVE_MISSING_NAME; + + return success ? CART_SAVE_OK : CART_SAVE_ERROR; +} + +static void onConsoleSaveProjectCommandConfirmed(Console* console, const char* param) +{ + CartSaveResult rom = saveProject(console, param); + + if(rom == CART_SAVE_OK) + { + printBack(console, "\nproject "); + printFront(console, console->romName); + printBack(console, " saved!\n"); + } + else if(rom == CART_SAVE_MISSING_NAME) + printBack(console, "\nproject name is missing\n"); + else + printBack(console, "\nproject saving error"); + + commandDone(console); +} + +static void onConsoleSaveProjectCommand(Console* console, const char* param) +{ + if(param && strlen(param) && fsExistsFile(console->fs, getProjectName(param))) + { + static const char* Rows[] = + { + "THE PROJECT", + "ALREADY EXISTS", + "", + "DO YOU WANT TO", + "OVERWRITE IT?", + }; + + confirmCommand(console, Rows, COUNT_OF(Rows), param, onConsoleSaveProjectCommandConfirmed); + } + else + { + onConsoleSaveProjectCommandConfirmed(console, param); + } +} + +static void loadTextSection(const char* project, const char* tag, void* dst, s32 size) +{ + char tagbuf[64]; + sprintf(tagbuf, "-- <%s>\n", tag); + + const char* start = SDL_strstr(project, tagbuf); + + if(start) + { + start += strlen(tagbuf); + + if(start < project + strlen(project)) + { + sprintf(tagbuf, "\n-- ", tag); + const char* end = SDL_strstr(start, tagbuf); + + if(end > start) + SDL_memcpy(dst, start, SDL_min(size, end - start)); + } + } +} + +static void loadBinarySection(const char* project, const char* tag, s32 count, void* dst, s32 size, bool flip) +{ + char tagbuf[64]; + sprintf(tagbuf, "-- <%s>\n", tag); + + const char* start = SDL_strstr(project, tagbuf); + + if(start) + { + start += strlen(tagbuf); + + sprintf(tagbuf, "\n-- ", 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); + } + } + } +} + +static bool loadProject(Console* console, const char* data, s32 size) +{ + 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'; + + 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)); + + loadTextSection(project, "CODE", cart->code.data, sizeof(tic_code)); + + for(s32 i = 0; i < COUNT_OF(BinarySections); i++) + { + const BinarySection* section = &BinarySections[i]; + loadBinarySection(project, section->tag, section->count, (u8*)cart + section->offset, section->size, section->flip); + } + + loadBinarySection(project, "COVER", 1, &cart->cover, -1, true); + + SDL_memcpy(&tic->cart, cart, sizeof(tic_cartridge)); + + SDL_free(cart); + + done = true; + } + + SDL_free(project); + } + + return done; +} + +static void onConsoleLoadProjectCommandConfirmed(Console* console, const char* param) +{ + if(param) + { + s32 size = 0; + const char* name = getProjectName(param); + + void* data = fsLoadFile(console->fs, name, &size); + + if(data && loadProject(console, data, size)) + { + strcpy(console->romName, name); + + studioRomLoaded(); + + printBack(console, "\nproject "); + printFront(console, console->romName); + printBack(console, " loaded!\nuse "); + printFront(console, "RUN"); + printBack(console, " command to run it\n"); + + SDL_free(data); + } + else + { + printBack(console, "\nproject loading error"); + } + } + else printBack(console, "\nproject name is missing"); + + commandDone(console); +} + +static void onConsoleLoadProjectCommand(Console* console, const char* param) +{ + if(studioCartChanged()) + { + static const char* Rows[] = + { + "YOU HAVE", + "UNSAVED CHANGES", + "", + "DO YOU REALLY WANT", + "TO LOAD PROJECT?", + }; + + confirmCommand(console, Rows, COUNT_OF(Rows), param, onConsoleLoadProjectCommandConfirmed); + } + else + { + onConsoleLoadProjectCommandConfirmed(console, param); + } +} + +#endif + static CartSaveResult saveCartName(Console* console, const char* name) { tic_mem* tic = console->tic; @@ -1569,7 +1924,7 @@ static CartSaveResult saveCartName(Console* console, const char* name) { s32 size = tic->api.save(&tic->cart, buffer); - name = getRomName(name); + name = getCartName(name); if(size && fsSaveFile(console->fs, name, buffer, size, true)) { @@ -1617,7 +1972,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[] = { @@ -1799,32 +2154,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.colors, "BORDER/BG 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++) @@ -1853,6 +2204,10 @@ static const struct {"new", NULL, "create new cart", onConsoleNewCommand}, {"load", NULL, "load cart", onConsoleLoadCommand}, {"save", NULL, "save cart", onConsoleSaveCommand}, +#if defined(TIC80_PRO) + {"loadp", NULL, "load project", onConsoleLoadProjectCommand}, + {"savep", NULL, "save project", onConsoleSaveProjectCommand}, +#endif {"run", NULL, "run loaded cart", onConsoleRunCommand}, {"resume", NULL, "resume run cart", onConsoleResumeCommand}, {"dir", "ls", "show list of files", onConsoleDirCommand}, diff --git a/src/map.c b/src/map.c index 3efc8e0..4c4538d 100644 --- a/src/map.c +++ b/src/map.c @@ -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) { diff --git a/src/music.c b/src/music.c index 2cf74e5..5ca4ba8 100644 --- a/src/music.c +++ b/src/music.c @@ -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}; diff --git a/src/studio.c b/src/studio.c index 3a0f8ac..4824673 100644 --- a/src/studio.c +++ b/src/studio.c @@ -342,13 +342,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) @@ -378,7 +376,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); diff --git a/src/studio.h b/src/studio.h index 17eb748..2163eec 100644 --- a/src/studio.h +++ b/src/studio.h @@ -146,7 +146,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); diff --git a/src/tic.c b/src/tic.c index 6b565b8..99c1a11 100644 --- a/src/tic.c +++ b/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 diff --git a/src/tic.h b/src/tic.h index afce57f..eb80ae2 100644 --- a/src/tic.h +++ b/src/tic.h @@ -31,13 +31,19 @@ #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