100 Commits
Author SHA1 Message Date
kichikuou 960c97ecc9 Update game_compatibility.md
- Mark Busou Shoujotai Blade Briders as supported.
- Extend the table through Rance X.
2026-09-22 11:19:26 +09:00
kichikuou e61f4b3c42 Math: Add Asin() and Acos() 2026-09-22 11:18:54 +09:00
kichikuou 03f9e7e87e Implement SystemService.GetSystemBufferName 2026-09-22 11:18:54 +09:00
kichikuou e93cddad76 SengokuRanceFont: Fall back to Gothic TTF for missing FNL glyphs
SengokuRanceFont.DLL falls back to MS Gothic for characters whose glyphs
are absent from the FNL font. Reproduce this behavior to enable Japanese
translations of mods based on the MangaGamer release of Sengoku Rance.

Add an HLL-local composite font that wraps each selected FNL font_size
together with a FreeType Gothic font_size. The composite font checks for
each glyph in the FNL font and delegates rendering and size to Gothic
when the glyph is missing.

Scale fallback glyphs down and adjust their vertical position so that
they align with the FNL text line.
2026-09-22 10:24:48 +09:00
kichikuou 461eca1a85 parts: Inherit alpha clipper from parent parts
Move alpha_clipper_parts_no into inherited part parameters so child
parts use their parent's clipper unless they explicitly override it.

Bump the save version to 7.
2026-09-16 12:47:59 +09:00
kichikuou 6ea8af95ba parts: Fix global params after releasing a parent
parts_release detached children without marking them dirty, leaving
them with global parameters inherited from the released parent.
Furthermore, parts_update_component skipped global parameter updates
for parentless parts.

Mark orphaned children dirty and combine parentless parts against the
root parameters.
2026-09-16 12:46:07 +09:00
kichikuou 167d75d092 parts: Implement PartsFunc 112 (GetText) 2026-09-16 10:14:38 +09:00
kichikuou 275d71712e parts: Honor FLAT graphic key alignment
Offset the sprite so that the origin matches the anchor selected by the
graphic key's align field. Use the source rectangle dimensions when an
area is specified, and the full texture dimensions otherwise.
2026-09-16 10:14:37 +09:00
kichikuou d68baa74a0 parts: Save and load 3DLayer components
Serialize the SealEngine plugin bound to the component. Instances now
remember the name so referenced assets can be reloaded.

Bump the parts save format version to 6.
2026-09-16 10:14:26 +09:00
kichikuou 267f7bb762 parts: Support PartsFunc func_id assignments in Blade Briders
Blade Briders inserts three layer functions at func_id 3-5 into the
assignment used by Rance9, shifting all subsequent func_ids up by three.
Evenicle uses the same assignment as Blade Briders.

Use this assignment internally and add three to Rance9 func_ids before
dispatch. Also implement the functions 3-5.
2026-09-15 08:36:47 +09:00
kichikuou addc26ca45 parts: Implement PartsEngine.GetComponentSpeedupRateByMessageSkip 2026-09-15 08:36:39 +09:00
kichikuou 11ad7a583a 3d: Implement UV tiling
Read ColorMap UV tiling parameters from POL materials and apply separate
tiling factors to base and blend texture coordinates.
2026-09-13 11:20:54 +09:00
kichikuou 9654950ab4 3d: Implement SealEngine.SetDrawOption 1 (lighting)
When set to zero, render all meshes, including billboards and particle
meshes, as if they had the (nolighting) attribute.
2026-09-13 09:40:42 +09:00
kichikuou 021187752b 3d: Implement SealEngine.{Set,Get}InstanceGrayscaleRate
Add a per-instance grayscale rate that desaturates the final fragment
color after tone mapping.
2026-09-13 09:40:42 +09:00
kichikuou 5cc163ab16 3d: Load billboard frame images by name
In SealEngine, the frame images of a billboard instance are PNG files in
the archive, named "<name>\<basename>.png", "<name>\<basename>[1].png"
and so on, and the motion frame number is the index into them.

This also changes the lifetime management for billboard motions. A
billboard's motion holds the frame animation state of the instance,
which the game sets up before calling Load, so keep it across loads
and free it in free_instance.
2026-09-13 09:40:37 +09:00
kichikuou e14b4bca79 3d: Implement per-instance billboard vertices and UVs
Store the four billboard vertex positions and UVs in each RE_instance
and use them for regular billboard rendering. The static billboard mesh
is kept for billboard particles.
2026-09-12 11:00:57 +09:00
kichikuou 02f29b6c6d 3d: Implement SealEngine.{Set,Get}EdgeReductionRate 2026-09-12 11:00:49 +09:00
kichikuou d800e12f59 Math: Replace rand() with mt19937
On Windows RAND_MAX is 0x7fff, which makes Rand() and RandF() far too
coarse. Use mt19937 instead so that seeded sequences reproduce the
original engine's behavior.
2026-08-14 09:31:32 +09:00
kichikuou 9bb871d9fd parts: Fix loss of line breaks in saved multi-line text
Text lines are saved individually without newlines. Insert an explicit
newline between lines on load so the original line structure is
restored. Also free the strings read from the save data.
2026-08-11 13:46:18 +09:00
kichikuou 62d38aaf59 parts: Reference construction process CGs by name
Rance 9's AFA archives have no ID field, so asset IDs are sequential
indices local to each archive. When the game loads an additional CG
archive, the ID spaces overlap: a name resolved to an ID against the
base archive later fetches a different CG from the additional archive.
Keep the CG name instead.

Bumps the save version to 5; version 4 and earlier saves still store IDs
and are converted on load.
2026-08-11 13:05:32 +09:00
kichikuou 33f1295237 Resolve archive paths in asset_manager_load_archive
AFAFactory.LoadArchive passed the SJIS archive name straight through, so
the extra assets loaded by the Rance 9 v2.00 patch were never found.
Move the path resolution out of CGManager.LoadArchive so that both
callers share it.
2026-08-09 10:36:00 +09:00
kichikuou 77d9e77053 Update libsys4 2026-08-06 09:22:23 +09:00
kichikuou 68e8257c27 game_compatibility.md: Add Rance 9 (JA) as supported 2026-08-04 20:52:41 +09:00
kichikuou a2524bafd5 PartsEngine: Stabilize save format for Rance 9
Bump the save version and always serialize controller state and the
Rance 9 parts fields. Load these fields based on the save version while
retaining compatibility with existing multi-controller saves.
2026-08-02 08:49:06 +09:00
kichikuou 09a0e3329a 3d: Disable specular with zero power
Treat zero SpecularPower as disabling specular lighting instead of
producing a constant white highlight (pow(..., 0) = 1).
2026-07-29 08:50:18 +09:00
kichikuou b68e1622fd 3d: Implement DrawShadow mesh attribute
Also parse some other .opr attributes for features not implemented in
xsystem4.
2026-07-28 14:29:50 +09:00
kichikuou d5b27a0a47 KiwiSoundEngine: Fix BGM loop for Rance 9
Commit 03062d5 fixed audio lookup for Rance 9, but the new archive-data
preparation path bypassed BGI and WAI metadata. Pass the asset number
through that path so loop points are applied correctly.
2026-07-26 07:53:03 +09:00
kichikuou f0c4db718d movie_ffmpeg: Use nb_samples for audio frame length
Audio linesize may include unused buffer space. Using it as the sample
count caused short Vorbis frames to replay stale samples and slowed down
audio playback.
2026-07-22 09:07:30 +09:00
kichikuou d17fc30731 pl_mpeg: Support MPEG-2 program stream headers
Fortress.alm from Rance IX uses an MPEG-2 Program Stream containing
MPEG-1 video and MPEG-1 Layer II audio. As far as I checked, it is the
only .alm movie released in Japan that does not use an MPEG-1 Program
Stream.

The existing pl_mpeg decoders support its elementary streams, but the
demuxer rejects its MPEG-2 pack and PES headers. Extend the demuxer to
parse those headers and the PTS/DTS combinations used by Fortress.alm.
2026-07-22 07:31:44 +09:00
kichikuou 81f9113906 Use libsys4 wrappers for zlib operations
Replace direct one-shot zlib compression and decompression calls with
the libsys4 wrappers. Use exact-size decompression where file formats
provide the uncompressed size.

File2.c continues to depend directly on zlib because it uses streaming
inflate.
2026-07-20 12:53:19 +09:00
kichikuou 6d05546c2c input: Handle numpad keys with NumLock disabled
When NumLock is disabled, Windows reports numpad keys as navigation
virtual-key codes in the WM_KEYDOWN message. SACT2 games rely on this
behavior.

Translate SDL keypad scancodes to the corresponding SACT key codes in
that state, and map keypad Enter to VK_RETURN.

Fixes #359.
2026-07-15 08:36:08 +09:00
kichikuou c0cdf4f070 debugger: Add "3d" debugger commands
Add the following commands to the debugger:

  3d list [all]                      List plugins and instances
  3d plugin [<plugin>]               Show or select the current plugin
  3d show [<instance>]               Dump plugin or instance state
  3d set <instance> draw <0|1>       Enable or disable drawing
  3d set <instance> pos <x> <y> <z>  Set the instance position
  3d help                            Show command help

The show and set commands operate on the current plugin. If only one
plugin exists, it is selected automatically. Changes made with set are
rendered immediately.
2026-07-12 13:37:44 +09:00
kichikuou d8479ad80a parts: Fail construction build without target texture
If AddCreateCGToProcess fails for a missing CG, no create op is added to
the construction process. Game scripts still add drawing operations and
then build the process, leaving those operations with no destination
texture and causing an incomplete framebuffer error.

This makes construction build operations return false as soon as an
operation cannot be executed.

Fixes #356.
2026-07-07 08:52:17 +09:00
kichikuou 05fee4ba83 Resolve game config paths relative to game dir
Game-specific .xsys4rc files are loaded from the game directory, so
relative paths in that file are most useful when interpreted from the
same directory. This allows patches that ship external fonts to
reference them with relative paths.
2026-07-05 08:03:29 +09:00
kichikuou fb3d0bbd3e 3d: Gate edge drawing on per-instance flag
In both TapirEngine and SealEngine an instance is outlined only when the
global edge draw option and its per-instance edge flag are both on. The
per-instance flag is exposed via Set/GetInstanceDrawParam, whose edge
index differs by engine (Tapir 0, Seal 2).

For the global option the engine only distinguishes on/off, so drop the
RE_draw_edge_mode enum.
2026-06-26 09:19:15 +09:00
kichikuou 507b65fc9c SealEngine: Implement SetEdgeLength/SetEdgeColor
In SealEngine the outline length and color are plugin-wide values set
via SetEdgeLength/SetEdgeColor, not the per-mesh .opr parameters used by
TapirEngine.
2026-06-25 16:32:54 +09:00
kichikuou fab789df65 VM: Check function signature in DG_STR_TO_METHOD
Only resolve the method when the function's signature matches the
delegate type, returning 0 otherwise.

Also, DG_SET / DG_NEW_FROM_METHOD / DG_ADD should not add function 0.
2026-06-21 06:56:24 +09:00
kichikuou 6dbb5baf19 SealEngine: Implement filmic tone mapping
Apply the Uncharted 2 tone curve [1] from light_params[0..7] as the
final fragment operation.

[1] https://filmicworlds.com/blog/filmic-tonemapping-operators/
2026-06-20 11:41:56 +09:00
kichikuou db8279ebc3 SealEngine: Implement hemisphere lighting
This implements the sky/middle/ground hemisphere ambient driven by
light_params[8..19].

SealEngine has no separate specular-light instance; its specular instead
shares the hemisphere light vector.
2026-06-20 11:41:56 +09:00
kichikuou 250bc82818 SealEngine: Enable light scattering
Reuse ReignEngine's existing scattering shader code. The ls_* uniforms
are sourced from .lit parameters.
2026-06-20 11:41:56 +09:00
kichikuou 4843e4e87a SealEngine: add light parameter storage and HLL bindings
SealEngine manages a 41-float light parameter set (serialized as .lit
file) for tone mapping, hemisphere lighting, and light scattering. This
adds the storage and HLL plumbing, without wiring values into shaders.

- A DIRECTIONAL_LIGHT instance owns a parameter set, filled by loading a
  .lit file (RE_instance_load).
- SealEngine.StoreInstanceLightParam() reflects that presets into the
  plugin's master buffer, which is used by the renderer.
- SealEngine.LoadInstanceLightParam() copies the master back into an
  instance, and Reset()/Set()/Get() operate directly on the master.
2026-06-20 11:41:56 +09:00
kichikuou 1051294247 SealEngine: Implement SpecularColor and SpecularPower .opr attributes
Parse per-mesh SpecularColor and SpecularPower attributes from .opr
files, and apply them to specular lighting.
2026-06-20 11:41:56 +09:00
kichikuou d073875d94 MsgSkip: Initialize in _PreLink when Init() is absent
Some games (e.g. Rance 01, Rance 9) only export GetFlag and SetFlag, so
Init() is never called. Initialize from _PreLink with a default save
file name in that case.
2026-06-18 08:10:49 +09:00
kichikuou b62edb7188 SealEngine: Support HeightDetection=true .opr attribute
In TapirEngine, CalcIntersectEyeVec() only worked on models with a
"collision" mesh. In SealEngine, a raycast collider is built from the
union of meshes flagged HeightDetection=true in .opr.

Also return the nearest intersection instead of the first.
2026-06-13 20:30:45 +09:00
kichikuou 85547ef4f4 3d: Remove color_add uniform
Since the ambient term is now added unconditionally, the s3de per-object
additive color can be folded into instance_ambient.
2026-06-12 15:00:40 +09:00
kichikuou f108b7f436 3d: Apply ambient and vertex color across all diffuse modes
The EMISSIVE and ENV_MAP diffuse modes did not apply the ambient term or
vertex color (color_mod) to the fragment. In the original TapirEngine /
SealEngine, all diffuse modes contribute ambient and vertex color.

This fixes a bug where the movement/attack range indicator color did
not change in battle scenes.
2026-06-12 15:00:40 +09:00
kichikuou 90c3ab5008 3d: Apply per-instance diffuse to mesh color
SetInstanceDiffuse has two roles depending on the instance type: for
directional lights it is the light color, and for meshes it is a
multiplier on the mesh color. xsystem4 honored only the former.

Treat inst->diffuse as color multiplier, via the diffuse_mod uniform.
2026-06-12 15:00:40 +09:00
kichikuou 7b9565fe25 3d: Add reset_draw_uniforms() to prevent per-draw state leaks
This adds a helper that resets every per-draw uniform to a neutral
default and call it at the start of the billboard, .pae and .3de paths.

This fixes uv_scroll and use_blend_texture leaks from a previously
rendered model.
2026-06-12 15:00:40 +09:00
kichikuou 4dcc50d6ca 3d: Build models from MarmotModelEngine.Update()
In TapirEngine and SealEngine, model updates are drived by
MarmotModelEngine.Update(). RE_render() now only renders.
2026-06-12 15:00:40 +09:00
kichikuou 5c47ad5aae SealEngine: Implement ZWrite .opr attribute 2026-06-12 15:00:39 +09:00
kichikuou 0e8bd36407 SACT2: Update draw plugins during TRANS_Update()
This fixes the issue where the 3D plugin freezes during transitions.
2026-06-10 17:21:04 +09:00
kichikuou 28554137cc Stoat/Chipmunk: Silence KeepPreviousView_Set/GetMode warnings
KeepPreviousView and its mode are purely a rendering performance knob
(incremental rendering optimization). Toggling the mode never affects
rendering correctness, so leaving these unimplemented is harmless and
warning about them is just noise.
2026-06-10 17:17:08 +09:00
kichikuou 32024bc805 PartsEngine: Fix "Invalid controller number" error after vm_reset
PE_Reset() zeroed the controller stack, leaving it with no default
controller. Since PE_Reset() does not clear parts_engine_initialized,
the subsequent PE_Init() returns early so the stack stays empty.

Reset the stack with ctrl_stack_init() instead, which re-creates the
default controller. ctrl_stack_fini() is now unused and removed.
2026-06-10 17:17:08 +09:00
kichikuou ec9bd95dc9 PartsEngine: Implement PartsFunc 3 (motion Pause/Resume)
Wire up PartsFunc 3 (PauseMotion) to a new PE_PauseMotion(). While
paused, PE_UpdateMotionTime stops advancing the motion clock.
2026-06-10 17:17:05 +09:00
kichikuou 8dee7d8658 PartsEngine: Implement PartsFunc 6 (SaveThumbnail)
Save a reduced copy of the main surface to a file. The image is written
as QNT under the save directory.

To avoid aliasing at large reductions, the surface is downscaled by
repeatedly halving before the final stretch.
2026-06-10 17:17:01 +09:00
kichikuou 847987ec1b 3de: Add support for billboard and polygon objects
At spawn, init_particle() samples its fixed properties from a
deterministic PRNG into s3de_particle, so playback is reproducible.

Each frame, update_object_states() interpolates the emitter's animated
properties into s3de_object_state.

To draw, render_s3de_effect() combines the two: for each live particle
s3de_billboard_world_transform() or s3de_mesh_world_transform() derives
a world matrix from the lifetime state computed by eval_particle(),
together with a posture basis that orients the quad or mesh. Objects are
drawn sorted by distance from the camera.
2026-06-04 09:41:49 +09:00
kichikuou bf0b2bb44f SealEngine: Add .3de parser, effect framework, camera, and sound
.3de is the particle/effect format used by SealEngine. Like the
ReignEngine's .pae, it is a text format describing an effect as a set of
emitters, but the two differ enough in design that .3de lives in its own
file (s3de.c) rather than being folded into particle.c.

This commit lays the groundwork:

- Parse .3de text.
- Integrate effect loading, playback, and caching into the existing
  infrastructure.
- Apply the camera object's per-frame override to RE_camera.
- Play the companion sound asset.

Billboard and polygon-object rendering will be added separately.
2026-05-31 08:22:01 +09:00
kichikuou 916daec6b2 3d: Match MOT bones to POL bones by array index
The original {Reign,Tapir,Seal}Engine apply motion bones to model bones
purely by array position, never consulting bone name or bone_id.

This fixes bone animation of "Shizuka & Nagi" model in Rance 9.
2026-05-29 07:17:41 +09:00
kichikuou aa112849de Remove passed_time parameter from parts_render_update 2026-05-28 07:39:15 +09:00
kichikuou 8ca1a31ccc PartsEngine: clamp passed_time to 0 in PE_UpdateComponent
After loading a save, the game script may give a negative passed_time
because it computes the delta time using an absolute system.GetTime()
value stored in the save data.

This fixes hang on load in Rance 9.

This also removes the equivalent workaround in parts_render_update for
Rance 01, since the new clamp covers that case as well.
2026-05-27 12:42:50 +09:00
kichikuou c01a27583c SealEngine: Add .mpr (motion material keyframes) support
.mpr is an optional sidecar to .mot used by SealEngine, replacing .txa
used in ReignEngine / TapirEngine. While .mot drives bone poses, .mpr
drives material properties on a per-frame timeline.

Supported keys, all keyed by frame number:

  Object{MulAlpha,MulDiffuse,AddAmbient} : applied to the whole model
  Mesh{MulAlpha,MulDiffuse,AddAmbient}   : applied per named mesh
  MeshTextureAnime                       : swap diffuse texture index

Colors and alpha use linear interpolation between keys. Out-of-range
frames hold the nearest endpoint.

Mesh* tracks act as per-mesh tints and do not leak to other meshes
that happen to share a material. MeshTextureAnime is the exception:
it rewrites the shared material's diffuse slot, so all meshes using
that material animate in sync (e.g. in Rance 9, hanny_black_move.mpr
lists only hibana_R but hibana_L animates together).
2026-05-22 13:57:02 +09:00
kichikuou 9eee82c13b 3d: Fix camera yaw direction and rotation order
The camera view direction was computed incorrectly in two ways:

1. glm_euler (XYZ order) applies pitch around world X. The original
   engine uses YXZ where pitch is around the camera's local X after yaw.

2. SetCameraAngle negated the input yaw.
2026-05-20 07:10:41 +09:00
kichikuou 32f4253948 Flat: Guard against division by zero in particle time calculation 2026-05-19 11:38:36 +09:00
kichikuou cb0b969492 Flat: Add particle emitter support
This implements rendering of FLAT_LIB_EMITTER libraries: timed spawning,
per-particle motion, fade, and 3-axis rotation.

For each visible emitter, render_flat_emitter() builds an emitter base
matrix from the ancestor key stack so particles follow their parent
timelines. parent_key_mode chooses between filtering ancestor properties
via the emitter's inherit_* flags (SELECTIVE) and using ancestor poses
wholesale (ANCESTOR_CURRENT / ANCESTOR_AT_BIRTH). For each birth frame
in the lifetime window, parts_flat_foreach_emitter_particle() reseeds
the RNG from (rand_seed, birth_frame), making particles deterministic
across re-renders, and yields each particle's pose, which is composed
with the emitter transform and submitted as a quad through the standard
parts texture pipeline.
2026-05-19 07:12:11 +09:00
kichikuou 224db2f9cc Parts: Fix Z-order of 3DLayer and Movie parts
3DLayer and Movie parts use a SACT sprite as their render target (for
SealEngine output or movie frames). That sprite was registered into
the scene at z=0, so the content was drawn at z=0 instead of (or in
addition to) the parts engine's render path at the parts' configured Z.

Hide the bound SACT sprite from the scene so the parts engine is the
sole renderer of its texture, and add a parts_render case for 3DLayer
that composites the bound sprite's texture through parts_render_cg.
2026-05-16 12:44:17 +09:00
kichikuou a4dd6a42df Flat: Fix color propagation for nested flat TIMELINE libraries
Propagate parent keys' mul/add color parameters to child TIMELINE
library rendering.
2026-05-12 10:25:03 +09:00
kichikuou f535223988 Flat: Support angle_x/angle_y rotation and reverse_lr/reverse_tb
The original engine implements proper perspective projection, but we
use simpler orthographic foreshortening. In Rance 9, the visual
difference is barely noticeable.
2026-05-09 12:12:38 +09:00
kichikuou f9087eb7ac Flat: Fix area_x/area_y leaking into screen position
area_x/area_y select a sub-rectangle of the texture, but should not
shift the on-screen position. Translate the quad by (-area_x, -area_y)
to cancel the offset the sub-rect's top-left would otherwise introduce.
2026-05-07 12:42:08 +09:00
kichikuou 636743478c Flat: Propagate parent TIMELINE's draw_filter
A parent key's draw_filter had no effect on CG/STOP_MOTION leaves
inside descendant TIMELINE libraries. Resolve the effective filter
at each key (own if non-NORMAL, otherwise inherited) and thread it
through the render chain.
2026-05-07 12:18:35 +09:00
kichikuou a2f087b7f9 Flat: Add support for FLAT_LIB_STOP_MOTION
FLAT_LIB_STOP_MOTION represents a sprite animation as a sequence of CG
frames. A stop_motion library specifies a base CG file (e.g. "foo.png");
at load time, parts_flat_load() discovers numbered frame variants
("foo_01.png", "foo_02.png", ...) and builds a frame list.

At render time, the active frame is resolved from the local timeline
clock using three parameters:
  - span: number of timeline ticks each frame is displayed
  - loop_type: 0 = play once and stop at last frame,
               1 = loop forward,
               2 = ping-pong (forward then backward)
  - local: the timeline's current local time
2026-05-07 11:23:53 +09:00
kichikuou c45bb3a736 Parts: support multiply/screen draw_filter for parts_render_cg 2026-05-03 12:39:43 +09:00
kichikuou 484f383826 Parts: fix multiply/screen draw_filter to respect alpha
The multiply and screen blend functions used in render_flat_item
ignored the source alpha, so transparent pixels still modulated the
destination color instead of leaving it untouched.

Add a draw_filter uniform to the parts fragment shader and have it
output an alpha-baked source color, so that the framebuffer result
after the GL blend equation interpolates correctly in alpha:

- multiply: shader outputs src = mix(vec3(1.0), mod_color, alpha)
  blend (GL_DST_COLOR, GL_ZERO) gives src*dst,
  i.e. mix(dst, mod_color*dst, alpha)
- screen: shader outputs src = mod_color * alpha
  blend (GL_ONE, GL_ONE_MINUS_SRC_COLOR) gives src + (1-src)*dst,
  i.e. mod_color*alpha + (1 - mod_color*alpha)*dst

In both cases alpha=0 leaves the destination untouched, alpha=1
gives the full multiply/screen result.

Also switch the surface-area mask from multiplication to discard,
so pixels outside the rect leave the destination intact under
multiply blending (where multiplying src by 0 would otherwise
zero out dst).
2026-05-03 12:39:41 +09:00
kichikuou af12e19859 Fix missing src/parts/message.c in CMakeLists.txt 2026-05-03 07:37:02 +09:00
kichikuou e83cb1c91e InputString: Handle Backspace key to delete last character
This adds a key handler facility to input.c so that the InputString
can intercept the Backspace key while the IME is open, and remove the
last character from the result string. Backspace is ignored while the
IME has composing text, so it does not interfere with IME editing.
2026-05-03 07:13:47 +09:00
kichikuou 4c4a62e1ed Fix InputString.GetResultString() in Rance 02
The newer version (from Boku Dake no Hokenshitsu onwards) of
InputString.GetResultString() does not clear its internal string.

This fixes the issue in Rance 02 where the text always remained empty
when editing save comments.
2026-05-03 07:12:33 +09:00
kichikuou f01e41be12 Implement InputString.SetResultString
Used in Rance 02.
2026-05-03 07:12:33 +09:00
kichikuou 9ed1f52627 VM: Keep struct page alive during method calls for AIN v6.1+
This fixes heap use-after-free in the music mode in Rance 9.

Hold a reference to the struct page (`this` pointer) for the duration
of a method call. Without this, if the sole external reference to an
object is dropped during one of its method calls (e.g. an event handler
that destroys its own parent), the object and its members are freed
immediately, causing use-after-free when the VM tries to access them
afterward.

This matches the behavior of the original VM, which was introduced in
Rance9.exe and later titles.

The fix is gated on AIN v6.1+ (AIN v6 with a MSG1 section) to match
the original. Applying it unconditionally would change the heap refcount
state visible to resume saves, potentially breaking existing saves for
older games.
2026-05-02 07:56:47 +09:00
kichikuou 13d0c51351 PartsEngine: Fix text line layout
line_space was stored but never used during rendering or size
calculation. Apply it as inter-line spacing in both the size calculation
and the render path. Also initialize empty lines with the current text
style height so that newline-only lines occupy the correct space.

Change line_space from unsigned to int because negative values can be
set.
2026-04-28 11:18:58 +09:00
kichikuou 46bdbb05ef PartsEngine: Re-render text when font properties change
This fixes the rendering of the sample message in the "Message Color"
configuration of Rance 9.

PE_SetPartsFont* and PE_SetText*Space functions only modified the text
style struct without re-rendering existing character textures. Since
character glyphs are baked into textures at append time, subsequent
style changes had no visible effect.
2026-04-28 11:15:00 +09:00
kichikuou 6509dca693 Add stub for SystemService_GetAntiAliasingMode 2026-04-26 11:26:20 +09:00
kichikuou bf455de35d SealEngine: Implement MagSpeed functions
mag_speed is a per-plugin integer (default 1) set by SetMagSpeed on
all existing plugins. use_mag_speed is a per-instance flag (default
true) set by SetInstanceUseMagSpeed. When use_mag_speed is true,
motion delta_frame is multiplied by mag_speed.
2026-04-26 11:26:20 +09:00
kichikuou 511f6a4bfa SealEngine: Implement and stub various functions 2026-04-26 11:26:17 +09:00
kichikuou 4239345289 SealEngine: Support POL v4 format
POL v4 introduces material groups with texture blending. A material's
sub-material can be a "blend group" containing exactly two leaf
materials, whose textures are blended using per-vertex weights for
smooth terrain transitions.
2026-04-26 10:03:09 +09:00
kichikuou 63ab438391 PartsEngine: Implement 3DLayer parts type
This adds PARTS_3DLAYER type that creates a SealEngine plugin bound to a
SACT sprite.
2026-04-25 12:06:01 +09:00
kichikuou f52f4bd1f0 PartsEngine: Simplify parts_msg_push() calls 2026-04-25 07:49:08 +09:00
kichikuou 1b87d3f9a5 PartsEngine: Implement drag-and-drop processing
This implements Parts_SetDrag and drag/drop event handling. When a part
has drag enabled, clicking and moving the mouse will move the part and
emit DRAG_BEGIN/DRAGGING/DRAG_END messages. Drop target detection emits
DROP_ENTER/DROP_ON/DROPPED/DROP_LEAVE messages on parts under the cursor
during drag.
2026-04-25 07:38:46 +09:00
kichikuou df6998c0f9 PartsEngine: Implement LayoutBox
This adds PARTS_LAYOUT_BOX parts type with support for automatic
horizontal/vertical child positioning. Layout calculation runs during
the component update cycle, repositioning children before their global
parameters are combined.
2026-04-22 11:25:28 +09:00
kichikuou c4f89d040a PartsEngine: Implement PartsFunc 4 (SetWantSave)
This adds want_save parts flag which defaults to true. Parts with
want_save set to false are excluded from save data.
2026-04-20 09:03:43 +09:00
kichikuou 93c790dcc6 PartsEngine: Implement PartsFunc 159 (AddConstructProcess)
This function has a large number of parameters, which are serialized
fields from CASConstructionProcess. Dispatch on the command number to
the appropriate PE_Add*ToPartsConstructionProcess function.
2026-04-20 08:36:39 +09:00
kichikuou 88f199a8f2 PartsEngine: Implement PartsFunc 45 (PARTS_SetLockInputState)
This adds lock_input_state parts flag which prevents state transition.
2026-04-20 08:10:27 +09:00
kichikuou 28c8c277c9 PartsEngine: Implement PartsFunc 40-42 (PARTS_GetAbsolute[XYZ]) 2026-04-20 07:59:03 +09:00
kichikuou e5087f0757 PartsEngine: Implement PartsFunc 103 (GetPartsCGSurfaceArea) 2026-04-20 07:24:16 +09:00
kichikuou d20f52cf79 PartsEngine: Implement PE_SetPartsRectangleDetectionSize
This sets the hitbox size for rectangle-based hit detection, as a new
parts type PARTS_RECT_DETECTION. This is a detection-only type with no
drawing.
2026-04-19 11:16:00 +09:00
kichikuou dcbd149409 PartsEngine: Implement MOUSE_ON and MOUSE_MOVE message dispatch
MOUSE_ON is sent every frame while hovering, with cursor position and
accumulated hover time. MOUSE_MOVE is sent when the cursor position
changes during hover.
2026-04-19 09:55:19 +09:00
kichikuou 1655c8a8e0 PartsEngine: Implement pass_cursor flag
This implements SetPassCursor/GetPartsPassCursor HLL functions. The
pass_cursor flag controls whether the cursor "passes through" a
component to reach parts behind it.

Key changes to input processing:
- Iteration order changed from back-to-front to front-to-back for proper
  cursor consumption
- MOUSE_ENTER / MOUSE_LEAVE now fire for all parts regardless of
  clickable
- Click hit test uses clickable || !pass_cursor pre-filter
- pass_cursor=false parts consume the cursor, preventing hover and
  click events from reaching parts behind them
2026-04-19 09:42:20 +09:00
kichikuou cb24db29c2 PartsEngine: Fix state stuck at CLICKED when target state is uninitialized
parts_set_state silently ignored transitions to uninitialized states,
causing parts without a HOVERED state to remain in CLICKED after mouse
release. Fall back through CLICKED -> HOVERED -> DEFAULT so the first
initialized state is always selected. DEFAULT is accepted unconditionally
as the terminal fallback, matching the original engine's behavior.
2026-04-18 07:58:59 +09:00
kichikuou 03ef51e76a PartsEngine: Send mouse event messages
This introduces a per-parts is_hovered flag (replacing the global
parts_prev_pos) and fire MOUSE_ENTER, MOUSE_LEAVE, KEY_TRIGGER,
KEY_DOWN, MOUSE_CLICK, and KEY_UP events on the corresponding mouse
state transitions.

This also fixes the hittest in parts_update_mouse() to account for the
parent's global position, and to use the DEFAULT state hitbox (matching
the original engine's behavior).
2026-04-15 08:59:06 +09:00
kichikuou a0d78a024c PartsEngine: Implement message dispatch system
This adds a message queue that captures GUI events and exposes them
through HLL accessor functions.
2026-04-15 08:56:52 +09:00
kichikuou 93ca13a50b PartsEngine: Implement SetComponentType and GetComponentType 2026-04-12 16:53:34 +09:00
kichikuou c540eb9796 PartsEngine: Implement controller (layer) system
PartsEngine in Rance 9 manages multiple "controllers" (called "layers"
in game scripts) that provide part grouping, draw-order control, and
bulk release. This implements the core controller API:

  - AddController / RemoveController: push/pop a controller on the
    stack. RemoveController releases all parts belonging to the removed
    controller and returns their IDs in erase_number_list.
  - SetActiveLayer / GetActiveLayer / GetSystemOverlayLayer: control
    which controller newly created parts are assigned to. The system
    overlay controller is a special controller that always renders on
    top and survives ReleaseAllWithoutSystem.
  - ReleaseAllWithoutSystem: releases all parts except those in the
    system overlay controller and re-initializes the controller stack.

Controllers are identified by their position in the stack (0 = bottom).
Draw ordering uses the scene's two-level sprite sorting: sp.z is set to
the controller's stack index and sp.z2 to the part's Z value, so parts
in higher controllers always render above lower ones regardless of
individual Z values. When the parts_multi_controller flag (see below) is
false, the original Z ordering is preserved (allowing Z-interleaving
with non-parts scene sprites -- see #180) for compatibility with games
that do not use controllers.

Save/load of the controller stack and per-part controller_no is gated on
a parts_multi_controller flag (set at PreLink time when the game's HLL
exports AddController) rather than a save version bump. This keeps the
save format stable for games that do not use controllers, while allowing
the save format to evolve freely during development.
2026-04-12 12:09:17 +09:00