Implement INPUTNAME string opcodes

This commit is contained in:
gamer147
2026-07-29 13:21:15 -04:00
parent 862b74ecbc
commit feef18d411
12 changed files with 443 additions and 4 deletions

View File

@@ -31,6 +31,10 @@ public readonly record struct SurfaceRectCopy(
/// <summary>A synchronous AGE-owned diagnostic prompt after native body/context formatting.</summary>
public readonly record struct DiagnosticMessage(string Caption, string Text);
/// <summary>AGERc command 10's synchronous full-width text edit request and result.</summary>
public readonly record struct FullwidthTextEditRequest(string CurrentText, string InitialText);
public readonly record struct FullwidthTextEditResult(bool Accepted, string Text);
public enum SurfaceBlackFadeDirection
{
FromBlack,
@@ -44,6 +48,9 @@ public interface IHost
/// <summary>Present a modal diagnostic and return only after the user dismisses it.</summary>
void ShowDiagnosticMessage(DiagnosticMessage message)
=> System.Console.Error.WriteLine($"{message.Caption}: {message.Text}");
/// <summary>Present AGERc's modal full-width editor. Cancel preserves CurrentText.</summary>
FullwidthTextEditResult EditFullwidthString(FullwidthTextEditRequest request)
=> new(false, request.CurrentText);
// Script context is retained for diagnostics/page location; resource operands are universal packed ids.
void EnterScriptContext(string scriptName) { }
void ExitScriptContext() { }

View File

@@ -0,0 +1,98 @@
using System.Text;
namespace Age.Engine.Sys4;
public enum FullwidthTextValidationError
{
None,
TooLong,
NonDoubleByteCharacter,
}
/// <summary>AGE's Japanese-locale byte-string operations used by INPUTNAME.</summary>
public static class Cp932Text
{
public const int NativeNameByteLimit = 16;
private static readonly Encoding NativeEncoding;
static Cp932Text()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
NativeEncoding = Encoding.GetEncoding(932);
}
private static bool IsLeadByte(byte value)
=> value is >= 0x81 and <= 0x9f or >= 0xe0 and <= 0xfc;
private static bool IsTrailByte(byte value)
=> value is >= 0x40 and <= 0x7e or >= 0x80 and <= 0xfc;
private static byte[] EncodeCString(string value, Encoding encoding)
{
int nul = value.IndexOf('\0');
return encoding.GetBytes(nul < 0 ? value : value[..nul]);
}
public static int CharacterLength(string value, Encoding encoding)
{
byte[] bytes = EncodeCString(value, encoding);
int count = 0;
for (int offset = 0; offset < bytes.Length; count++)
{
if (IsLeadByte(bytes[offset]) && offset + 1 < bytes.Length
&& IsTrailByte(bytes[offset + 1]))
offset += 2;
else
offset++;
}
return count;
}
public static string Substring(
string value, int start, int count, Encoding encoding)
{
byte[] bytes = EncodeCString(value, encoding);
var characters = new List<(int Offset, int Length)>();
for (int offset = 0; offset < bytes.Length;)
{
int length = IsLeadByte(bytes[offset]) && offset + 1 < bytes.Length
&& IsTrailByte(bytes[offset + 1])
? 2
: 1;
characters.Add((offset, length));
offset += length;
}
int end = unchecked(start + count);
if (end < 1 || end > characters.Count) end = characters.Count;
using var selected = new MemoryStream();
for (int index = 0; index < characters.Count; index++)
{
if (index < start || index >= end) continue;
(int offset, int length) = characters[index];
selected.Write(bytes, offset, length);
}
return encoding.GetString(selected.ToArray());
}
public static FullwidthTextValidationError ValidateFullwidthName(
string value, Encoding encoding, int byteLimit = NativeNameByteLimit)
{
byte[] bytes = EncodeCString(value, encoding);
if (bytes.Length > byteLimit) return FullwidthTextValidationError.TooLong;
for (int offset = 0; offset < bytes.Length; offset += 2)
{
if (offset + 1 >= bytes.Length
|| !IsLeadByte(bytes[offset])
|| !IsTrailByte(bytes[offset + 1]))
return FullwidthTextValidationError.NonDoubleByteCharacter;
}
return FullwidthTextValidationError.None;
}
public static FullwidthTextValidationError ValidateFullwidthName(
string value, int byteLimit = NativeNameByteLimit)
=> ValidateFullwidthName(value, NativeEncoding, byteLimit);
}

View File

@@ -2,6 +2,7 @@ using Age.Engine.Diagnostics;
using Age.Engine.Hosting;
using Age.Engine.Model;
using Age.Engine.Persistence;
using Age.Engine.Sys4;
using System.Text;
namespace Age.Engine.Vm;
@@ -1237,6 +1238,25 @@ public sealed class VirtualMachine
case "halve-strlen": // 0x1a6: strlen(native encoded bytes) >> 1
Write(a[0], NativeStringByteLength(ReadStr(a[1])) >> 1);
return pc + 1;
case "edit-fullwidth-string-dialog": // 0x144: blocking AGERc command-10 editor
{
string current = ReadStr(a[0]);
string initial = ReadStr(a[1]);
FullwidthTextEditResult result =
_host.EditFullwidthString(new(current, initial));
if (result.Accepted) WriteStr(a[0], result.Text);
return pc + 1;
}
case "cp932-character-length": // 0x2c6: Japanese-locale _mbstrlen
Write(a[0], Cp932Text.CharacterLength(ReadStr(a[1]), _nativeStringEncoding));
return pc + 1;
case "cp932-substring": // 0x2c8: multibyte-character interval [start,start+count)
WriteStr(a[0], Cp932Text.Substring(
ReadStr(a[1]),
unchecked((int)Read(a[2])),
unchecked((int)Read(a[3])),
_nativeStringEncoding));
return pc + 1;
case "u00425790": // upstream ABI label
case "append-diagnostic-value": // 0x1b2: generic operand text -> EngineCtx accumulator
_diagnosticOutput.Append(FormatDiagnosticOperand(a[0]));