Merge remote-tracking branch 'origin/main' into website

This commit is contained in:
Vadim Grigoruk
2025-09-08 18:59:45 +02:00
5 changed files with 175 additions and 36 deletions
+7 -7
View File
@@ -337,20 +337,20 @@ run the following commands in the Terminal
```
brew install git cmake
git clone --recursive https://github.com/nesbox/TIC-80 && cd TIC-80/build
cmake -DBUILD_WITH_ALL=On ..
cmake -DBUILD_WITH_ALL=On -DCMAKE_POLICY_VERSION_MINIMUM=3.5 ..
make -j4
```
to create application icon for development version
```
mkdir -p ~/Applications/TIC80dev.app/Contents/{MacOS,Resources}
cp -f macosx/tic80.plist ~/Applications/TIC80dev.app/Contents/Info.plist
cp -f macosx/tic80.icns ~/Applications/TIC80dev.app/Contents/Resources
cat > ~/Applications/TIC80dev.app/Contents/MacOS/tic80 <<EOF
mkdir -p ~/Applications/tic80dev.app/Contents/{MacOS,Resources}
cp -f macosx/tic80.plist ~/Applications/tic80dev.app/Contents/Info.plist
cp -f macosx/tic80.icns ~/Applications/tic80dev.app/Contents/Resources
cat > ~/Applications/tic80dev.app/Contents/MacOS/tic80 <<EOF
#!/bin/sh
exec /Users/nesbox/projects/TIC-80/build/bin/tic80 --skip --scale 2 >/dev/null
exec /Users/nesbox/projects/TIC-80/build/bin/tic80 --skip >/dev/null
EOF
chmod +x ~/Applications/TIC80dev.app/Contents/MacOS/TIC80dev
chmod +x ~/Applications/tic80dev.app/Contents/MacOS/tic80
```
Make sure to update the absolute path to the tic80 binary in the script, or
update the launch arguments.
+12
View File
@@ -30,6 +30,18 @@ if(BUILD_SDL AND NOT EMSCRIPTEN AND NOT RPI AND NOT PREFER_SYSTEM_LIBRARIES)
add_subdirectory(${THIRDPARTY_DIR}/sdl2)
if(MSVC)
# CMake policy CMP0079
# This allows linking libraries to targets not built in the current directory.
cmake_policy(SET CMP0079 NEW)
target_link_libraries(SDL2 PRIVATE
libcmt.lib
libvcruntime.lib
libucrt.lib
)
endif()
endif()
################################
+82 -17
View File
@@ -24,6 +24,12 @@
#if defined(BUILD_EDITORS)
#if defined(_WIN32)
#include <windows.h>
#else
#include <sys/time.h>
#endif
#include "editors/code.h"
#include "editors/sprite.h"
#include "editors/map.h"
@@ -67,6 +73,9 @@
#define MD5_HASHSIZE 16
// interval between the Windows and Unix epoch
#define UNIX_EPOCH_IN_FILETIME 116444736000000000ULL
#if defined(TIC80_PRO)
#define TIC_EDITOR_BANKS (TIC_BANKS)
#else
@@ -235,13 +244,6 @@ struct Studio
void *userdata;
};
#if defined(BUILD_EDITORS)
static const char VideoGif[] = "video%i.gif";
static const char ScreenGif[] = "screen%i.gif";
#endif
static void emptyDone(void* data) {}
void fadePalette(tic_palette* pal, s32 value)
@@ -1674,18 +1676,81 @@ static void setCoverImage(Studio* studio)
}
}
static void stopVideoRecord(Studio* studio, const char* name)
static void generateScreenshotName(Studio* studio, const char* extension, char filenameOut[TICNAME_MAX])
{
const char* romName = studio->console->rom.name;
// --- Strip extension ---
const char* dot = strrchr(romName, '.');
size_t baseLen = dot ? (size_t)(dot - romName) : strlen(romName);
// --- Get current time with millisecond precision ---
time_t sec = 0;
long msec = 0;
#if defined(_WIN32)
// Windows: Use GetSystemTimeAsFileTime for high precision.
// It provides time in 100-nanosecond intervals since Jan 1, 1601.
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
ULARGE_INTEGER uli;
uli.LowPart = ft.dwLowDateTime;
uli.HighPart = ft.dwHighDateTime;
// Convert FILETIME to Unix seconds and milliseconds
sec = (time_t)((uli.QuadPart - UNIX_EPOCH_IN_FILETIME) / 10000000ULL);
msec = (long)((uli.QuadPart / 10000) % 1000);
#else
// Other systems (Linux, macOS, 3DS, etc.): Use gettimeofday, which is widely available.
struct timeval tv;
gettimeofday(&tv, NULL);
sec = tv.tv_sec;
msec = (long)(tv.tv_usec / 1000);
#endif
// --- Convert seconds to a local time structure (thread-safe) ---
struct tm tm_local;
// Thread-safe localtime
#if defined(_WIN32)
// Windows secure version
localtime_s(&tm_local, &sec);
#else
// POSIX re-entrant version
localtime_r(&sec, &tm_local);
#endif
// --- Format the timestamp string ---
char timestamp[32] = {0};
// Format as -yymmdd-hhmmss
strftime(timestamp, sizeof(timestamp), "-%y%m%d-%H%M%S", &tm_local);
// Append milliseconds
size_t len = strlen(timestamp);
snprintf(timestamp + len, sizeof(timestamp) - len, "-%03ld", msec);
// --- Adjust baseLen to prevent overflow ---
size_t maxBaseLen = TICNAME_MAX
- strlen(timestamp)
- strlen(extension)
- 1; // for null terminator
if (baseLen > maxBaseLen)
baseLen = maxBaseLen;
// --- Build the final filename string ---
snprintf(filenameOut, TICNAME_MAX, "%.*s%s%s",
(int)baseLen, romName, timestamp, extension);
}
static void stopVideoRecord(Studio* studio)
{
MsfGifResult result = msf_gif_end(&studio->video.gif);
// Find an available filename to save.
s32 i = 0;
char filename[TICNAME_MAX];
do
{
snprintf(filename, sizeof filename, name, ++i);
}
while(tic_fs_exists(studio->fs, filename));
generateScreenshotName(studio, ".gif", filename);
// Now that it has found an available filename, save it.
if(tic_fs_save(studio->fs, filename, result.data, result.dataSize, true))
@@ -1707,7 +1772,7 @@ static void startVideoRecord(Studio* studio)
{
if(studio->video.record)
{
stopVideoRecord(studio, VideoGif);
stopVideoRecord(studio);
}
else
{
@@ -2044,7 +2109,7 @@ static void recordFrame(Studio* studio, u32* pixels)
if(studio->video.screenshot)
{
studio->video.screenshot = false;
stopVideoRecord(studio, ScreenGif);
stopVideoRecord(studio);
return;
}
+73 -11
View File
@@ -765,6 +765,49 @@ void tic80_libretro_update_mouse(tic80_mouse* mouse)
mouse->y = state->mouseY + TIC80_OFFSET_TOP;
}
/**
* Gets the 32-bit color value from the TIC-80 palette.
*/
static u32 get_screen_color(tic_mem* tic, u8 index)
{
tic_rgb color = tic->ram->vram.palette.colors[index];
// The core requests RETRO_PIXEL_FORMAT_XRGB8888, so we format the color as 0x00RRGGBB.
return (color.r << 16) | (color.g << 8) | (color.b);
}
/**
* Draws a single pixel directly to the final screen buffer.
*/
static void draw_pixel_on_screen(u32* screen, s32 x, s32 y, u32 color)
{
// Bounds check against the visible screen area
if (x < 0 || x >= TIC80_WIDTH || y < 0 || y >= TIC80_HEIGHT)
return;
s32 full_x = x + TIC80_OFFSET_LEFT;
s32 full_y = y + TIC80_OFFSET_TOP;
screen[full_y * TIC80_FULLWIDTH + full_x] = color;
}
/**
* Draws a horizontal line directly to the final screen buffer.
*/
static void draw_hline_on_screen(u32* screen, s32 x1, s32 x2, s32 y, u32 color)
{
for (s32 x = x1; x <= x2; x++)
draw_pixel_on_screen(screen, x, y, color);
}
/**
* Draws a vertical line directly to the final screen buffer.
*/
static void draw_vline_on_screen(u32* screen, s32 x, s32 y1, s32 y2, u32 color)
{
for (s32 y = y1; y <= y2; y++)
draw_pixel_on_screen(screen, x, y, color);
}
/**
* Draws a software cursor on the screen where the mouse is.
*/
@@ -777,26 +820,45 @@ void tic80_libretro_mousecursor(tic80* game, tic80_mouse* mouse, enum mouse_curs
return;
}
tic_mem* tic = (tic_mem*)state->tic;
tic_mem* tic = (tic_mem*)game;
u32* screen = game->screen;
s32 mx = state->mouseX;
s32 my = state->mouseY;
// Draw the cursor.
// TODO: Fix the cursor not being drawn on the screen by possibly modifing game->screen directly.
// Calculate the final 32-bit color value
u32 cursor_color = get_screen_color(tic, state->mouseCursorColor);
// Draw the cursor directly to the screen buffer.
switch (cursortype) {
case MOUSE_CURSOR_NONE:
// Nothing.
break;
case MOUSE_CURSOR_DOT:
tic_api_pix(tic, state->mouseX, state->mouseY, state->mouseCursorColor, false);
draw_pixel_on_screen(screen, mx, my, cursor_color);
break;
case MOUSE_CURSOR_CROSS:
tic_api_line(tic, state->mouseX - 4, state->mouseY, state->mouseX - 2, state->mouseY, state->mouseCursorColor);
tic_api_line(tic, state->mouseX + 2, state->mouseY, state->mouseX + 4, state->mouseY, state->mouseCursorColor);
tic_api_line(tic, state->mouseX, state->mouseY - 4, state->mouseX, state->mouseY - 2, state->mouseCursorColor);
tic_api_line(tic, state->mouseX, state->mouseY + 2, state->mouseX, state->mouseY + 4, state->mouseCursorColor);
draw_hline_on_screen(screen, mx - 4, mx - 2, my, cursor_color);
draw_hline_on_screen(screen, mx + 2, mx + 4, my, cursor_color);
draw_vline_on_screen(screen, mx, my - 4, my - 2, cursor_color);
draw_vline_on_screen(screen, mx, my + 2, my + 4, cursor_color);
break;
case MOUSE_CURSOR_ARROW:
tic_api_tri(tic, state->mouseX, state->mouseY, state->mouseX + 3, state->mouseY, state->mouseX, state->mouseY + 3, state->mouseCursorColor);
tic_api_line(tic, state->mouseX + 3, state->mouseY, state->mouseX, state->mouseY + 3, tic_color_black);
{
// Calculate black for the outline.
u32 black_color = get_screen_color(tic, tic_color_black);
// Draw the filled triangle part of the arrow
for (int y = 0; y <= 2; y++) {
for (int x = 0; x <= 2 - y; x++) {
draw_pixel_on_screen(screen, mx + x, my + y, cursor_color);
}
}
// Draw the black outline (hypotenuse of the triangle)
draw_pixel_on_screen(screen, mx + 3, my, black_color);
draw_pixel_on_screen(screen, mx + 2, my + 1, black_color);
draw_pixel_on_screen(screen, mx + 1, my + 2, black_color);
draw_pixel_on_screen(screen, mx, my + 3, black_color);
}
break;
}
}
@@ -972,7 +1034,7 @@ void tic80_libretro_variables(bool startup)
state->mouseHideTimerStart = atoi(var.value);
if (state->mouseHideTimerStart > 0) {
state->mouseHideTimerStart = state->mouseHideTimerStart * TIC80_FRAMERATE;
state->mouseHideTimer = state->mouseHideTimerStart;
state->mouseHideTimer = 0; // Cursor starts hidden
}
else {
state->mouseHideTimerStart = 0;
Vendored
+1 -1