Merge remote-tracking branch 'origin/main' into website
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Submodule vendor/sdl2 updated: f070c83a60...5d24957039
Reference in New Issue
Block a user