From 3c5ae18ce1d2d12af99b4c49086452a1af48d651 Mon Sep 17 00:00:00 2001 From: DCurrent Date: Tue, 18 Aug 2026 21:05:04 -0400 Subject: [PATCH 1/4] Fix misnamed onmovascript -> omoveyscript --- engine/openbor.c | 4 ++-- engine/source/openborscript/commands.c | 2 +- engine/source/openborscript/commands.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/openbor.c b/engine/openbor.c index 0a9b0eef0..5fb4b86ea 100644 --- a/engine/openbor.c +++ b/engine/openbor.c @@ -17265,8 +17265,8 @@ s_model *load_cached_model(char *name, char *owner, char unload) case CMD_MODEL_ONMOVEZSCRIPT: pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovez_script, "onmovezscript", filename, 1, 0); break; - case CMD_MODEL_ONMOVEASCRIPT: - pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovea_script, "onmoveascript", filename, 1, 0); + case CMD_MODEL_ONMOVEYSCRIPT: + pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->onmovea_script, "onmoveyscript", filename, 1, 0); break; case CMD_MODEL_ONDEATHSCRIPT: pos += lcmHandleCommandScripts(&arglist, buf + pos, newchar->scripts->ondeath_script, "ondeathscript", filename, 1, 0); diff --git a/engine/source/openborscript/commands.c b/engine/source/openborscript/commands.c index e1b7cc843..88dd1d77a 100644 --- a/engine/source/openborscript/commands.c +++ b/engine/source/openborscript/commands.c @@ -514,7 +514,7 @@ List *createModelCommandList(void) LIST_ADD(CMD_MODEL_ONFALLSCRIPT, "onfallscript"); LIST_ADD(CMD_MODEL_ONKILLSCRIPT, "onkillscript"); LIST_ADD(CMD_MODEL_ONMODELCOPYSCRIPT, "onmodelcopyscript"); - LIST_ADD(CMD_MODEL_ONMOVEASCRIPT, "onmoveascript"); + LIST_ADD(CMD_MODEL_ONMOVEYSCRIPT, "onmoveyscript"); LIST_ADD(CMD_MODEL_ONMOVEXSCRIPT, "onmovexscript"); LIST_ADD(CMD_MODEL_ONMOVEZSCRIPT, "onmovezscript"); LIST_ADD(CMD_MODEL_ONPAINSCRIPT, "onpainscript"); diff --git a/engine/source/openborscript/commands.h b/engine/source/openborscript/commands.h index b8c9d86bd..297025ed5 100644 --- a/engine/source/openborscript/commands.h +++ b/engine/source/openborscript/commands.h @@ -621,7 +621,7 @@ typedef enum modelCommand CMD_MODEL_ONFALLSCRIPT, CMD_MODEL_ONKILLSCRIPT, CMD_MODEL_ONMODELCOPYSCRIPT, - CMD_MODEL_ONMOVEASCRIPT, + CMD_MODEL_ONMOVEYSCRIPT, CMD_MODEL_ONMOVEXSCRIPT, CMD_MODEL_ONMOVEZSCRIPT, CMD_MODEL_ONPAINSCRIPT, From 343fcf8c1cea3583888234f2c94c790210caee39 Mon Sep 17 00:00:00 2001 From: DCurrent Date: Tue, 18 Aug 2026 23:39:47 -0400 Subject: [PATCH 2/4] Restore hardware YUV movie presentation Route legacy playwebm() playback through the hardware-accelerated YUV output path and preserve the WebM source display dimensions. Add movie_draw_to_yuv() for the channel-based movie API while retaining pause, seek, speed, repeat, size, and stop controls. Restore the configured game video mode when YUV playback ends and preserve native-resolution screenshot capture. --- engine/android/app/jni/openbor/video.c | 7 ++- engine/openbor.c | 33 +++++++++--- engine/sdl/video.c | 5 ++ engine/sdl/video.h | 2 +- engine/source/openborscript/index.c | 6 +++ engine/source/openborscript/movie.c | 27 ++++++++++ engine/source/openborscript/movie.h | 1 + engine/source/webmlib/movie_playback.c | 70 ++++++++++++++++++++++++++ engine/source/webmlib/movie_playback.h | 1 + engine/version.tmp | 6 +-- 10 files changed, 145 insertions(+), 13 deletions(-) diff --git a/engine/android/app/jni/openbor/video.c b/engine/android/app/jni/openbor/video.c index 78fc12d09..cbc708b92 100644 --- a/engine/android/app/jni/openbor/video.c +++ b/engine/android/app/jni/openbor/video.c @@ -454,6 +454,11 @@ int video_set_mode(s_videomodes videomodes) return 1; } +int video_restore_mode(void) +{ + return video_set_mode(stored_videomodes); +} + void video_fullscreen_flip() { } @@ -598,4 +603,4 @@ int video_current_refresh_rate() if (SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(window), &display_mode) != 0) return 60; return display_mode.refresh_rate; -} \ No newline at end of file +} diff --git a/engine/openbor.c b/engine/openbor.c index 5fb4b86ea..9d8b2a7e4 100644 --- a/engine/openbor.c +++ b/engine/openbor.c @@ -51879,6 +51879,7 @@ int playwebm(const char *path, int noskip) int retval = 1; int source_id = -1; s_movie_playback *playback = NULL; + s_screen *screenshot_frame = NULL; movie_playback_stop_all(); source_id = movie_source_load(path, MOVIE_LOADING_STREAM); @@ -51900,8 +51901,10 @@ int playwebm(const char *path, int noskip) goto quit; } - clearscreen(vscreen); - video_copy_screen(vscreen); + /* Legacy playback uses the WebM display size and hardware YUV output. */ + movie_playback_set_width(playback, MOVIE_SIZE_NATIVE); + movie_playback_set_height(playback, MOVIE_SIZE_NATIVE); + while(playback->active) { int interrupt_requested; int present_frame; @@ -51918,8 +51921,7 @@ int playwebm(const char *path, int noskip) } present_frame = playback->frame_dirty; if(present_frame) { - clearscreen(vscreen); - if(!movie_playback_draw_to_screen(vscreen, playback->index)) { + if(!movie_playback_draw_to_yuv(playback->index)) { retval = 0; break; } @@ -51927,15 +51929,30 @@ int playwebm(const char *path, int noskip) if(playback->current_frame && !noscreenshot && (bothnewkeys & FLAG_SCREENSHOT)) { - screenshot(vscreen, getpal, 1); - } - if(present_frame) { - video_copy_screen(vscreen); + if(!screenshot_frame || + screenshot_frame->width != playback->current_frame->width || + screenshot_frame->height != playback->current_frame->height) { + if(screenshot_frame) { + freescreen(&screenshot_frame); + } + screenshot_frame = allocscreen( + playback->current_frame->width, + playback->current_frame->height, + PIXEL_32 + ); + } + if(screenshot_frame) { + yuv_to_rgb(playback->current_frame, screenshot_frame); + screenshot(screenshot_frame, NULL, 0); + } } usleep(1000); } quit: + if(screenshot_frame) { + freescreen(&screenshot_frame); + } if(playback && playback->failed && retval > 0) { retval = 0; } diff --git a/engine/sdl/video.c b/engine/sdl/video.c index 5b5e8db1c..9b9b48135 100644 --- a/engine/sdl/video.c +++ b/engine/sdl/video.c @@ -201,6 +201,11 @@ int video_set_mode(s_videomodes videomodes) return 1; } +int video_restore_mode(void) +{ + return video_set_mode(stored_videomodes); +} + void video_fullscreen_flip() { int restore_yuv = yuv_mode; diff --git a/engine/sdl/video.h b/engine/sdl/video.h index c861c4827..4d45868b3 100644 --- a/engine/sdl/video.h +++ b/engine/sdl/video.h @@ -24,6 +24,7 @@ void FramerateDelay(); // Frees all VESA shit when returning to textmode int video_set_mode(s_videomodes); +int video_restore_mode(void); int video_copy_screen(s_screen*); void video_clearscreen(); void video_fullscreen_flip(); @@ -39,4 +40,3 @@ int video_display_yuv_frame(void); int video_current_refresh_rate(); #endif - diff --git a/engine/source/openborscript/index.c b/engine/source/openborscript/index.c index 205a60c78..d1330cec1 100644 --- a/engine/source/openborscript/index.c +++ b/engine/source/openborscript/index.c @@ -941,6 +941,10 @@ const char *Script_GetFunctionName(void *functionRef) { return "movie_draw_to_screen"; } + else if (functionRef == ((void *)openbor_movie_draw_to_yuv)) + { + return "movie_draw_to_yuv"; + } else if (functionRef == ((void *)openbor_movie_set_sound_channel)) { return "movie_set_sound_channel"; @@ -1807,6 +1811,8 @@ void Script_LoadSystemFunctions() (void *)openbor_movie_play, "movie_play"); List_InsertAfter(&theFunctionList, (void *)openbor_movie_draw_to_screen, "movie_draw_to_screen"); + List_InsertAfter(&theFunctionList, + (void *)openbor_movie_draw_to_yuv, "movie_draw_to_yuv"); List_InsertAfter(&theFunctionList, (void *)openbor_movie_set_sound_channel, "movie_set_sound_channel"); List_InsertAfter(&theFunctionList, diff --git a/engine/source/openborscript/movie.c b/engine/source/openborscript/movie.c index dc4140bd8..26c946498 100644 --- a/engine/source/openborscript/movie.c +++ b/engine/source/openborscript/movie.c @@ -276,6 +276,32 @@ HRESULT openbor_movie_draw_to_screen( return E_FAIL; } +/* +* Caskey, Damon V. +* 2026-08-18 +* +* Present a movie channel directly through the exclusive hardware YUV path. +*/ +HRESULT openbor_movie_draw_to_yuv( + ScriptVariant **varlist, + ScriptVariant **pretvar, + int paramCount +) +{ + LONG channel; + + *pretvar = NULL; + if(paramCount < 1 || + FAILED(ScriptVariant_IntegerValue(varlist[0], &channel)) || + channel < 0 || + (unsigned int)channel >= MOVIE_CHANNEL_COUNT || + !movie_playback_draw_to_yuv((int)channel)) { + printf("\nScript error: movie_draw_to_yuv(int channel). Movie channel is invalid.\n"); + return E_FAIL; + } + return S_OK; +} + /* * Caskey, Damon V. * 2026-08-12 @@ -563,6 +589,7 @@ MOVIE_UNSUPPORTED(openbor_movie_load) MOVIE_UNSUPPORTED(openbor_movie_unload) MOVIE_UNSUPPORTED(openbor_movie_play) MOVIE_UNSUPPORTED(openbor_movie_draw_to_screen) +MOVIE_UNSUPPORTED(openbor_movie_draw_to_yuv) MOVIE_UNSUPPORTED(openbor_movie_set_sound_channel) MOVIE_UNSUPPORTED(openbor_movie_stop) MOVIE_UNSUPPORTED(openbor_movie_get_channel_object) diff --git a/engine/source/openborscript/movie.h b/engine/source/openborscript/movie.h index 0af2255e5..253980c91 100644 --- a/engine/source/openborscript/movie.h +++ b/engine/source/openborscript/movie.h @@ -32,6 +32,7 @@ HRESULT openbor_movie_load(ScriptVariant **varlist, ScriptVariant **pretvar, int HRESULT openbor_movie_unload(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount); HRESULT openbor_movie_play(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount); HRESULT openbor_movie_draw_to_screen(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount); +HRESULT openbor_movie_draw_to_yuv(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount); HRESULT openbor_movie_set_sound_channel(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount); HRESULT openbor_movie_stop(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount); HRESULT openbor_movie_get_channel_object(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount); diff --git a/engine/source/webmlib/movie_playback.c b/engine/source/webmlib/movie_playback.c index 51da1a2ae..c248cf248 100644 --- a/engine/source/webmlib/movie_playback.c +++ b/engine/source/webmlib/movie_playback.c @@ -33,6 +33,7 @@ static void *movie_cache_allocate_nonfatal(size_t size) #include "sound_channel.h" #include "soundmix.h" #include "timer.h" +#include "video.h" #include "vidplay.h" #include "movie_playback.h" @@ -98,6 +99,17 @@ typedef struct s_movie_playback_pool { static s_movie_playback_pool movie_playback_pool; +/* +* Caskey, Damon V. +* 2026-08-18 +* +* Track exclusive hardware YUV presentation independently from decoder +* channels. The overlay replaces the normal game texture until playback +* ends, at which point the configured game video mode is restored. +*/ +static int movie_yuv_channel = -1; +static yuv_video_mode movie_yuv_mode; + /* * Caskey, Damon V. * 2026-08-17 @@ -1124,6 +1136,11 @@ void movie_playback_stop(s_movie_playback *playback) if(channel < 0) { return; } + if(movie_yuv_channel == channel) { + movie_yuv_channel = -1; + memset(&movie_yuv_mode, 0, sizeof(movie_yuv_mode)); + video_restore_mode(); + } movie_playback_pool.active_mask &= ~(UINT64_C(1) << channel); movie_playback_reset_record(&movie_playback_pool.channel[channel]); } @@ -1601,6 +1618,59 @@ bool movie_playback_draw_to_screen(s_screen *screen, int channel) return movie_playback_render_to(playback, screen); } +/* +* Caskey, Damon V. +* 2026-08-18 +* +* Present one retained decoder frame through the hardware YUV path. This is +* an exclusive final-output operation, not a compositing operation. Width and +* height select the overlay's logical display size; zero and native both use +* the WebM display dimensions. Playback timing and transport remain owned by +* the channel and therefore work identically to screen composition. +*/ +bool movie_playback_draw_to_yuv(int channel) +{ + s_movie_playback *playback; + yuv_video_mode mode; + + if(!movie_playback_pool.initialized || + channel < 0 || + (unsigned int)channel >= MOVIE_CHANNEL_COUNT) { + return false; + } + playback = &movie_playback_pool.channel[channel]; + if(!playback->active || + !(movie_playback_pool.active_mask & (UINT64_C(1) << channel))) { + return false; + } + if(!playback->current_frame) { + return true; + } + + mode = playback->video_mode; + if(playback->width && playback->width != MOVIE_SIZE_NATIVE) { + mode.display_width = (int)playback->width; + } + if(playback->height && playback->height != MOVIE_SIZE_NATIVE) { + mode.display_height = (int)playback->height; + } + if(movie_yuv_channel != channel || + memcmp(&movie_yuv_mode, &mode, sizeof(mode))) { + if(!video_setup_yuv_overlay(&mode)) { + return false; + } + movie_yuv_channel = channel; + movie_yuv_mode = mode; + } + if(!video_prepare_yuv_frame(playback->current_frame) || + !video_display_yuv_frame()) { + return false; + } + playback->frame_dirty = 0; + playback->current_frame_presented = 1; + return true; +} + /* * Caskey, Damon V. * 2026-08-12 diff --git a/engine/source/webmlib/movie_playback.h b/engine/source/webmlib/movie_playback.h index d90c3a4f7..c79c82f9d 100644 --- a/engine/source/webmlib/movie_playback.h +++ b/engine/source/webmlib/movie_playback.h @@ -96,6 +96,7 @@ bool movie_playback_get_snapshot( uint64_t movie_playback_get_active_mask(void); void movie_playback_update(int interrupt_requested); bool movie_playback_draw_to_screen(s_screen *screen, int channel); +bool movie_playback_draw_to_yuv(int channel); void movie_playback_stop(s_movie_playback *playback); void movie_playback_stop_all(void); diff --git a/engine/version.tmp b/engine/version.tmp index a8ecaa12f..6e0d134c8 100644 --- a/engine/version.tmp +++ b/engine/version.tmp @@ -12,9 +12,9 @@ #define VERSION_NAME "OpenBOR" #define VERSION_MAJOR "4" #define VERSION_MINOR "0" -#define VERSION_BUILD "7982" -#define VERSION_BUILD_INT 7982 -#define VERSION_COMMIT "ae48540" +#define VERSION_BUILD "7989" +#define VERSION_BUILD_INT 7989 +#define VERSION_COMMIT "3c5ae18" #define VERSION "v"VERSION_MAJOR"."VERSION_MINOR" Build "VERSION_BUILD" (commit hash: "VERSION_COMMIT")" #endif From be2e2dc507c47796774b7344bf1af2bfc98d602e Mon Sep 17 00:00:00 2001 From: DCurrent Date: Wed, 19 Aug 2026 00:45:28 -0400 Subject: [PATCH 3/4] Fix special input flag clearing Correct player_check_special() to clear each special move's actual trigger flags instead of always clearing FLAG_SPECIAL. This prevents inputs such as Attack 4 from remaining active after the special completes, which could leave the player stuck running or produce similar control glitches. Fixes #362. --- engine/openbor.c | 39 +++++++++++++++------------------------ engine/version.tmp | 6 +++--- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/engine/openbor.c b/engine/openbor.c index 9d8b2a7e4..908cde477 100644 --- a/engine/openbor.c +++ b/engine/openbor.c @@ -43905,8 +43905,7 @@ int player_check_special() { case AJSPECIAL_KEY_SPECIAL: - if (player_keys & FLAG_SPECIAL) - { + if (player_keys & FLAG_SPECIAL) { thekey = FLAG_SPECIAL; } @@ -43914,37 +43913,32 @@ int player_check_special() case AJSPECIAL_KEY_DOUBLE: - if (!validanim(self, ANI_BLOCK) && player_keys & FLAG_SPECIAL) - { - thekey = FLAG_SPECIAL; - } - else if (player_keys & FLAG_JUMP && player_keys & FLAG_ATTACK) - { + if (!validanim(self, ANI_BLOCK) && player_keys & FLAG_SPECIAL) { thekey = FLAG_SPECIAL; + + } else if (player_keys & FLAG_JUMP && player_keys & FLAG_ATTACK) { + thekey = FLAG_JUMP | FLAG_ATTACK; } break; case AJSPECIAL_KEY_ATTACK2: - if (player_keys & FLAG_ATTACK2) - { - thekey = FLAG_SPECIAL; + if (player_keys & FLAG_ATTACK2) { + thekey = FLAG_ATTACK2; } break; case AJSPECIAL_KEY_ATTACK3: - if (player_keys & FLAG_ATTACK3) - { - thekey = FLAG_SPECIAL; + if (player_keys & FLAG_ATTACK3) { + thekey = FLAG_ATTACK3; } break; case AJSPECIAL_KEY_ATTACK4: - if (player_keys & FLAG_ATTACK4) - { - thekey = FLAG_SPECIAL; + if (player_keys & FLAG_ATTACK4) { + thekey = FLAG_ATTACK4; } break; @@ -43952,19 +43946,16 @@ int player_check_special() thekey = 0; } - if (!thekey) - { + if (!thekey) { return 0; } - if(check_special()) - { + if(check_special()) { self->stalltime = 0; player[self->playerindex].playkeys &= ~thekey; return 1; - } - else - { + + } else { return 0; } } diff --git a/engine/version.tmp b/engine/version.tmp index 6e0d134c8..8eab73595 100644 --- a/engine/version.tmp +++ b/engine/version.tmp @@ -12,9 +12,9 @@ #define VERSION_NAME "OpenBOR" #define VERSION_MAJOR "4" #define VERSION_MINOR "0" -#define VERSION_BUILD "7989" -#define VERSION_BUILD_INT 7989 -#define VERSION_COMMIT "3c5ae18" +#define VERSION_BUILD "7990" +#define VERSION_BUILD_INT 7990 +#define VERSION_COMMIT "343fcf8" #define VERSION "v"VERSION_MAJOR"."VERSION_MINOR" Build "VERSION_BUILD" (commit hash: "VERSION_COMMIT")" #endif From d8fe6252b2815068cb6904d7a472e5e5349b9f99 Mon Sep 17 00:00:00 2001 From: DCurrent Date: Wed, 19 Aug 2026 00:56:44 -0400 Subject: [PATCH 4/4] Isolate pending select models by player Track each player's pending character model separately during select-out animations. Previously, all player slots shared one pending model pointer. Input from another player could overwrite an active selection transition, causing one player to receive another player's character choice. Fixes #363 --- engine/openbor.c | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/engine/openbor.c b/engine/openbor.c index 908cde477..38a18d1b3 100644 --- a/engine/openbor.c +++ b/engine/openbor.c @@ -43905,7 +43905,8 @@ int player_check_special() { case AJSPECIAL_KEY_SPECIAL: - if (player_keys & FLAG_SPECIAL) { + if (player_keys & FLAG_SPECIAL) + { thekey = FLAG_SPECIAL; } @@ -43913,31 +43914,36 @@ int player_check_special() case AJSPECIAL_KEY_DOUBLE: - if (!validanim(self, ANI_BLOCK) && player_keys & FLAG_SPECIAL) { + if (!validanim(self, ANI_BLOCK) && player_keys & FLAG_SPECIAL) + { thekey = FLAG_SPECIAL; - - } else if (player_keys & FLAG_JUMP && player_keys & FLAG_ATTACK) { + } + else if (player_keys & FLAG_JUMP && player_keys & FLAG_ATTACK) + { thekey = FLAG_JUMP | FLAG_ATTACK; } break; case AJSPECIAL_KEY_ATTACK2: - if (player_keys & FLAG_ATTACK2) { + if (player_keys & FLAG_ATTACK2) + { thekey = FLAG_ATTACK2; } break; case AJSPECIAL_KEY_ATTACK3: - if (player_keys & FLAG_ATTACK3) { + if (player_keys & FLAG_ATTACK3) + { thekey = FLAG_ATTACK3; } break; case AJSPECIAL_KEY_ATTACK4: - if (player_keys & FLAG_ATTACK4) { + if (player_keys & FLAG_ATTACK4) + { thekey = FLAG_ATTACK4; } break; @@ -43946,16 +43952,19 @@ int player_check_special() thekey = 0; } - if (!thekey) { + if (!thekey) + { return 0; } - if(check_special()) { + if(check_special()) + { self->stalltime = 0; player[self->playerindex].playkeys &= ~thekey; return 1; - - } else { + } + else + { return 0; } } @@ -52641,8 +52650,7 @@ static void load_select_screen_info(s_savelevel *save) int selectplayer(int *players, char *filename, int useSavedGame) { s_model *tempmodel; - s_model *model_old = NULL; - s_model *model_new = NULL; + s_model *model_new[MAX_PLAYERS] = { NULL }; int i; int exit = 0; int escape = 0; @@ -52993,10 +53001,11 @@ int selectplayer(int *players, char *filename, int useSavedGame) // Transition from select (player selected another model, and the // select out transition is now finished). Repeat of left/right key // logic below and probably needs consolidation. - if (example[i]->animnum == ANI_SELECTOUT && model_new) + if (example[i]->animnum == ANI_SELECTOUT && model_new[i]) { // Apply new model. - ent_set_model(example[i], model_new->name, 0); + ent_set_model(example[i], model_new[i]->name, 0); + model_new[i] = NULL; // Copy example model name to player name variable. strcpy(player[i].name, example[i]->model->name); @@ -53082,17 +53091,15 @@ int selectplayer(int *players, char *filename, int useSavedGame) } // Get model in use right now. - model_old = example[i]->model; - // Let's get the new model. Left key = previous model // in cycle. Right key = next. if ((player[i].newkeys & FLAG_MOVELEFT)) { - model_new = prevplayermodeln(model_old, i); + model_new[i] = prevplayermodeln(example[i]->model, i); } else { - model_new = nextplayermodeln(model_old, i); + model_new[i] = nextplayermodeln(example[i]->model, i); } // Do we have a select out transition? If so play it here. @@ -53104,7 +53111,8 @@ int selectplayer(int *players, char *filename, int useSavedGame) else { // Apply new model. - ent_set_model(example[i], model_new->name, 0); + ent_set_model(example[i], model_new[i]->name, 0); + model_new[i] = NULL; // Copy example model name to player name variable. strcpy(player[i].name, example[i]->model->name);