Skip to content

Commit c35ca88

Browse files
committed
merge with website
1 parent 7f41c76 commit c35ca88

27 files changed

+3960
-39
lines changed

.github/workflows/build.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,8 @@ jobs:
438438
with:
439439
ruby-version: 2.6
440440

441+
- uses: actions/setup-node@v5
442+
441443
- name: Build all
442444
run: |
443445
cd build
@@ -452,6 +454,35 @@ jobs:
452454
path: |
453455
build/bin/*
454456
457+
- name: Build website
458+
run: |
459+
mkdir -p website/public/app
460+
cp build/bin/* website/public/app
461+
cd website
462+
npm install
463+
npm run build
464+
465+
- name: Upload pages artifact
466+
uses: actions/upload-pages-artifact@v3
467+
with:
468+
path: website/dist
469+
470+
# === GitHub Pages ===
471+
pages:
472+
runs-on: ubuntu-latest
473+
needs: webapp
474+
475+
# !TODO: change it main
476+
if: github.ref == 'refs/heads/website'
477+
478+
permissions:
479+
pages: write
480+
id-token: write
481+
482+
steps:
483+
- name: Deploy to GitHub Pages
484+
uses: actions/deploy-pages@v4
485+
455486
# === Android ===
456487
android:
457488
runs-on: ubuntu-latest

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ option(BUILD_STATIC "Static runtime" ${BUILD_STATIC_DEFAULT})
5151
option(BUILD_WITH_ALL "Build all supported scripts" ${BUILD_WITH_ALL_DEFAULT})
5252
option(BUILD_SDL "SDL Enabled" OFF)
5353
option(BUILD_SOKOL "Sokol Enabled" ${BUILD_SOKOL_DEFAULT})
54+
option(BUILD_SOKOL_WEBGPU "Sokol WebGPU Enabled" OFF)
5455
option(BUILD_LIBRETRO "libretro Enabled" ${BUILD_LIBRETRO_DEFAULT})
5556
option(BUILD_TOOLS "prj2cart" OFF)
5657
option(BUILD_EDITORS "Build cart editors" ON)
@@ -81,6 +82,7 @@ message("BUILD_SDL: ${BUILD_SDL}")
8182
message("BUILD_WITH_ALL: ${BUILD_WITH_ALL}")
8283
message("BUILD_PLAYER: ${BUILD_PLAYER}")
8384
message("BUILD_IPO: ${BUILD_IPO}")
85+
message("BUILD_SOKOL_WEBGPU: ${BUILD_SOKOL_WEBGPU}")
8486

8587
if(UNIX AND NOT APPLE AND NOT EMSCRIPTEN AND NOT ANDROID AND NOT NINTENDO_3DS)
8688
set(LINUX TRUE)

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -337,20 +337,20 @@ run the following commands in the Terminal
337337
```
338338
brew install git cmake
339339
git clone --recursive https://github.com/nesbox/TIC-80 && cd TIC-80/build
340-
cmake -DBUILD_WITH_ALL=On ..
340+
cmake -DBUILD_WITH_ALL=On -DCMAKE_POLICY_VERSION_MINIMUM=3.5 ..
341341
make -j4
342342
```
343343

344344
to create application icon for development version
345345
```
346-
mkdir -p ~/Applications/TIC80dev.app/Contents/{MacOS,Resources}
347-
cp -f macosx/tic80.plist ~/Applications/TIC80dev.app/Contents/Info.plist
348-
cp -f macosx/tic80.icns ~/Applications/TIC80dev.app/Contents/Resources
349-
cat > ~/Applications/TIC80dev.app/Contents/MacOS/tic80 <<EOF
346+
mkdir -p ~/Applications/tic80dev.app/Contents/{MacOS,Resources}
347+
cp -f macosx/tic80.plist ~/Applications/tic80dev.app/Contents/Info.plist
348+
cp -f macosx/tic80.icns ~/Applications/tic80dev.app/Contents/Resources
349+
cat > ~/Applications/tic80dev.app/Contents/MacOS/tic80 <<EOF
350350
#!/bin/sh
351-
exec /Users/nesbox/projects/TIC-80/build/bin/tic80 --skip --scale 2 >/dev/null
351+
exec /Users/nesbox/projects/TIC-80/build/bin/tic80 --skip >/dev/null
352352
EOF
353-
chmod +x ~/Applications/TIC80dev.app/Contents/MacOS/TIC80dev
353+
chmod +x ~/Applications/tic80dev.app/Contents/MacOS/tic80
354354
```
355355
Make sure to update the absolute path to the tic80 binary in the script, or
356356
update the launch arguments.

build/webapp/favicon.ico

31.3 KB
Binary file not shown.

cmake/sokol.cmake

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@ if(BUILD_SOKOL)
1515
elseif(WIN32)
1616
target_compile_definitions(sokol PUBLIC SOKOL_D3D11 UNICODE)
1717
elseif(EMSCRIPTEN)
18-
target_compile_definitions(sokol PUBLIC SOKOL_WGPU)
19-
target_compile_options(sokol PRIVATE --use-port=emdawnwebgpu)
18+
19+
if(BUILD_SOKOL_WEBGPU)
20+
target_compile_definitions(sokol PUBLIC SOKOL_WGPU)
21+
target_compile_options(sokol PRIVATE --use-port=emdawnwebgpu)
22+
else()
23+
target_compile_definitions(sokol PUBLIC SOKOL_GLES3)
24+
endif()
2025
endif()
2126

2227
if(APPLE)
@@ -90,7 +95,14 @@ if(BUILD_SOKOL)
9095
set_target_properties(tic80 PROPERTIES PREFIX "")
9196
elseif(EMSCRIPTEN)
9297
add_executable(tic80 ${TIC80_SRC})
93-
set_target_properties(tic80 PROPERTIES LINK_FLAGS --use-port=emdawnwebgpu -s ALLOW_MEMORY_GROWTH=1 -s FETCH=1 -s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['$dynCall','$writeArrayToMemory123'] -s EXPORTED_RUNTIME_METHODS=['FS'] -s EXPORT_ES6=1 -s MODULARIZE=1)
98+
99+
set(LINK_FLAGS "-s EXPORT_ES6=1 -s ALLOW_MEMORY_GROWTH=1 -s FETCH=1 -s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['$dynCall','$writeArrayToMemory'] -s EXPORTED_RUNTIME_METHODS=['FS'] -s EXPORTED_FUNCTIONS=['_main','_free']")
100+
101+
if(BUILD_SOKOL_WEBGPU)
102+
set_target_properties(tic80 PROPERTIES LINK_FLAGS "${LINK_FLAGS} --use-port=emdawnwebgpu")
103+
else()
104+
set_target_properties(tic80 PROPERTIES LINK_FLAGS "${LINK_FLAGS} -s USE_WEBGL2=1")
105+
endif()
94106
else()
95107
add_executable(tic80 ${TIC80_SRC})
96108
endif()

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/studio/studio.c

Lines changed: 82 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424

2525
#if defined(BUILD_EDITORS)
2626

27+
#if defined(_WIN32)
28+
#include <windows.h>
29+
#else
30+
#include <sys/time.h>
31+
#endif
32+
2733
#include "editors/code.h"
2834
#include "editors/sprite.h"
2935
#include "editors/map.h"
@@ -67,6 +73,9 @@
6773

6874
#define MD5_HASHSIZE 16
6975

76+
// interval between the Windows and Unix epoch
77+
#define UNIX_EPOCH_IN_FILETIME 116444736000000000ULL
78+
7079
#if defined(TIC80_PRO)
7180
#define TIC_EDITOR_BANKS (TIC_BANKS)
7281
#else
@@ -235,13 +244,6 @@ struct Studio
235244
void *userdata;
236245
};
237246

238-
#if defined(BUILD_EDITORS)
239-
240-
static const char VideoGif[] = "video%i.gif";
241-
static const char ScreenGif[] = "screen%i.gif";
242-
243-
#endif
244-
245247
static void emptyDone(void* data) {}
246248

247249
void fadePalette(tic_palette* pal, s32 value)
@@ -1674,18 +1676,81 @@ static void setCoverImage(Studio* studio)
16741676
}
16751677
}
16761678

1677-
static void stopVideoRecord(Studio* studio, const char* name)
1679+
static void generateScreenshotName(Studio* studio, const char* extension, char filenameOut[TICNAME_MAX])
1680+
{
1681+
const char* romName = studio->console->rom.name;
1682+
1683+
// --- Strip extension ---
1684+
const char* dot = strrchr(romName, '.');
1685+
size_t baseLen = dot ? (size_t)(dot - romName) : strlen(romName);
1686+
1687+
// --- Get current time with millisecond precision ---
1688+
time_t sec = 0;
1689+
long msec = 0;
1690+
1691+
#if defined(_WIN32)
1692+
// Windows: Use GetSystemTimeAsFileTime for high precision.
1693+
// It provides time in 100-nanosecond intervals since Jan 1, 1601.
1694+
FILETIME ft;
1695+
GetSystemTimeAsFileTime(&ft);
1696+
1697+
ULARGE_INTEGER uli;
1698+
uli.LowPart = ft.dwLowDateTime;
1699+
uli.HighPart = ft.dwHighDateTime;
1700+
1701+
// Convert FILETIME to Unix seconds and milliseconds
1702+
sec = (time_t)((uli.QuadPart - UNIX_EPOCH_IN_FILETIME) / 10000000ULL);
1703+
msec = (long)((uli.QuadPart / 10000) % 1000);
1704+
#else
1705+
// Other systems (Linux, macOS, 3DS, etc.): Use gettimeofday, which is widely available.
1706+
struct timeval tv;
1707+
gettimeofday(&tv, NULL);
1708+
sec = tv.tv_sec;
1709+
msec = (long)(tv.tv_usec / 1000);
1710+
#endif
1711+
1712+
// --- Convert seconds to a local time structure (thread-safe) ---
1713+
struct tm tm_local;
1714+
1715+
// Thread-safe localtime
1716+
#if defined(_WIN32)
1717+
// Windows secure version
1718+
localtime_s(&tm_local, &sec);
1719+
#else
1720+
// POSIX re-entrant version
1721+
localtime_r(&sec, &tm_local);
1722+
#endif
1723+
1724+
// --- Format the timestamp string ---
1725+
char timestamp[32] = {0};
1726+
1727+
// Format as -yymmdd-hhmmss
1728+
strftime(timestamp, sizeof(timestamp), "-%y%m%d-%H%M%S", &tm_local);
1729+
1730+
// Append milliseconds
1731+
size_t len = strlen(timestamp);
1732+
snprintf(timestamp + len, sizeof(timestamp) - len, "-%03ld", msec);
1733+
1734+
// --- Adjust baseLen to prevent overflow ---
1735+
size_t maxBaseLen = TICNAME_MAX
1736+
- strlen(timestamp)
1737+
- strlen(extension)
1738+
- 1; // for null terminator
1739+
1740+
if (baseLen > maxBaseLen)
1741+
baseLen = maxBaseLen;
1742+
1743+
// --- Build the final filename string ---
1744+
snprintf(filenameOut, TICNAME_MAX, "%.*s%s%s",
1745+
(int)baseLen, romName, timestamp, extension);
1746+
}
1747+
1748+
static void stopVideoRecord(Studio* studio)
16781749
{
16791750
MsfGifResult result = msf_gif_end(&studio->video.gif);
16801751

1681-
// Find an available filename to save.
1682-
s32 i = 0;
16831752
char filename[TICNAME_MAX];
1684-
do
1685-
{
1686-
snprintf(filename, sizeof filename, name, ++i);
1687-
}
1688-
while(tic_fs_exists(studio->fs, filename));
1753+
generateScreenshotName(studio, ".gif", filename);
16891754

16901755
// Now that it has found an available filename, save it.
16911756
if(tic_fs_save(studio->fs, filename, result.data, result.dataSize, true))
@@ -1707,7 +1772,7 @@ static void startVideoRecord(Studio* studio)
17071772
{
17081773
if(studio->video.record)
17091774
{
1710-
stopVideoRecord(studio, VideoGif);
1775+
stopVideoRecord(studio);
17111776
}
17121777
else
17131778
{
@@ -2044,7 +2109,7 @@ static void recordFrame(Studio* studio, u32* pixels)
20442109
if(studio->video.screenshot)
20452110
{
20462111
studio->video.screenshot = false;
2047-
stopVideoRecord(studio, ScreenGif);
2112+
stopVideoRecord(studio);
20482113
return;
20492114
}
20502115

src/system/libretro/tic80_libretro.c

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,49 @@ void tic80_libretro_update_mouse(tic80_mouse* mouse)
765765
mouse->y = state->mouseY + TIC80_OFFSET_TOP;
766766
}
767767

768+
/**
769+
* Gets the 32-bit color value from the TIC-80 palette.
770+
*/
771+
static u32 get_screen_color(tic_mem* tic, u8 index)
772+
{
773+
tic_rgb color = tic->ram->vram.palette.colors[index];
774+
// The core requests RETRO_PIXEL_FORMAT_XRGB8888, so we format the color as 0x00RRGGBB.
775+
return (color.r << 16) | (color.g << 8) | (color.b);
776+
}
777+
778+
/**
779+
* Draws a single pixel directly to the final screen buffer.
780+
*/
781+
static void draw_pixel_on_screen(u32* screen, s32 x, s32 y, u32 color)
782+
{
783+
// Bounds check against the visible screen area
784+
if (x < 0 || x >= TIC80_WIDTH || y < 0 || y >= TIC80_HEIGHT)
785+
return;
786+
787+
s32 full_x = x + TIC80_OFFSET_LEFT;
788+
s32 full_y = y + TIC80_OFFSET_TOP;
789+
790+
screen[full_y * TIC80_FULLWIDTH + full_x] = color;
791+
}
792+
793+
/**
794+
* Draws a horizontal line directly to the final screen buffer.
795+
*/
796+
static void draw_hline_on_screen(u32* screen, s32 x1, s32 x2, s32 y, u32 color)
797+
{
798+
for (s32 x = x1; x <= x2; x++)
799+
draw_pixel_on_screen(screen, x, y, color);
800+
}
801+
802+
/**
803+
* Draws a vertical line directly to the final screen buffer.
804+
*/
805+
static void draw_vline_on_screen(u32* screen, s32 x, s32 y1, s32 y2, u32 color)
806+
{
807+
for (s32 y = y1; y <= y2; y++)
808+
draw_pixel_on_screen(screen, x, y, color);
809+
}
810+
768811
/**
769812
* Draws a software cursor on the screen where the mouse is.
770813
*/
@@ -777,26 +820,45 @@ void tic80_libretro_mousecursor(tic80* game, tic80_mouse* mouse, enum mouse_curs
777820
return;
778821
}
779822

780-
tic_mem* tic = (tic_mem*)state->tic;
823+
tic_mem* tic = (tic_mem*)game;
824+
u32* screen = game->screen;
825+
s32 mx = state->mouseX;
826+
s32 my = state->mouseY;
827+
828+
// Calculate the final 32-bit color value
829+
u32 cursor_color = get_screen_color(tic, state->mouseCursorColor);
781830

782-
// Draw the cursor.
783-
// TODO: Fix the cursor not being drawn on the screen by possibly modifing game->screen directly.
831+
// Draw the cursor directly to the screen buffer.
784832
switch (cursortype) {
785833
case MOUSE_CURSOR_NONE:
786834
// Nothing.
787835
break;
788836
case MOUSE_CURSOR_DOT:
789-
tic_api_pix(tic, state->mouseX, state->mouseY, state->mouseCursorColor, false);
837+
draw_pixel_on_screen(screen, mx, my, cursor_color);
790838
break;
791839
case MOUSE_CURSOR_CROSS:
792-
tic_api_line(tic, state->mouseX - 4, state->mouseY, state->mouseX - 2, state->mouseY, state->mouseCursorColor);
793-
tic_api_line(tic, state->mouseX + 2, state->mouseY, state->mouseX + 4, state->mouseY, state->mouseCursorColor);
794-
tic_api_line(tic, state->mouseX, state->mouseY - 4, state->mouseX, state->mouseY - 2, state->mouseCursorColor);
795-
tic_api_line(tic, state->mouseX, state->mouseY + 2, state->mouseX, state->mouseY + 4, state->mouseCursorColor);
840+
draw_hline_on_screen(screen, mx - 4, mx - 2, my, cursor_color);
841+
draw_hline_on_screen(screen, mx + 2, mx + 4, my, cursor_color);
842+
draw_vline_on_screen(screen, mx, my - 4, my - 2, cursor_color);
843+
draw_vline_on_screen(screen, mx, my + 2, my + 4, cursor_color);
796844
break;
797845
case MOUSE_CURSOR_ARROW:
798-
tic_api_tri(tic, state->mouseX, state->mouseY, state->mouseX + 3, state->mouseY, state->mouseX, state->mouseY + 3, state->mouseCursorColor);
799-
tic_api_line(tic, state->mouseX + 3, state->mouseY, state->mouseX, state->mouseY + 3, tic_color_black);
846+
{
847+
// Calculate black for the outline.
848+
u32 black_color = get_screen_color(tic, tic_color_black);
849+
850+
// Draw the filled triangle part of the arrow
851+
for (int y = 0; y <= 2; y++) {
852+
for (int x = 0; x <= 2 - y; x++) {
853+
draw_pixel_on_screen(screen, mx + x, my + y, cursor_color);
854+
}
855+
}
856+
// Draw the black outline (hypotenuse of the triangle)
857+
draw_pixel_on_screen(screen, mx + 3, my, black_color);
858+
draw_pixel_on_screen(screen, mx + 2, my + 1, black_color);
859+
draw_pixel_on_screen(screen, mx + 1, my + 2, black_color);
860+
draw_pixel_on_screen(screen, mx, my + 3, black_color);
861+
}
800862
break;
801863
}
802864
}
@@ -972,7 +1034,7 @@ void tic80_libretro_variables(bool startup)
9721034
state->mouseHideTimerStart = atoi(var.value);
9731035
if (state->mouseHideTimerStart > 0) {
9741036
state->mouseHideTimerStart = state->mouseHideTimerStart * TIC80_FRAMERATE;
975-
state->mouseHideTimer = state->mouseHideTimerStart;
1037+
state->mouseHideTimer = 0; // Cursor starts hidden
9761038
}
9771039
else {
9781040
state->mouseHideTimerStart = 0;

tic80.sublime-project

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
"name": "CMAKE_BUILD_TYPE=Release",
4141
"shell_cmd": "cmake -DCMAKE_BUILD_TYPE=Release .."
4242
},
43+
{
44+
"name": "SOKOL_WEBGPU=ON",
45+
"shell_cmd": "cmake -DBUILD_SOKOL_WEBGPU=ON .."
46+
},
4347
]
4448
}
4549
],

0 commit comments

Comments
 (0)