Compare commits

..
13 Commits
Author SHA1 Message Date
kichikuou b30fd71e4b Version 2.13.0 2024-12-07 08:23:49 +09:00
kichikuou 99ad91306b Emscripten: Use texthook suppression list 2024-12-06 13:11:09 +09:00
kichikuou ed96f26cea Add texthook_suppress option
For suppressing text hook on specified scenario pages.
2024-12-06 09:24:08 +09:00
kichikuou e6f71190ed Add texthook option
`-texthook print` prints text to console, and `-texthook copy`
automatically copies text to clipboard (for use with text hooker tools).

On Windows, copy mode can be toggled from the menu bar.
2024-12-04 16:34:12 +09:00
kichikuou 8219feee6d Windows: Redirect stderr to console
So that WARNING messages are printed on the console.
2024-12-04 11:43:59 +09:00
kichikuou bc8a85e046 Version 2.12.1 2024-12-02 10:42:58 +09:00
kichikuou 67f62f02e6 MSYS build: Static link portmidi
Fixes #60.
2024-12-02 10:12:20 +09:00
kichikuou 471c65973b emscripten: Do not use mmap for ALDs
To save memory, as ALD files are now stored as regular files in memfs.
2024-11-28 10:23:21 +09:00
kichikuou 5ff7149dc1 Fix a compiler warning 2024-11-28 10:21:18 +09:00
kichikuou 80988e52cf emscripten: Restore a hack to asyncify send_agsevent() callers
It was inadvertently removed in commit 61ad428.
2024-11-27 09:12:39 +09:00
kichikuou ade76adda6 Version 2.12.0 2024-11-26 20:49:17 +09:00
kichikuou 53d0e8ffef NightDemonDemo, tDemo: refresh screen when frame rate < 30 2024-11-25 17:30:15 +09:00
kichikuou b2e14bd19a Implement tDemo module
Fixes #59.
2024-11-25 16:35:05 +09:00
19 changed files with 393 additions and 77 deletions
+11
View File
@@ -1,5 +1,16 @@
# Changelog
## 2.13.0 - 2024-12-07
- Windows: `Option` -> `Auto Copy Text to Clipboard` menu command has been added.
When enabled, in-game text will be copied to the clipboard automatically. This
can be used with text hooker tools that support clipboard monitoring.
## 2.12.1 - 2024-12-02
- Windows: Fixed missing DLL error (#60)
## 2.12.0 - 2024-11-26
- Implemented playback of the opening movie for 妻みぐい2.
## 2.11.7 - 2024-11-12
- Fixed crash when canceling menu with two-finger touch (#58)
- Fixed screen artifact in Rance 3 immediately after selecting "Game Start"
+13 -4
View File
@@ -10,7 +10,7 @@ set(CMAKE_C_STANDARD 99)
enable_testing()
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
set(XSYSTEM35_VERSION "2.11.7")
set(XSYSTEM35_VERSION "2.13.0")
include(CheckSymbolExists)
include(FetchContent)
@@ -143,9 +143,18 @@ else() # non-emscripten, non-android
endif()
endif()
find_library(PORTMIDI portmidi)
if (PORTMIDI)
set(HAVE_PORTMIDI 1)
if (WIN32)
optional_pkg_check_modules(PORTMIDI IMPORTED_TARGET portmidi)
if (PORTMIDI_FOUND)
set(HAVE_PORTMIDI 1)
add_static_library(portmidi_static PORTMIDI)
set(PORTMIDI portmidi_static)
endif()
else ()
find_library(PORTMIDI portmidi)
if (PORTMIDI)
set(HAVE_PORTMIDI 1)
endif()
endif()
endif()
+2 -2
View File
@@ -16,8 +16,8 @@ android {
defaultConfig {
minSdkVersion 19
targetSdkVersion 34
versionCode 25
versionName "2.11.7" + gitRevision()
versionCode 28
versionName "2.13.0" + gitRevision()
externalNativeBuild {
cmake {
arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static"
+8
View File
@@ -40,6 +40,14 @@ finds System 3.x game files (*.ALD) from the current directory.
The format when writing save files. Possible values are: `xsystem35`,
`system36`, `system39` (default).
*-texthook* _mode_::
Set the text hook mode. Possible values are: `none` (default), `print` (print
text to console), `copy` (copy text to clipboard).
*-texthook_suppress* _list_::
Suppress text hook on specified scenario pages. _list_ is a comma-separated
list of page numbers. Used to suppress system messages, etc.
*-renderer* _name_::
Use the SDL rendering driver named _name_.
+1 -1
View File
@@ -55,7 +55,7 @@ Game Compatibility
| Rance 5D - The Lonely Girl (MangaGamer) | Supported | |
| 俺の下であがけ | Unknown | |
| 楽園行 | Unknown | |
| 妻みぐい2 | Supported | Opening demo is not supported |
| 妻みぐい2 | Supported | |
| シェル・クレイル | Supported | |
| ナイトデーモン | Supported | |
| 学園KING | Supported | From アリスCD |
+1
View File
@@ -73,6 +73,7 @@ static void ndd_run(int demonum) {
if (sys_keywait(wait_ms, KEYWAIT_CANCELABLE))
break;
} else {
sdl_updateScreen();
if (sys_getInputInfo())
break;
}
+72 -2
View File
@@ -1,3 +1,23 @@
/*
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
* 1998- <masaki-c@is.aist-nara.ac.jp>
* 2024 kichikuou <KichikuouChrome@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "config.h"
#include <stdio.h>
@@ -7,6 +27,16 @@
#include "xsystem35.h"
#include "modules.h"
#include "nact.h"
#include "alk.h"
#include "input.h"
#include "bgm.h"
#include "sdl_core.h"
#include "ngraph.h"
#include "cg.h"
#include "jpeg.h"
#define TDEMO_MUSIC_NO 1
#define FPS 30
static void Init() {
int p1 = getCaliValue();
@@ -16,7 +46,7 @@ static void Init() {
*var = 1;
DEBUG_COMMAND_YET("tDemo.Init %d,%d,%d,%p:", p1, p2, p3, var);
DEBUG_COMMAND("tDemo.Init %d,%d,%d,%p:", p1, p2, p3, var);
}
static void SetKeyCancelFlag() {
@@ -33,7 +63,47 @@ static void SetLoopFlag() {
}
static void Run() {
DEBUG_COMMAND_YET("tDemo.Run:");
alk_t *alk = alk_new("tDEMO.alk");
if (!alk) {
WARNING("Cannot open tDEMO.alk");
return;
}
while (sys_getInputInfo()); // wait for key up
musbgm_play(TDEMO_MUSIC_NO, 0, 100);
uint32_t start = sdl_getTicks();
while (!nact->is_quit) {
int i = (sdl_getTicks() - start) / (1000 / FPS);
if (i >= alk->datanum)
break;
cgdata *cg = jpeg_extract(alk->entries[i].data, alk->entries[i].size);
if (cg) {
gr_drawimage24(sf0, cg, 0, 0);
ags_updateFull();
cgdata_free(cg);
} else {
WARNING("Cannot decode CG %d", i);
}
int wait_ms = (i + 1) * (1000 / FPS) - (sdl_getTicks() - start);
if (wait_ms > 0) {
if (sys_keywait(wait_ms, KEYWAIT_CANCELABLE))
break;
} else {
sdl_updateScreen();
if (sys_getInputInfo())
break;
}
}
musbgm_stop(TDEMO_MUSIC_NO, 0);
alk_free(alk);
DEBUG_COMMAND("tDemo.Run:");
}
static const ModuleFunc functions[] = {
+4 -1
View File
@@ -162,11 +162,14 @@ if (EMSCRIPTEN)
-sASYNCIFY_IGNORE_INDIRECT=1
-sASYNCIFY_REMOVE=SDL_Delay
-sASYNCIFY_IMPORTS=wait_vsync
# Functions that call an asynchronous function via a function pointer must
# be added to ASYNCIFY_ADD, even if it had a direct call to another
# asynchronous function, so that the indirect call itself is instrumented.
-sASYNCIFY_ADD=commands2F60,nact_main,send_agsevent,cb_waitkey_sprite
-sALLOW_MEMORY_GROWTH=1
-sNO_EXIT_RUNTIME=1
-sEXPORTED_FUNCTIONS=_main,_malloc
-sEXPORTED_RUNTIME_METHODS=FS,IDBFS,getValue,addRunDependency,removeRunDependency
-sEXPORTED_RUNTIME_METHODS=FS,IDBFS,getValue,addRunDependency,removeRunDependency,stringToUTF8OnStack
--emit-tsd=xsystem35.d.ts
)
+1 -1
View File
@@ -77,7 +77,7 @@ static bool read_index(int volume, drifiles *d, FILE *fp) {
drifiles *dri_init(const char **file, int cnt, boolean use_mmap) {
drifiles *d = calloc(1, sizeof(drifiles));
#ifndef HAVE_MEMORY_MAPPED_FILE
#if !defined(HAVE_MEMORY_MAPPED_FILE) || defined(__EMSCRIPTEN__)
use_mmap = FALSE;
#endif
+7
View File
@@ -222,6 +222,13 @@ void send_agsevent(enum agsevent_type type, int code) {
.mousey = mousey
};
nact->ags.eventcb(&agse); // Async in emscripten
#ifdef __EMSCRIPTEN__
// HACK: this ensures that callers of this function are instrumented.
// This is necessary because ASYNCIFY_PROPAGATE_ADD does not work:
// https://github.com/emscripten-core/emscripten/issues/23015
emscripten_sleep(0);
#endif
}
void sdl_setCursorInternalLocation(int x, int y) {
+1 -1
View File
@@ -155,7 +155,7 @@ static void makeDIB(int width, int height, int depth) {
SDL_FreeSurface(sdl_dib);
}
uint32_t format;
uint32_t format = 0;
switch (depth) {
case 8:
format = SDL_PIXELFORMAT_INDEX8;
+177 -38
View File
@@ -19,78 +19,217 @@
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
#include "scenario.h"
#include "texthook.h"
#include "nact.h"
#ifdef __EMSCRIPTEN__ // ----------------------------------------------
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
void texthook_message(const char *m) {
char *utf = toUTF8(m);
EM_ASM_({ xsystem35.texthook.message(UTF8ToString($0), $1); },
utf, sl_getPage());
free(utf);
}
static struct {
int newlines;
} print;
EM_JS(void, texthook_newline, (void), {
xsystem35.texthook.newline();
});
EM_JS(void, texthook_nextpage, (void), {
xsystem35.texthook.nextpage();
});
EM_JS(void, texthook_keywait, (void), {
xsystem35.texthook.keywait();
});
#elif defined(TEXTHOOK_PRINT) // --------------------------------------
static int newlines = 0;
void texthook_message(const char *m) {
if (newlines)
static void texthook_print_message(const char *m) {
if (print.newlines)
printf("%d:", sl_getPage());
char *utf = toUTF8(m);
printf("%s", utf);
free(utf);
newlines = 0;
print.newlines = 0;
}
void texthook_newline(void) {
if (newlines < 2) {
static void texthook_print_newline(void) {
if (print.newlines < 2) {
putchar('\n');
fflush(stdout);
newlines++;
print.newlines++;
}
}
void texthook_nextpage(void) {
while (newlines < 2) {
static void texthook_print_nextpage(void) {
while (print.newlines < 2) {
putchar('\n');
fflush(stdout);
newlines++;
print.newlines++;
}
}
void texthook_keywait(void) {
texthook_newline();
static void texthook_print_keywait(void) {
texthook_print_newline();
}
#else // --------------------------------------------------------------
static struct {
char *buf;
size_t size;
size_t pos;
} copy;
static void texthook_copy_message(const char *m) {
size_t len = strlen(m);
if (copy.pos + len + 1 > copy.size) {
copy.size += min(copy.pos + len + 1, 100);
copy.buf = realloc(copy.buf, copy.size);
}
strcpy(copy.buf + copy.pos, m);
copy.pos += len;
}
static void texthook_copy_newline(void) {
if (copy.pos == 0)
return;
texthook_message("\n");
}
static void texthook_copy_to_clipboard(void) {
char *utf = toUTF8(copy.buf);
SDL_SetClipboardText(utf);
free(utf);
copy.pos = 0;
}
static void texthook_copy_nextpage(void) {
if (copy.pos > 0)
texthook_copy_to_clipboard();
}
static void texthook_copy_keywait(void) {
if (copy.pos > 0)
texthook_copy_to_clipboard();
}
static enum texthook_mode mode = TEXTHOOK_NONE;
static int *suppression_list = NULL;
static enum {
INIT,
SUPPRESSING,
EMITTING,
} suppression_state = INIT;
void texthook_set_mode(enum texthook_mode m) {
mode = m;
}
// suppressions is a comma-separated list of page numbers to suppress.
EMSCRIPTEN_KEEPALIVE
void texthook_set_suppression_list(const char *suppressions) {
if (suppression_list != NULL) {
free(suppression_list);
}
if (!suppressions) {
suppression_list = NULL;
return;
}
int count = 1;
for (const char *p = suppressions; *p; p++) {
if (*p == ',')
count++;
}
suppression_list = (int*)malloc((count + 1) * sizeof(int));
int index = 0;
char *buf = strdup(suppressions);
char *token = strtok(buf, ",");
while (token) {
suppression_list[index++] = atoi(token);
token = strtok(NULL, ",");
}
free(buf);
suppression_list[index] = -1; // sentinel
}
static void set_suppression_state(void) {
int page = sl_getPage();
if (suppression_list != NULL) {
for (int i = 0; suppression_list[i] != -1; i++) {
if (page == suppression_list[i]) {
suppression_state = SUPPRESSING;
return;
}
}
}
suppression_state = EMITTING;
}
void texthook_message(const char *m) {
if (suppression_state == INIT)
set_suppression_state();
if (suppression_state == SUPPRESSING)
return;
#ifdef __EMSCRIPTEN__
char *utf = toUTF8(m);
EM_ASM(xsystem35.texthook.message(UTF8ToString($0)), utf);
free(utf);
#else
switch (mode) {
case TEXTHOOK_NONE:
break;
case TEXTHOOK_PRINT:
texthook_print_message(m);
break;
case TEXTHOOK_COPY:
texthook_copy_message(m);
break;
}
#endif
}
void texthook_newline(void) {
#ifdef __EMSCRIPTEN__
EM_ASM(xsystem35.texthook.newline());
#else
switch (mode) {
case TEXTHOOK_NONE:
break;
case TEXTHOOK_PRINT:
texthook_print_newline();
break;
case TEXTHOOK_COPY:
texthook_copy_newline();
break;
}
#endif
suppression_state = INIT;
}
void texthook_nextpage(void) {
#ifdef __EMSCRIPTEN__
EM_ASM(xsystem35.texthook.nextpage());
#else
switch (mode) {
case TEXTHOOK_NONE:
break;
case TEXTHOOK_PRINT:
texthook_print_nextpage();
break;
case TEXTHOOK_COPY:
texthook_copy_nextpage();
break;
}
#endif
suppression_state = INIT;
}
void texthook_keywait(void) {
#ifdef __EMSCRIPTEN__
EM_ASM(xsystem35.texthook.keywait());
#else
switch (mode) {
case TEXTHOOK_NONE:
break;
case TEXTHOOK_PRINT:
texthook_print_keywait();
break;
case TEXTHOOK_COPY:
texthook_copy_keywait();
break;
}
#endif
suppression_state = INIT;
}
#endif // -------------------------------------------------------------
+8
View File
@@ -20,6 +20,14 @@
#ifndef __TEXTHOOK__
#define __TEXTHOOK__
enum texthook_mode {
TEXTHOOK_NONE,
TEXTHOOK_PRINT,
TEXTHOOK_COPY,
};
void texthook_set_mode(enum texthook_mode mode);
void texthook_set_suppression_list(const char *suppressions);
void texthook_message(const char *m);
void texthook_newline(void);
void texthook_nextpage(void);
+1
View File
@@ -36,6 +36,7 @@ static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
void win_alloc_console(void) {
AllocConsole();
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
freopen("CONIN$", "r", stdin);
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
+5
View File
@@ -28,6 +28,7 @@
#include "sdl_private.h"
#include "resources.h"
#include "msgskip.h"
#include "texthook.h"
static HMENU hmenu;
@@ -119,6 +120,10 @@ void win_menu_onSysWMEvent(SDL_SysWMmsg* msg) {
if (toggle_menu_item(ID_OPTION_MOUSE_MOVE, &checked))
nact->ags.mouse_warp_enabled = checked;
break;
case ID_OPTION_AUTO_COPY:
if (toggle_menu_item(ID_OPTION_AUTO_COPY, &checked))
texthook_set_mode(checked ? TEXTHOOK_COPY : TEXTHOOK_NONE);
break;
case ID_MSGSKIP:
msgskip_activate(!msgskip_isActivated());
break;
+1
View File
@@ -33,6 +33,7 @@
#define ID_SCREEN_FULL 40012
#define ID_SCREEN_INTEGER_SCALING 40013
#define ID_OPTION_MOUSE_MOVE 40021
#define ID_OPTION_AUTO_COPY 40022
#define ID_MSGSKIP 40030
struct SDL_RWops;
+2
View File
@@ -32,6 +32,7 @@ IDR_MENU1 MENU DISCARDABLE
POPUP "Option"
{
MENUITEM "Mouse Movement", ID_OPTION_MOUSE_MOVE
MENUITEM "Auto Copy Text to Clipboard", ID_OPTION_AUTO_COPY
}
MENUITEM "MsgSkip[off]", ID_MSGSKIP
}
@@ -71,6 +72,7 @@ IDR_MENU1 MENU DISCARDABLE
POPUP "オプション"
{
MENUITEM "マウスカーソル移動", ID_OPTION_MOUSE_MOVE
MENUITEM "テキストを自動コピー", ID_OPTION_AUTO_COPY
}
MENUITEM "文字送り[off]", ID_MSGSKIP
}
+65 -27
View File
@@ -72,6 +72,7 @@
#include "filecheck.h"
#include "s39init.h"
#include "msgskip.h"
#include "texthook.h"
static char *gameResourceFile = "xsystem35.gr";
static void sys35_usage(boolean verbose);
@@ -111,45 +112,46 @@ static void sys35_usage(boolean verbose) {
}
puts("Usage: xsystem35 [OPTIONS]\n");
puts("OPTIONS");
puts(" -gamefile file : set game resource file to 'file'");
puts(" -game game : enable game-specific hacks");
puts(" -savedir dir : directory to save game state files");
puts(" -saveformat fmt : save file format. 'xsystem35', 'system36' or 'system39' (default)");
puts(" -renderer name : set rendering driver name to 'name'");
puts(" -playlist file : load CD playlist from 'file'");
puts(" -devmidi device : set midi device name to 'device'");
puts(" -gamefile file : set game resource file to 'file'");
puts(" -game game : enable game-specific hacks");
puts(" -savedir dir : directory to save game state files");
puts(" -saveformat fmt : save file format. 'xsystem35', 'system36' or 'system39' (default)");
puts(" -renderer name : set rendering driver name to 'name'");
puts(" -playlist file : load CD playlist from 'file'");
puts(" -texthook mode : text hook mode. 'none' (default), 'print' or 'copy'");
puts(" -texthook_suppress list : suppress text hook on specified pages");
puts(" -M? : select output midi methos");
puts(" -M? : select output midi methos");
#ifdef ENABLE_MIDI_SDLMIXER
puts(" -Me : SDL_mixer midi player");
puts(" -Me : SDL_mixer midi player");
#endif
#ifdef ENABLE_MIDI_PORTMIDI
puts(" -Mp? : ALSA (via PortMidi) (?:devicenumber)");
puts(" -Mp? : ALSA (via PortMidi) (?:devicenumber)");
#endif
puts(" -M0 : Disable MIDI output");
puts(" -M0 : Disable MIDI output");
puts(" -devjoy device : joystick device index (0-)");
puts(" -devjoy device : joystick device index (0-)");
puts(" -ttfont_mincho : set TrueType font for mincho");
puts(" -ttfont_gothic : set TrueType font for mincho");
puts(" -ttfont_mincho : set TrueType font for mincho");
puts(" -ttfont_gothic : set TrueType font for mincho");
#ifdef DEBUG
puts(" -debuglv # : logging level");
puts(" : 1: warings");
puts(" : 2: unimplemented commands");
puts(" : 5: command trace");
puts(" : 6: message trace");
puts(" -debuglv # : logging level");
puts(" : 1: warings");
puts(" : 2: unimplemented commands");
puts(" : 5: command trace");
puts(" : 6: message trace");
#endif
#ifdef ENABLE_DEBUGGER
puts(" -debug : start with debugger");
puts(" -debug : start with debugger");
#endif
puts(" -noantialias : never use antialiased string");
puts(" -fullscreen : start with fullscreen");
puts(" -integerscale : use integer scaling when resizing");
puts(" -noimagecursor : disable image cursor");
puts(" -version : show version");
puts(" -h : show this message");
puts(" --help : show this message");
puts(" -noantialias : never use antialiased string");
puts(" -fullscreen : start with fullscreen");
puts(" -integerscale : use integer scaling when resizing");
puts(" -noimagecursor : disable image cursor");
puts(" -version : show version");
puts(" -h : show this message");
puts(" --help : show this message");
exit(1);
}
@@ -242,6 +244,19 @@ static void sys_reset(void) {
s39ain_reset(&nact->ain);
}
static bool set_texthook_mode(const char *mode) {
if (!strcasecmp(mode, "none")) {
texthook_set_mode(TEXTHOOK_NONE);
} else if (!strcasecmp(mode, "print")) {
texthook_set_mode(TEXTHOOK_PRINT);
} else if (!strcasecmp(mode, "copy")) {
texthook_set_mode(TEXTHOOK_COPY);
} else {
return false;
}
return true;
}
static void sys35_ParseOption(int *argc, char **argv) {
int i;
FILE *fp;
@@ -282,6 +297,17 @@ static void sys35_ParseOption(int *argc, char **argv) {
if (argv[i + 1] != NULL) {
muscd_set_playlist(argv[i + 1]);
}
} else if (0 == strcmp(argv[i], "-texthook")) {
if (argv[i + 1] != NULL) {
if (!set_texthook_mode(argv[i + 1])) {
fprintf(stderr, "xsystem35: Invalid texthook mode '%s'\n\n", argv[i + 1]);
sys35_usage(FALSE);
}
}
} else if (0 == strcmp(argv[i], "-texthook_suppress")) {
if (argv[i + 1] != NULL) {
texthook_set_suppression_list(argv[i + 1]);
}
} else if (0 == strncmp(argv[i], "-M", 2)) {
int subdev = 0;
if (argv[i][3] != '\0') {
@@ -415,6 +441,18 @@ static void check_profile() {
if (param) {
save_setFormat(param);
}
/* text hook */
param = get_profile("texthook");
if (param) {
if (!set_texthook_mode(param)) {
sys_error("Invalid texthook mode '%s'", param);
}
}
param = get_profile("texthook_suppress");
if (param) {
texthook_set_suppression_list(param);
}
}
#ifdef HAVE_SIGACTION
+13
View File
@@ -92,6 +92,19 @@
#game: rance4_v2
# Text hook mode.
# none : no text hook (default)
# print : print text to console
# copy : automatically copy text to clipboard (for use with text hooker tools)
#texthook: none
# Suppress text hook on specified scenario pages.
# Used to suppress system messages, etc.
# The example below is for Kichikuou Rance.
#texthook_suppress: 7,11,15,16,17,18,23,24,32,33,197
# Enables the ZB command (change the font weight). It is disabled by default
# because it makes the text too bold with our default fonts.
# This option is experimental and may be removed in the future. See