Compare commits

..
4 Commits
Author SHA1 Message Date
kichikuou 50a6b17c5e Version 2.19.1 2026-08-02 12:41:18 +09:00
kichikuou 0d49619366 Fix unit map storage for 16-bit variables
Store unit map attributes as vmvar_t and derive VR/VW transfer sizes
from their element type. This prevents buffer overruns after vmvar_t
became uint16_t and fixes upward movement in Rance IV.

Add scenario coverage for both transfer directions and verify that
adjacent variables remain intact.
2026-08-02 12:34:40 +09:00
kichikuou da3cc5d900 ags: Consolidate display info accessors 2026-07-24 21:04:25 +09:00
kichikuou 1785e93fcc Refactor gfx API 2026-07-24 21:04:22 +09:00
26 changed files with 258 additions and 214 deletions
+3
View File
@@ -1,5 +1,8 @@
# Changelog
## 2.19.1 - 2026-08-02
- Fixed an issue that prevented moving upward during battles in Rance 4.
## 2.19.0 - 2026-07-19
- Replaced the GTK-based popup menu and dialogs with a pure-SDL implementation.
- On Android, the popup menu opens with a three-finger tap.
+1 -1
View File
@@ -5,7 +5,7 @@ set(CMAKE_C_STANDARD 99)
enable_testing()
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
set(XSYSTEM35_VERSION "2.19.0")
set(XSYSTEM35_VERSION "2.19.1")
include(CheckSymbolExists)
include(FetchContent)
+1 -1
View File
@@ -17,7 +17,7 @@ android {
minSdkVersion 21
targetSdkVersion 34
versionCode 36
versionName "2.19.0" + gitRevision()
versionName "2.19.1" + gitRevision()
externalNativeBuild {
cmake {
arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static"
+30 -17
View File
@@ -193,7 +193,7 @@ void ags_setViewArea(int x, int y, int width, int height) {
nact->ags.view_area.y = y;
nact->ags.view_area.w = width;
nact->ags.view_area.h = height;
gfx_setWindowSize(width, height);
gfx_setViewSize(width, height);
}
void ags_setWindowTitle(const char *title_utf8) {
@@ -202,20 +202,33 @@ void ags_setWindowTitle(const char *title_utf8) {
gfx_setWindowTitle(buf);
}
void ags_getDIBInfo(DispInfo *info) {
info->width = nact->ags.world_width;
info->height = nact->ags.world_height;
info->depth = nact->ags.world_depth;
}
void ags_getViewAreaInfo(DispInfo *info) {
gfx_getWindowInfo(NULL, NULL, &info->depth);
info->width = nact->ags.view_area.w;
info->height = nact->ags.view_area.h;
}
void ags_getWindowInfo(DispInfo *info) {
gfx_getWindowInfo(&info->width, &info->height, &info->depth);
void ags_getDisplayInfo(enum ags_display display, int *width, int *height, int *depth) {
SDL_DisplayMode dm;
switch (display) {
case AGS_DISPLAY_CURRENT:
SDL_GetCurrentDisplayMode(0, &dm);
*width = dm.w;
*height = dm.h;
*depth = SDL_BITSPERPIXEL(dm.format);
break;
case AGS_DISPLAY_DIB:
*width = nact->ags.world_width;
*height = nact->ags.world_height;
*depth = nact->ags.world_depth;
break;
case AGS_DISPLAY_VIEW_AREA:
SDL_GetCurrentDisplayMode(0, &dm);
*width = nact->ags.view_area.w;
*height = nact->ags.view_area.h;
*depth = SDL_BITSPERPIXEL(dm.format);
break;
case AGS_DISPLAY_DESKTOP:
SDL_GetDesktopDisplayMode(0, &dm);
*width = dm.w;
*height = dm.h;
*depth = SDL_BITSPERPIXEL(dm.format);
break;
}
}
void ags_setExposeSwitch(bool expose) {
@@ -733,10 +746,10 @@ void ags_setCursorLocation(int x, int y, bool is_dibgeo, bool for_selection) {
for (int i = 1; i < 8; i++) {
int xi = ((dx*i*i*i) >> 9) - ((3*dx*i*i)>> 6) + ((3*dx*i) >> 3) + p.x;
int yi = ((dy*i*i*i) >> 9) - ((3*dy*i*i)>> 6) + ((3*dy*i) >> 3) + p.y;
event_set_mouse_location(xi, yi);
gfx_warpMouse(xi, yi);
sys_sleep(cursor_move_time / 7);
}
event_set_mouse_location(x, y);
gfx_warpMouse(x, y);
} else if (!for_selection) {
event_set_mouse_internal_location(x, y);
sys_sleep(cursor_move_time);
+7 -8
View File
@@ -102,11 +102,12 @@ typedef struct {
uint32_t index;
} PixelColor;
typedef struct {
int width;
int height;
int depth;
} DispInfo;
enum ags_display {
AGS_DISPLAY_CURRENT,
AGS_DISPLAY_DIB,
AGS_DISPLAY_VIEW_AREA,
AGS_DISPLAY_DESKTOP,
};
struct _ags {
SDL_Color pal[256]; /* system palette */
@@ -145,9 +146,7 @@ void ags_reset(void);
extern void ags_setWorldSize(int width, int height, int depth);
extern void ags_setViewArea(int x, int y, int width, int height);
extern void ags_setWindowTitle(const char *title_utf8);
extern void ags_getDIBInfo(DispInfo *info);
extern void ags_getWindowInfo(DispInfo *info);
extern void ags_getViewAreaInfo(DispInfo *info);
extern void ags_getDisplayInfo(enum ags_display display, int *width, int *height, int *depth);
extern bool ags_check_param(int *x, int *y, int *w, int *h);
extern bool ags_check_param_xy(int *x, int *y);
extern surface_t *ags_getDIB();
+6 -6
View File
@@ -90,7 +90,7 @@ typedef struct {
#define UNITMAP_ATTRIB_WALKRESULT (3)
/* UnitMAP 全体へのポインタ */
static int *UnitMap = NULL;
static vmvar_t *UnitMap = NULL;
/* VC command */
static int nPageNum;
static int x0Map;
@@ -160,7 +160,7 @@ void commandVC() { /* from Rance4 */
return;
}
UnitMap = (int *)calloc(cxMap * cyMap * nPageNum * UNITMAP_ATTRIB_DEPTH, sizeof(int));
UnitMap = calloc(cxMap * cyMap * nPageNum * UNITMAP_ATTRIB_DEPTH, sizeof(*UnitMap));
srcimg = (UnitMapSrcImg *)calloc(nPageNum, sizeof(UnitMapSrcImg));
if (NULL == UnitMap || NULL == srcimg) {
@@ -458,7 +458,7 @@ void commandVR() { /* from Rance4 */
int nPage = getCaliValue();
int nType = getCaliValue();
vmvar_t *var = getCaliVariable();
int *dst;
vmvar_t *dst;
TRACE("VR %d,%d,%p:",nPage, nType, var);
@@ -481,14 +481,14 @@ void commandVR() { /* from Rance4 */
return;
}
memcpy(dst, var, sizeof(int) * MAPSIZE_PER_ATTRIB);
memcpy(dst, var, MAPSIZE_PER_ATTRIB * sizeof(*dst));
}
void commandVW() { /* from Rance4 */
int nPage = getCaliValue();
int nType = getCaliValue();
vmvar_t *var = getCaliVariable();
int *src;
vmvar_t *src;
TRACE("VW %d,%d,%p:",nPage, nType, var);
@@ -511,7 +511,7 @@ void commandVW() { /* from Rance4 */
return;
}
memcpy(var, src, sizeof(int) * MAPSIZE_PER_ATTRIB);
memcpy(var, src, MAPSIZE_PER_ATTRIB * sizeof(*src));
}
void commandVE() { /* from T2 */
+12 -28
View File
@@ -53,6 +53,14 @@ static uint32_t get_counter(int num) {
return sys_get_ticks() - counters[num].base;
}
static void write_display_info(vmvar_t *var, enum ags_display display) {
int width, height, depth;
ags_getDisplayInfo(display, &width, &height, &depth);
var[0] = width;
var[1] = height;
var[2] = depth;
}
void commandZC() {
/* システムの使用環境を変更する */
int m = getCaliValue();
@@ -394,39 +402,21 @@ void commandZZ2() {
void commandZZ3() {
/* WINDOWSの全画面サイズや表示色数を変数列に返す */
vmvar_t *var = getCaliVariable();
DispInfo info;
ags_getWindowInfo(&info);
*var = info.width;
*(var + 1) = info.height;
*(var + 2) = info.depth;
write_display_info(var, AGS_DISPLAY_CURRENT);
TRACE("ZZ3 %p:",var);
}
void commandZZ4() {
/* DIB の全画面 サイズや色数を変数列に返す */
vmvar_t *var = getCaliVariable();
DispInfo info;
ags_getDIBInfo(&info);
*var = info.width;
*(var + 1) = info.height;
*(var + 2) = info.depth;
write_display_info(var, AGS_DISPLAY_DIB);
TRACE("ZZ4 %p:",var);
}
void commandZZ5() {
/* SYSTEM3.5用表示画面 の サイズや色数を変数列に返す */
vmvar_t *var = getCaliVariable();
DispInfo info;
ags_getViewAreaInfo(&info);
*var = info.width;
*(var + 1) = info.height;
*(var + 2) = info.depth;
write_display_info(var, AGS_DISPLAY_VIEW_AREA);
TRACE("ZZ5 %p:",var);
}
@@ -449,13 +439,7 @@ void commandZZ8() {
void commandZZ9() {
/* 起動時のスクリーンサイズを取得する */
vmvar_t *var = getCaliVariable();
DispInfo info;
ags_getWindowInfo(&info);
*var = info.width;
*(var + 1) = info.height;
*(var + 2) = info.depth;
write_display_info(var, AGS_DISPLAY_DESKTOP);
TRACE("ZZ9 %p:",var);
}
+19 -45
View File
@@ -35,7 +35,6 @@
#include "debugger.h"
#include "nact.h"
#include "gfx.h"
#include "gfx_private.h"
#include "scheduler.h"
#include "menu.h"
#include "modal.h"
@@ -54,8 +53,8 @@ enum CustomEventCode {
DEBUGGER_COMMAND,
};
/* pointer の状態 */
static int mousex, mousey, mouseb;
static SDL_Point mouse_pos;
static int mouseb;
static int mouse_wheel_up, mouse_wheel_down;
bool RawKeyInfo[256];
@@ -264,8 +263,8 @@ void send_agsevent(enum agsevent_type type, int code) {
agsevent_t agse = {
.type = type,
.code = code,
.mousex = mousex,
.mousey = mousey
.mousex = mouse_pos.x,
.mousey = mouse_pos.y
};
nact->ags.eventcb(&agse); // Async in emscripten
@@ -277,37 +276,16 @@ void send_agsevent(enum agsevent_type type, int code) {
#endif
}
void event_set_mouse_location(int x, int y) {
// scale mouse x and y
float scalex, scaley;
SDL_RenderGetScale(gfx_renderer, &scalex, &scaley);
x *= scalex;
y *= scaley;
// calculate window borders
int logw, logh;
SDL_RenderGetLogicalSize(gfx_renderer, &logw, &logh);
float scalew, scaleh;
scalew = logw * scalex;
scaleh = logh * scaley;
int winw, winh;
SDL_GetWindowSize(gfx_window, &winw, &winh);
float border_left = (winw - scalew) / 2;
float border_top = (winh - scaleh) / 2;
// offset x and y by window borders
x += border_left;
y += border_top;
SDL_WarpMouseInWindow(gfx_window, x, y);
void event_set_mouse_internal_location(int x, int y) {
mouse_pos.x = x;
mouse_pos.y = y;
send_agsevent(AGSEVENT_MOUSE_MOTION, 0);
}
void event_set_mouse_internal_location(int x, int y) {
mousex = x;
mousey = y;
send_agsevent(AGSEVENT_MOUSE_MOTION, 0);
SDL_Point event_get_touch_position(const SDL_TouchFingerEvent *e) {
int view_w, view_h;
gfx_getViewSize(&view_w, &view_h);
return (SDL_Point){e->x * view_w, e->y * view_h};
}
// Stores a deferred touch event (valid if .timestamp != 0) to add a delay
@@ -365,7 +343,7 @@ void event_handle_event(SDL_Event *e) {
case SDL_WINDOWEVENT:
switch (e->window.event) {
case SDL_WINDOWEVENT_EXPOSED:
gfx_dirty = true;
gfx_requestRedraw();
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
volume_on_window_focus(false);
@@ -381,7 +359,7 @@ void event_handle_event(SDL_Event *e) {
break;
#endif
case SDL_APP_DIDENTERFOREGROUND:
gfx_dirty = true;
gfx_requestRedraw();
break;
case SDL_KEYDOWN:
keyEventProsess(&e->key, true);
@@ -393,7 +371,7 @@ void event_handle_event(SDL_Event *e) {
msgskip_activate(!msgskip_isActivated());
break;
case SDLK_F4:
gfx_setFullscreen(!gfx_fullscreen);
gfx_setFullscreen(!gfx_isFullscreen());
break;
}
#ifdef __ANDROID__
@@ -454,8 +432,7 @@ void event_handle_event(SDL_Event *e) {
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_RIGHT)] = true;
} else {
button = SDL_BUTTON_LEFT;
mousex = e->tfinger.x * view_w;
mousey = e->tfinger.y * view_h;
mouse_pos = event_get_touch_position(&e->tfinger);
send_agsevent(AGSEVENT_MOUSE_MOTION, 0);
defer_touch_event(&e->tfinger);
}
@@ -466,16 +443,14 @@ void event_handle_event(SDL_Event *e) {
case SDL_FINGERUP:
if (SDL_GetNumTouchFingers(e->tfinger.touchId) == 0) {
int ags_button = (mouseb & 1 << SDL_BUTTON_LEFT) ? AGSEVENT_BUTTON_LEFT : AGSEVENT_BUTTON_RIGHT;
mousex = e->tfinger.x * view_w;
mousey = e->tfinger.y * view_h;
mouse_pos = event_get_touch_position(&e->tfinger);
send_agsevent(AGSEVENT_BUTTON_RELEASE, ags_button);
defer_touch_event(&e->tfinger);
}
break;
case SDL_FINGERMOTION:
mousex = e->tfinger.x * view_w;
mousey = e->tfinger.y * view_h;
mouse_pos = event_get_touch_position(&e->tfinger);
send_agsevent(AGSEVENT_MOUSE_MOTION, 0);
break;
@@ -608,8 +583,7 @@ int event_get_mouse(SDL_Point *p) {
get_event();
if (p) {
p->x = mousex;
p->y = mousey;
*p = mouse_pos;
}
int m1 = mouseb & (1 << 1) ? SYS35KEY_RET : 0;
+1 -1
View File
@@ -27,8 +27,8 @@
void event_init(void);
void event_remove(void);
void event_set_joy_device_index(int index);
void event_set_mouse_location(int x, int y);
void event_set_mouse_internal_location(int x, int y);
SDL_Point event_get_touch_position(const SDL_TouchFingerEvent *e);
int event_get_key(void);
int event_get_mouse(SDL_Point *p);
void event_get_wheel(vmvar_t *forward, vmvar_t *back);
+6 -44
View File
@@ -35,7 +35,7 @@
#include "portab.h"
#include "system.h"
#include "font.h"
#include "gfx_private.h"
#include "gfx.h"
#include "hacks.h"
typedef struct {
@@ -155,44 +155,6 @@ void font_measure_text(FontSpec font, const char *str_utf8, int len, int *w, int
}
}
// SDL can't blit ARGB to an indexed bitmap properly, so we do it ourselves.
static void gfx_drawAntiAlias_8bpp(int dstx, int dsty, SDL_Surface *src, uint8_t col)
{
const SDL_Color* palette = gfx_palette->colors;
Uint8 cache[256*7];
memset(cache, 0, 256);
for (int y = 0; y < src->h && dsty + y < main_surface->h; y++) {
if (dsty + y < 0)
continue;
uint8_t *sp = (uint8_t*)src->pixels + y * src->pitch;
uint8_t *dp = (uint8_t*)main_surface->pixels + (dsty + y) * main_surface->pitch + dstx;
for (int x = 0; x < src->w && dstx + x < main_surface->w; x++) {
Uint8 r, g, b, alpha;
SDL_GetRGBA(*((Uint32*)sp), src->format, &r, &g, &b, &alpha);
alpha = alpha >> 5; // reduce bit depth
if (!alpha) {
// Transparent, do nothing
} else if (alpha == 7) {
*dp = col; // Fully opaque
} else if (cache[*dp] & 1 << alpha) {
*dp = cache[alpha << 8 | *dp]; // use cached value
} else {
// find nearest color in palette
cache[*dp] |= 1 << alpha;
int c = gfx_nearest_color(
(palette[col].r * alpha + palette[*dp].r * (7 - alpha)) / 7,
(palette[col].g * alpha + palette[*dp].g * (7 - alpha)) / 7,
(palette[col].b * alpha + palette[*dp].b * (7 - alpha)) / 7);
cache[alpha << 8 | *dp] = c;
*dp = c;
}
sp += src->format->BytesPerPixel;
dp++;
}
}
}
SDL_Rect font_draw_text(FontSpec font, int x, int y, const char *str_utf8, uint8_t cl) {
SDL_Surface *fs;
SDL_Rect r_src, r_dst = {};
@@ -212,11 +174,11 @@ SDL_Rect font_draw_text(FontSpec font, int x, int y, const char *str_utf8, uint8
if ((game_id == GAME_RANCE3 || game_id == GAME_RANCE3_ENG) && cl == 32)
antialias = false;
if (antialias) {
fs = TTF_RenderUTF8_Blended(fontset->id, str_utf8, gfx_palette->colors[cl]);
} else {
fs = TTF_RenderUTF8_Solid(fontset->id, str_utf8, gfx_palette->colors[cl]);
}
SDL_Color color = gfx_getPaletteColor(cl);
if (antialias)
fs = TTF_RenderUTF8_Blended(fontset->id, str_utf8, color);
else
fs = TTF_RenderUTF8_Solid(fontset->id, str_utf8, color);
if (!fs) {
WARNING("Text rendering failed: %s", TTF_GetError());
return r_dst;
+9 -2
View File
@@ -22,6 +22,7 @@
#include "config.h"
#include <SDL_surface.h>
#include <SDL_video.h>
#include "portab.h"
#include "ags.h"
@@ -31,9 +32,12 @@ void gfx_Remove(void);
/* ウィンド関係 */
void gfx_setWorldSize(int width, int height, int depth);
void gfx_setWindowSize(int w, int h);
void gfx_setViewSize(int w, int h);
void gfx_getViewSize(int *width, int *height);
SDL_Window *gfx_getWindow(void);
void gfx_warpMouse(int x, int y);
SDL_Rect gfx_viewToWindowRect(SDL_Rect rect);
void gfx_setWindowTitle(char *name);
void gfx_getWindowInfo(int *width, int *height, int *depth);
void gfx_setFullscreen(bool on);
bool gfx_isFullscreen(void);
void gfx_raiseWindow(void);
@@ -44,10 +48,12 @@ SDL_Surface *gfx_createSurfaceView(SDL_Surface *sf, int x, int y, int w, int h);
/* 画面更新 */
void gfx_updateArea(SDL_Rect *src, SDL_Point *dst);
void gfx_updateAll(SDL_Rect *view_rect);
void gfx_requestRedraw(void);
void gfx_updateScreen(void);
/* パレット関係 */
void gfx_setPalette(SDL_Color *pal, int first, int count);
SDL_Color gfx_getPaletteColor(uint8_t color);
/* 描画関係 */
void gfx_drawRectangle(int x, int y, int w, int h, uint8_t c);
@@ -55,6 +61,7 @@ void gfx_fillRectangle(int x, int y, int w, int h, uint8_t c);
void gfx_fillRectangleRGB(int x, int y, int w, int h, uint8_t r, uint8_t g, uint8_t b);
void gfx_fillCircle(int left, int top, int diameter, uint8_t c);
void gfx_drawLine(int x1, int y1, int x2, int y2, uint8_t c);
void gfx_drawAntiAlias_8bpp(int x, int y, SDL_Surface *src, uint8_t color);
SDL_Rect gfx_floodFill(int x, int y, int col);
SDL_Rect gfx_drawString(int x, int y, const char *str_utf8, uint8_t col, FontSpec font);
void gfx_copyArea(int sx,int sy, int w, int h, int dx, int dy);
+48 -1
View File
@@ -43,6 +43,8 @@
#include "modal.h"
#include "debugger.h"
static bool gfx_dirty;
static void gfx_pal_check(void) {
if (nact->ags.pal_changed) {
nact->ags.pal_changed = false;
@@ -56,6 +58,10 @@ static Uint32 palette_color(uint8_t c) {
return SDL_MapRGB(main_surface->format, gfx_palette->colors[c].r, gfx_palette->colors[c].g, gfx_palette->colors[c].b);
}
SDL_Color gfx_getPaletteColor(uint8_t color) {
return gfx_palette->colors[color];
}
void gfx_updateScreen(void) {
if (!gfx_dirty)
return;
@@ -66,6 +72,10 @@ void gfx_updateScreen(void) {
gfx_dirty = false;
}
void gfx_requestRedraw(void) {
gfx_dirty = true;
}
/* off-screen の指定領域を Main Window へ転送 */
void gfx_updateArea(SDL_Rect *rect, SDL_Point *dst) {
SDL_Rect sw = {0, 0, main_surface->w, main_surface->h};
@@ -254,7 +264,7 @@ SDL_Rect gfx_floodFill(int x, int y, int c) {
}
}
int gfx_nearest_color(int r, int g, int b) {
static int nearest_color(int r, int g, int b) {
int i, col, mind = INT_MAX;
for (i = 0; i < 256; i++) {
int dr = r - gfx_palette->colors[i].r;
@@ -269,6 +279,43 @@ int gfx_nearest_color(int r, int g, int b) {
return col;
}
// SDL can't blit ARGB to an indexed bitmap properly, so we do it ourselves.
void gfx_drawAntiAlias_8bpp(int dstx, int dsty, SDL_Surface *src, uint8_t col) {
const SDL_Color *palette = gfx_palette->colors;
Uint8 cache[256 * 7];
memset(cache, 0, 256);
for (int y = 0; y < src->h && dsty + y < main_surface->h; y++) {
if (dsty + y < 0)
continue;
uint8_t *sp = (uint8_t *)src->pixels + y * src->pitch;
uint8_t *dp = (uint8_t *)main_surface->pixels
+ (dsty + y) * main_surface->pitch + dstx;
for (int x = 0; x < src->w && dstx + x < main_surface->w; x++) {
Uint8 r, g, b, alpha;
SDL_GetRGBA(*(Uint32 *)sp, src->format, &r, &g, &b, &alpha);
alpha >>= 5; // reduce bit depth
if (!alpha) {
// Transparent, do nothing.
} else if (alpha == 7) {
*dp = col; // Fully opaque.
} else if (cache[*dp] & 1 << alpha) {
*dp = cache[alpha << 8 | *dp];
} else {
cache[*dp] |= 1 << alpha;
int c = nearest_color(
(palette[col].r * alpha + palette[*dp].r * (7 - alpha)) / 7,
(palette[col].g * alpha + palette[*dp].g * (7 - alpha)) / 7,
(palette[col].b * alpha + palette[*dp].b * (7 - alpha)) / 7);
cache[alpha << 8 | *dp] = c;
*dp = c;
}
sp += src->format->BytesPerPixel;
dp++;
}
}
}
SDL_Rect gfx_drawString(int x, int y, const char *str_utf8, uint8_t col, FontSpec font) {
gfx_pal_check();
return font_draw_text(font, x, y, str_utf8, col);
-4
View File
@@ -30,17 +30,13 @@
#include "portab.h"
#include "ags.h"
extern SDL_Window *gfx_window;
extern SDL_Renderer *gfx_renderer;
extern SDL_Texture *gfx_texture;
extern SDL_Palette *gfx_palette;
extern surface_t *gfx_dibinfo;
extern int view_w;
extern int view_h;
extern bool gfx_dirty;
extern bool gfx_fullscreen;
int gfx_nearest_color(int r, int g, int b);
SDL_Surface *gfx_dib_to_surface_with_alpha(int x, int y, int w, int h);
SDL_Surface *gfx_dib_to_surface_colorkey(int x, int y, int w, int h, int col);
+30 -12
View File
@@ -36,7 +36,7 @@
#include "xsystem35.h"
#include "image.h"
SDL_Window *gfx_window;
static SDL_Window *gfx_window;
SDL_Renderer *gfx_renderer;
SDL_Texture *gfx_texture;
SDL_Surface *main_surface; // offscreen surface
@@ -44,8 +44,7 @@ SDL_Palette *gfx_palette;
surface_t *gfx_dibinfo;
int view_w;
int view_h;
bool gfx_dirty;
bool gfx_fullscreen;
static bool gfx_fullscreen;
static void window_init(const char *render_driver);
static void makeDIB(int width, int height, int depth);
@@ -57,7 +56,7 @@ int gfx_Initialize(const char *render_driver) {
/* offscreen Pixmap */
makeDIB(SYS35_DEFAULT_WIDTH, SYS35_DEFAULT_HEIGHT, SYS35_DEFAULT_DEPTH);
gfx_setWindowSize(SYS35_DEFAULT_WIDTH, SYS35_DEFAULT_HEIGHT);
gfx_setViewSize(SYS35_DEFAULT_WIDTH, SYS35_DEFAULT_HEIGHT);
#ifdef __EMSCRIPTEN__
// Prevent SDL from calling emscripten_exit_fullscreen on visibilitychange
@@ -171,13 +170,32 @@ void gfx_setWorldSize(int width, int height, int depth) {
SDL_FillRect(main_surface, NULL, 0);
}
/* display の size と depth の取得 */
void gfx_getWindowInfo(int *width, int *height, int *depth) {
SDL_DisplayMode dm;
SDL_GetCurrentDisplayMode(0, &dm);
if (width) *width = dm.w;
if (height) *height = dm.h;
if (depth) *depth = SDL_BITSPERPIXEL(dm.format);
void gfx_getViewSize(int *width, int *height) {
if (width)
*width = view_w;
if (height)
*height = view_h;
}
SDL_Window *gfx_getWindow(void) {
return gfx_window;
}
static SDL_Point view_to_window_point(int x, int y) {
SDL_Point point;
SDL_RenderLogicalToWindow(gfx_renderer, x, y, &point.x, &point.y);
return point;
}
void gfx_warpMouse(int x, int y) {
SDL_Point point = view_to_window_point(x, y);
SDL_WarpMouseInWindow(gfx_window, point.x, point.y);
}
SDL_Rect gfx_viewToWindowRect(SDL_Rect rect) {
SDL_Point tl = view_to_window_point(rect.x, rect.y);
SDL_Point br = view_to_window_point(rect.x + rect.w, rect.y + rect.h);
return (SDL_Rect){tl.x, tl.y, br.x - tl.x, br.y - tl.y};
}
/* DIBの取得 */
@@ -212,7 +230,7 @@ void gfx_raiseWindow(void) {
SDL_RaiseWindow(gfx_window);
}
void gfx_setWindowSize(int w, int h) {
void gfx_setViewSize(int w, int h) {
if (w == view_w && h == view_h) return;
view_w = w;
+8 -10
View File
@@ -139,9 +139,11 @@ bool input_modal_number(INPUTNUM_PARAM *p) {
#else // not Android or Emscripten
#include <SDL.h>
#include "nact.h"
#include "font.h"
#include "gfx_private.h"
#include "gfx.h"
#include "modal.h"
#include "utfsjis.h"
@@ -223,12 +225,8 @@ static bool menu_string_handler(const SDL_Event *e, modal *modal) {
// near the field. `box` is in logical (view) coordinates; convert to window
// pixels for SDL_SetTextInputRect.
static void set_text_input_rect(mu_Rect box) {
SDL_Rect r = { box.x, box.y, box.w, box.h };
int x2, y2;
SDL_RenderLogicalToWindow(gfx_renderer, box.x, box.y, &r.x, &r.y);
SDL_RenderLogicalToWindow(gfx_renderer, box.x + box.w, box.y + box.h, &x2, &y2);
r.w = x2 - r.x;
r.h = y2 - r.y;
SDL_Rect r = gfx_viewToWindowRect(
(SDL_Rect){box.x, box.y, box.w, box.h});
SDL_SetTextInputRect(&r);
}
@@ -270,7 +268,7 @@ static bool inputstring_build(mu_Context *ctx, modal *modal) {
int w = 320;
int h = 3 * (row_h + ctx->style->spacing) + ctx->style->padding * 2
+ ctx->style->title_height;
mu_Rect r = mu_rect((view_w - w) / 2, (view_h - h) / 2, w, h);
mu_Rect r = modal_centered_rect(w, h);
if (mu_begin_window_ex(ctx, title, r,
MU_OPT_NORESIZE | MU_OPT_NOCLOSE | MU_OPT_NOSCROLL)) {
@@ -327,7 +325,7 @@ bool input_modal_string(INPUTSTRING_PARAM *p) {
// Look up an AGS color index in the active palette and return it as a mu_Color.
static mu_Color palette_color(int index) {
SDL_Color c = gfx_palette->colors[index];
SDL_Color c = gfx_getPaletteColor(index);
return (mu_Color){ c.r, c.g, c.b, 255 };
}
@@ -436,7 +434,7 @@ static bool inputnumber_build(mu_Context *ctx, modal *modal) {
int w = 240;
int h = 3 * (row_h + ctx->style->spacing) + ctx->style->padding * 2
+ ctx->style->title_height;
mu_Rect r = mu_rect((view_w - w) / 2, (view_h - h) / 2, w, h);
mu_Rect r = modal_centered_rect(w, h);
if (mu_begin_window_ex(ctx, title, r,
MU_OPT_NORESIZE | MU_OPT_NOCLOSE | MU_OPT_NOSCROLL)) {
+7 -5
View File
@@ -50,12 +50,14 @@ EM_JS(void, menu_setSkipState, (bool enabled, bool activated), {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL.h>
#include "portab.h"
#include "event.h"
#include "system.h"
#include "nact.h"
#include "menu.h"
#include "gfx_private.h"
#include "gfx.h"
#include "msgskip.h"
#include "modal.h"
#include "volume.h"
@@ -105,8 +107,8 @@ static bool menu_popup_handler(const SDL_Event *e, modal *modal) {
st->touch_armed = true;
} else if (e->type == SDL_FINGERDOWN && st->touch_armed) {
// A tap outside the menu dismisses it.
int x = e->tfinger.x * view_w, y = e->tfinger.y * view_h;
if (!point_in_rect(x, y, st->rect))
SDL_Point p = event_get_touch_position(&e->tfinger);
if (!point_in_rect(p.x, p.y, st->rect))
st->base.cancelled = true;
}
return modal_default_handler(e, modal);
@@ -124,7 +126,7 @@ static bool menu_build(mu_Context *ctx, modal *modal) {
int w = 200;
// `rows` rows with (rows - 1) inter-row gaps, plus the window's body padding.
int h = rows * row_h + (rows - 1) * ctx->style->spacing + ctx->style->padding * 2;
mu_Rect r = mu_rect((view_w - w) / 2, (view_h - h) / 2, w, h);
mu_Rect r = modal_centered_rect(w, h);
st->rect = r;
if (mu_begin_window_ex(ctx, "menu", r,
@@ -198,7 +200,7 @@ static bool confirm_lose_progress(const char *title, const char *confirm_label)
};
const SDL_MessageBoxData messagebox_data = {
.flags = SDL_MESSAGEBOX_INFORMATION,
.window = gfx_window,
.window = gfx_getWindow(),
.title = title,
.message = _("Any unsaved progress will be lost."),
.numbuttons = SDL_arraysize(buttons),
+19 -14
View File
@@ -30,6 +30,7 @@
#include "event.h"
#include "font.h"
#include "gfx.h"
#include "gfx_private.h"
#include "modal.h"
#include "nact.h"
@@ -54,7 +55,7 @@ static enum {
} touch_phase;
static int touch_hover_frames; // frames left to dwell in TOUCH_HOVER
static bool touch_release_after_press; // finger lifted early; release once pressed
static int touch_x, touch_y;
static SDL_Point touch_pos;
static const FontSpec menu_font = { FONT_GOTHIC, FONT_WEIGHT_NORMAL, MODAL_FONT_SIZE };
@@ -137,23 +138,20 @@ bool modal_default_handler(const SDL_Event *e, modal *modal) {
// Touch handling (see touch_phase above). The press/release is driven
// from the frame loop in modal_run(), not emitted here directly.
case SDL_FINGERDOWN:
touch_x = e->tfinger.x * view_w;
touch_y = e->tfinger.y * view_h;
mu_input_mousemove(ctx, touch_x, touch_y);
touch_pos = event_get_touch_position(&e->tfinger);
mu_input_mousemove(ctx, touch_pos.x, touch_pos.y);
touch_phase = TOUCH_HOVER;
touch_hover_frames = TOUCH_HOVER_FRAMES;
touch_release_after_press = false;
break;
case SDL_FINGERMOTION:
touch_x = e->tfinger.x * view_w;
touch_y = e->tfinger.y * view_h;
mu_input_mousemove(ctx, touch_x, touch_y);
touch_pos = event_get_touch_position(&e->tfinger);
mu_input_mousemove(ctx, touch_pos.x, touch_pos.y);
break;
case SDL_FINGERUP:
touch_x = e->tfinger.x * view_w;
touch_y = e->tfinger.y * view_h;
touch_pos = event_get_touch_position(&e->tfinger);
if (touch_phase == TOUCH_PRESS) {
mu_input_mouseup(ctx, touch_x, touch_y, MU_MOUSE_LEFT);
mu_input_mouseup(ctx, touch_pos.x, touch_pos.y, MU_MOUSE_LEFT);
touch_phase = TOUCH_NONE;
} else if (touch_phase == TOUCH_HOVER) {
// Tap ended before the press was emitted; release right after it.
@@ -294,6 +292,13 @@ bool modal_handle_event(const SDL_Event *e) {
return current_modal->handler(e, current_modal);
}
mu_Rect modal_centered_rect(int width, int height) {
int view_w, view_h;
gfx_getViewSize(&view_w, &view_h);
return mu_rect((view_w - width) / 2, (view_h - height) / 2,
width, height);
}
EMSCRIPTEN_KEEPALIVE // Prevent inlining, because this function is listed in ASYNCIFY_ADD
void modal_run(modal *m) {
assert(!ctx); // modals are not nestable
@@ -314,12 +319,12 @@ void modal_run(modal *m) {
if (touch_hover_frames > 0) {
touch_hover_frames--;
} else {
mu_input_mousedown(ctx, touch_x, touch_y, MU_MOUSE_LEFT);
mu_input_mousedown(ctx, touch_pos.x, touch_pos.y, MU_MOUSE_LEFT);
touch_phase = TOUCH_PRESS;
}
}
if (touch_phase == TOUCH_PRESS && touch_release_after_press) {
mu_input_mouseup(ctx, touch_x, touch_y, MU_MOUSE_LEFT);
mu_input_mouseup(ctx, touch_pos.x, touch_pos.y, MU_MOUSE_LEFT);
touch_phase = TOUCH_NONE;
touch_release_after_press = false;
}
@@ -334,7 +339,7 @@ void modal_run(modal *m) {
mu_Id hash = mu_get_id(ctx, ctx->command_list.items, ctx->command_list.idx);
if (hash != prev_hash) {
prev_hash = hash;
gfx_dirty = true;
gfx_requestRedraw();
}
nact->callback();
sys_wait_vsync(); // -> gfx_updateScreen() -> modal_render_overlay()
@@ -343,7 +348,7 @@ void modal_run(modal *m) {
current_modal = NULL;
free(ctx);
ctx = NULL;
gfx_dirty = true; // repaint once more to clear the overlay
gfx_requestRedraw(); // repaint once more to clear the overlay
// The gesture that opened/dismissed the dialog must not leave the engine
// with a key or button stuck "down".
+3
View File
@@ -48,6 +48,9 @@ void modal_run(modal *modal);
// consumed, false when no modal is running or the handler falls through.
bool modal_handle_event(const union SDL_Event *e);
// Return a rectangle of the requested size centered in the logical view.
mu_Rect modal_centered_rect(int width, int height);
// Render the active modal's overlay onto the screen. Call this from the screen
// update path; it is a no-op when no modal is running.
void modal_render_overlay(void);
+1 -2
View File
@@ -21,7 +21,6 @@
#include <SDL.h>
#include "debugger.h"
#include "gfx.h"
#include "gfx_private.h"
#include "scheduler.h"
#ifdef __ANDROID__
@@ -136,5 +135,5 @@ void sys_show_message_box(enum messagebox_type type, const char* title_utf8, con
case MESSAGEBOX_WARNING: flags = SDL_MESSAGEBOX_WARNING; break;
case MESSAGEBOX_INFO: flags = SDL_MESSAGEBOX_INFORMATION; break;
}
SDL_ShowSimpleMessageBox(flags, title_utf8, message_utf8, gfx_window);
SDL_ShowSimpleMessageBox(flags, title_utf8, message_utf8, gfx_getWindow());
}
+1 -2
View File
@@ -34,7 +34,6 @@
#include "utfsjis.h"
#include "music.h"
#include "modal.h"
#include "gfx_private.h"
// Volume Valancer で扱う最大チャンネル数
#define MAXVOLCH 16
@@ -193,7 +192,7 @@ static bool volume_build(mu_Context *ctx, modal *modal) {
+ ctx->style->padding * 2;
int h = (nch + 1) * (row_h + ctx->style->spacing) + ctx->style->padding * 2
+ ctx->style->title_height;
mu_Rect r = mu_rect((view_w - w) / 2, (view_h - h) / 2, w, h);
mu_Rect r = modal_centered_rect(w, h);
if (mu_begin_window_ex(ctx, _("Sound Settings"), r,
MU_OPT_NORESIZE | MU_OPT_NOSCROLL)) {
+3 -3
View File
@@ -20,7 +20,7 @@
#include <commctrl.h>
#include <SDL_syswm.h>
#include "input_modal.h"
#include "gfx_private.h"
#include "gfx.h"
#include "resources.h"
static HWND get_hwnd(SDL_Window *window) {
@@ -139,7 +139,7 @@ bool input_modal_string(INPUTSTRING_PARAM *p) {
DialogBoxParamW(
GetModuleHandle(NULL),
MAKEINTRESOURCEW(IDD_TEXTINPUT),
get_hwnd(gfx_window),
get_hwnd(gfx_getWindow()),
text_dialog_proc,
(LPARAM)p);
return true;
@@ -149,7 +149,7 @@ bool input_modal_number(INPUTNUM_PARAM *p) {
INT_PTR ret = DialogBoxParamW(
GetModuleHandle(NULL),
MAKEINTRESOURCEW(IDD_NUMINPUT),
get_hwnd(gfx_window),
get_hwnd(gfx_getWindow()),
num_dialog_proc,
(LPARAM)p);
return ret == IDOK;
+11 -8
View File
@@ -18,12 +18,12 @@
*/
#include <windows.h>
#include <time.h>
#include <SDL_events.h>
#include <SDL_syswm.h>
#include "system.h"
#include "menu.h"
#include "nact.h"
#include "gfx.h"
#include "gfx_private.h"
#include "resources.h"
#include "msgskip.h"
#include "texthook.h"
@@ -46,7 +46,7 @@ static void saveScreenshot(void) {
OPENFILENAME ofn = {
.lStructSize = sizeof(OPENFILENAME),
.hwndOwner = get_hwnd(gfx_window),
.hwndOwner = get_hwnd(gfx_getWindow()),
.lpstrFilter = "Bitmap files (*.bmp)\0*.bmp\0All files (*.*)\0*.*\0",
.lpstrFile = pathbuf,
.nMaxFile = MAX_PATH,
@@ -79,12 +79,15 @@ static bool toggle_menu_item(UINT id, bool *checked_out) {
}
void menu_init(void) {
SDL_Window *window = gfx_getWindow();
int view_w, view_h;
gfx_getViewSize(&view_w, &view_h);
HINSTANCE hinst = (HINSTANCE)GetModuleHandle(NULL);
hmenu = LoadMenu(hinst, MAKEINTRESOURCE(IDR_MENU1));
SetMenu(get_hwnd(gfx_window), hmenu);
SetMenu(get_hwnd(window), hmenu);
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
// Let SDL recalc the window size, taking menu height into account.
SDL_SetWindowSize(gfx_window, view_w, view_h);
SDL_SetWindowSize(window, view_w, view_h);
CheckMenuItem(hmenu, ID_OPTION_MOUSE_MOVE, MF_BYCOMMAND | MFS_CHECKED);
}
@@ -108,11 +111,11 @@ void win_menu_onSysWMEvent(SDL_SysWMmsg* msg) {
break;
case ID_SCREEN_WINDOW:
gfx_setFullscreen(false);
SetMenu(get_hwnd(gfx_window), hmenu);
SetMenu(get_hwnd(gfx_getWindow()), hmenu);
break;
case ID_SCREEN_FULL:
gfx_setFullscreen(true);
SetMenu(get_hwnd(gfx_window), NULL);
SetMenu(get_hwnd(gfx_getWindow()), NULL);
break;
case ID_SCREEN_INTEGER_SCALING:
if (toggle_menu_item(ID_SCREEN_INTEGER_SCALING, &checked))
@@ -140,11 +143,11 @@ void win_menu_onSysWMEvent(SDL_SysWMmsg* msg) {
void win_menu_onMouseMotion(int x, int y) {
if (!gfx_isFullscreen())
return;
SetMenu(get_hwnd(gfx_window), y > 0 ? NULL : hmenu);
SetMenu(get_hwnd(gfx_getWindow()), y > 0 ? NULL : hmenu);
}
void menu_setSkipState(bool enabled, bool activated) {
HWND hwnd = get_hwnd(gfx_window);
HWND hwnd = get_hwnd(gfx_getWindow());
EnableMenuItem(hmenu, ID_MSGSKIP, enabled ? MF_ENABLED : MF_GRAYED);
+1
View File
@@ -15,6 +15,7 @@
%#stack.adv:
%#math.adv:
%#variable.adv:
%#unitmap.adv:
HH0,tests_passed: ' tests passed' R
HH0,tests_failed: ' tests failed' R
+1
View File
@@ -7,6 +7,7 @@ strvar.adv
stack.adv
math.adv
variable.adv
unitmap.adv
#DLLHeader
ShString.HEL
BIN
View File
Binary file not shown.
+30
View File
@@ -0,0 +1,30 @@
MS FILE,"unitmap.adv":
!unitmap_src0:11!!unitmap_src1:22!!unitmap_src2:33!!unitmap_src3:44!
!unitmap_dst0:0!!unitmap_dst1:0!!unitmap_dst2:0!!unitmap_dst3:0!!unitmap_sentinel:1234!
VC 1,0,0,2,2,1,1:
; VR copies consecutive 16-bit VM variables to the unit map.
VR 0,3,unitmap_src0:
VG 0,3,0,0:
~AssertEquals RND,11,__LINE__:
VG 0,3,1,0:
~AssertEquals RND,22,__LINE__:
VG 0,3,0,1:
~AssertEquals RND,33,__LINE__:
VG 0,3,1,1:
~AssertEquals RND,44,__LINE__:
; VW performs the reverse conversion without overwriting adjacent variables.
VS 0,3,0,0,101:
VS 0,3,1,0,102:
VS 0,3,0,1,103:
VS 0,3,1,1,104:
VW 0,3,unitmap_dst0:
~AssertEquals unitmap_dst0,101,__LINE__:
~AssertEquals unitmap_dst1,102,__LINE__:
~AssertEquals unitmap_dst2,103,__LINE__:
~AssertEquals unitmap_dst3,104,__LINE__:
~AssertEquals unitmap_sentinel,1234,__LINE__:
%0: