From 24224e0d2bfe47e8173580a703a3c91018f68cd2 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 8 Apr 2023 15:01:30 +0900 Subject: [PATCH] Merge some changes from SysEng 0.7 (for Gakuen Senki port) --- src/sys/ags.cpp | 48 +++++++++++---- src/sys/ags.h | 3 +- src/sys/ags_draw.cpp | 2 + src/sys/ags_vsp.cpp | 23 +++++++- src/sys/crc32.h | 2 + src/sys/nact.h | 3 + src/sys/nact_crc32.cpp | 2 + src/sys/nact_input.cpp | 2 +- src/sys/nact_sys1.cpp | 131 ++++++++++++++++++++++++++++++++++++++--- src/win/nact_win.cpp | 2 - 10 files changed, 192 insertions(+), 26 deletions(-) diff --git a/src/sys/ags.cpp b/src/sys/ags.cpp index aa5d86d..53a9adb 100644 --- a/src/sys/ags.cpp +++ b/src/sys/ags.cpp @@ -38,8 +38,19 @@ static SDL_Surface* display_surface; AGS::AGS(NACT* parent, const Config& config) : nact(parent), dirty(false) { - SDL_RenderSetLogicalSize(g_renderer, 640, 400); - sdlTexture = SDL_CreateTexture(g_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, 640, 400); // TOOD: pixelformat? + // 画面サイズ + if (nact->crc32_a == CRC32_GAKUEN || nact->crc32_a == CRC32_GAKUEN_ENG) { + screen_width = 512; + screen_height = 424; + } else { + screen_width = 640; + screen_height = 400; + } + scroll = screen_height; + + SDL_SetWindowSize(g_window, screen_width, screen_height); + SDL_RenderSetLogicalSize(g_renderer, screen_width, screen_height); + sdlTexture = SDL_CreateTexture(g_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, screen_width, screen_height); // TOOD: pixelformat? // DIBSection 8bpp * 3 (表, 裏, メニュー) for(int i = 0; i < 3; i++) { @@ -232,9 +243,11 @@ AGS::AGS(NACT* parent, const Config& config) : nact(parent), dirty(false) SET_MENU(i, 452, 14, 627, 214, true); break; case CRC32_VAMPIRE: - case CRC32_VAMPIRE_ENG: SET_TEXT(i, 8, 255, 615, 383, false); SET_MENU(i, 448, 11, 615, 224, false); + case CRC32_VAMPIRE_ENG: + SET_TEXT(i, 8, 255, 615, 383, false); + SET_MENU(i, 448, 11, 615, 234, false); break; case CRC32_YAKATA: SET_TEXT(i, 48, 288, 594, 393, false); @@ -252,6 +265,15 @@ AGS::AGS(NACT* parent, const Config& config) : nact(parent), dirty(false) SET_TEXT(i, 104, 304, 620, 382, false); SET_MENU(i, 420, 28, 620, 244, true); break; + case CRC32_GAKUEN: + case CRC32_GAKUEN_ENG: + SET_TEXT(i, 8, 260, 505, 384, false); + if (i == 1) { + SET_MENU(i, 128, 32, 337, 178, true); + } else { + SET_MENU(i, 288, 30, 433, 210, true); + } + break; default: SET_TEXT(i, 8, 311, 623, 391, true); SET_MENU(i, 464, 80, 623, 240, true); @@ -290,10 +312,6 @@ AGS::AGS(NACT* parent, const Config& config) : nact(parent), dirty(false) // 画面選択 src_screen = dest_screen = 0; - // 画面サイズ - screen_height = 400; - scroll = 400; - // メッセージ表示 text_dest_x = text_w[0].sx; text_dest_y = text_w[0].sy + 2; @@ -447,7 +465,7 @@ void AGS::flush_screen(bool update) for(int y = 0; y < screen_height; y++) { uint32* src = vram[0][y]; uint32* dest = surface_line(hBmpDest, y); - for(int x = 0; x < 640; x++) { + for(int x = 0; x < screen_width; x++) { if (nact->sys_ver == 3 && src[x] & 0x80000000) { // あゆみちゃん物語 フルカラー実写版 dest[x] = src[x] & 0xffffff; @@ -457,7 +475,7 @@ void AGS::flush_screen(bool update) } } } - invalidate_screen(0, 0, 640, screen_height); + invalidate_screen(0, 0, screen_width, screen_height); } void AGS::draw_screen(int sx, int sy, int width, int height) @@ -514,8 +532,8 @@ void AGS::update_screen() void AGS::save_screenshot(const char* path) { - SDL_Surface* sf = SDL_CreateRGBSurface(0, 640, screen_height, 32, 0, 0, 0, 0); - SDL_Rect r = {0, 0, 640, screen_height}; + SDL_Surface* sf = SDL_CreateRGBSurface(0, screen_width, screen_height, 32, 0, 0, 0, 0); + SDL_Rect r = {0, 0, screen_width, screen_height}; SDL_UnlockSurface(hBmpDest); SDL_BlitSurface(hBmpDest, &r, sf, NULL); SDL_LockSurface(hBmpDest); @@ -529,6 +547,14 @@ void AGS::save_screenshot(const char* path) SDL_FreeSurface(sf); } +int AGS::calculate_menu_max(int window) { + if (nact->crc32_a == CRC32_INTRUDER) + return 6; + if (nact->crc32_a == CRC32_GAKUEN || nact->crc32_a == CRC32_GAKUEN_ENG) + return (menu_w[window - 1].ey - menu_w[window - 1].sy) / (menu_font_size + 4); + return 11; +} + #ifdef __EMSCRIPTEN__ extern "C" { diff --git a/src/sys/ags.h b/src/sys/ags.h index 89222ef..2887c8b 100644 --- a/src/sys/ags.h +++ b/src/sys/ags.h @@ -129,6 +129,7 @@ public: void translate_mouse_coords(int* x, int* y); void save_screenshot(const char* path); + int calculate_menu_max(int window); bool dirty; @@ -140,7 +141,7 @@ public: int dest_screen; int scroll; - int screen_height; + int screen_width, screen_height; // メッセージ表示 int text_dest_x; diff --git a/src/sys/ags_draw.cpp b/src/sys/ags_draw.cpp index 9d48817..3ee7900 100644 --- a/src/sys/ags_draw.cpp +++ b/src/sys/ags_draw.cpp @@ -37,6 +37,8 @@ void AGS::load_cg(int page, int transparent) case 1: switch (nact->crc32_a) { case CRC32_BUNKASAI: + case CRC32_GAKUEN: + case CRC32_GAKUEN_ENG: load_vsp(data, page, transparent); break; case CRC32_INTRUDER: diff --git a/src/sys/ags_vsp.cpp b/src/sys/ags_vsp.cpp index ba2951f..a0d3ea0 100644 --- a/src/sys/ags_vsp.cpp +++ b/src/sys/ags_vsp.cpp @@ -141,7 +141,13 @@ void AGS::load_vsp(uint8* data, int page, int transparent) c[6] = ((b0 >> 1) & 1) | ((b1 ) & 2) | ((b2 << 1) & 4) | ((b3 << 2) & 8); c[7] = ((b0 ) & 1) | ((b1 << 1) & 2) | ((b2 << 2) & 4) | ((b3 << 3) & 8); - uint32* dest = &vram[dest_screen][y + sy][(x + sx) * 8]; + uint32* dest; + // Gakuen Senki uses exact sx values rather than 8x. + if (nact->crc32_a == CRC32_GAKUEN || nact->crc32_a == CRC32_GAKUEN_ENG) + dest = &vram[dest_screen][y + sy][(x * 8) + sx]; + else + dest = &vram[dest_screen][y + sy][(x + sx) * 8]; + if(transparent == -1) { for(int i = 0; i < 8; i++) { dest[i] = c[i] | base; @@ -159,7 +165,20 @@ void AGS::load_vsp(uint8* data, int page, int transparent) // 画面更新 if(dest_screen == 0 && extract_cg) { - draw_screen(sx * 8, sy, width * 8, height); + if (nact->crc32_a == CRC32_GAKUEN || nact->crc32_a == CRC32_GAKUEN_ENG) { + // Gakuen Senki's images were converted to VSP for the modern port, but don't always adhere to VSP's width restrictions, which demand + // every image width be a factor of 8. Thankfully, the exceptions are designed to fit into specific parts of the GUI, and so have + // consistent widths for each output position. + // + // As above, GS also uses exact sx values rather than values that are meant to be multiplied by 8. + if (sx == 289 && sy == 26) draw_screen(sx, sy, 188, height); + else if (sx == 289 && sy == 92) draw_screen(sx, sy, 190, height); + else if (sx == 278 && sy == 208) draw_screen(sx, sy, 212, height); + else draw_screen(sx, sy, width * 8, height); + } + else { + draw_screen(sx * 8, sy, width * 8, height); + } } } diff --git a/src/sys/crc32.h b/src/sys/crc32.h index 569582c..0f60c78 100644 --- a/src/sys/crc32.h +++ b/src/sys/crc32.h @@ -32,6 +32,8 @@ #define CRC32_VAMPIRE 0x957bcfbf // Little Vampire #define CRC32_VAMPIRE_ENG 0x61985a7f // Little Vampire (English) Patch 1.5 #define CRC32_YAKATA 0x8cef6fa6 // ALICEの館 +#define CRC32_GAKUEN 0xe4d6ec66 // 学園戦記 (unofficial system1 port) 1.0JP +#define CRC32_GAKUEN_ENG 0x6ba8c102 // Gakuen Senki (English) 1.0 // SYSTEM2 diff --git a/src/sys/nact.h b/src/sys/nact.h index 94a3674..1c6a26d 100644 --- a/src/sys/nact.h +++ b/src/sys/nact.h @@ -183,6 +183,9 @@ protected: // DPS bool text_refresh; + // GAKUEN + bool enable_graphics = true; + bool column; // 座標モード bool wait_keydown; // ウェイト時のキー受付 int text_wait_time; // テキスト表示のウェイト diff --git a/src/sys/nact_crc32.cpp b/src/sys/nact_crc32.cpp index 72b5f23..b9b8afb 100644 --- a/src/sys/nact_crc32.cpp +++ b/src/sys/nact_crc32.cpp @@ -39,6 +39,8 @@ const struct CRCTable { {"little_vampire", 1, "Little Vampire", JAPANESE, CRC32_VAMPIRE}, {"little_vampire_eng", 1, "Little Vampire", ENGLISH, CRC32_VAMPIRE_ENG}, {"yakata", 1, "ALICEの館", JAPANESE, CRC32_YAKATA}, + {"gakuen", 1, "学園戦記", JAPANESE, CRC32_GAKUEN}, + {"gakuen_eng", 1, "Gakuen Senki", ENGLISH, CRC32_GAKUEN_ENG}, {"ayumi_fd", 2, "あゆみちゃん物語", JAPANESE, CRC32_AYUMI_FD}, {"ayumi_hint", 2, "あゆみちゃん物語 ヒントディスク", JAPANESE, CRC32_AYUMI_HINT}, diff --git a/src/sys/nact_input.cpp b/src/sys/nact_input.cpp index 92890a9..1536255 100644 --- a/src/sys/nact_input.cpp +++ b/src/sys/nact_input.cpp @@ -52,7 +52,7 @@ void NACT::pump_events() case SDL_FINGERDOWN: case SDL_FINGERUP: case SDL_FINGERMOTION: - mousex = e.tfinger.x * 640; + mousex = e.tfinger.x * ags->screen_width; mousey = e.tfinger.y * ags->screen_height; switch (SDL_GetNumTouchFingers(e.tfinger.touchId)) { case 0: diff --git a/src/sys/nact_sys1.cpp b/src/sys/nact_sys1.cpp index d55aade..43c914f 100644 --- a/src/sys/nact_sys1.cpp +++ b/src/sys/nact_sys1.cpp @@ -20,9 +20,6 @@ #include "encoding.h" #include "texthook.h" -// メニューの改ページ -#define MENU_MAX (crc32_a == CRC32_INTRUDER ? 6 : 11) - #define WAIT(tm) \ { \ Uint32 dwTime = SDL_GetTicks() + (tm); \ @@ -112,6 +109,17 @@ void NACT_Sys1::opening() cmd_a(); ags->draw_box(0); break; + case CRC32_GAKUEN: + case CRC32_GAKUEN_ENG: + mako->set_cd_track(1, 1); + ags->load_cg(302, -1); + WAIT(3000); + ags->load_cg(303, -1); + WAIT(3000); + mako->play_music(1); + ags->load_cg(304, -1); + WAIT(4000); + break; } } @@ -303,6 +311,9 @@ void NACT_Sys1::cmd_set_menu() ags->menu_dest_y += 2; ags->draw_menu = true; + if (crc32_a == CRC32_GAKUEN || crc32_a == CRC32_GAKUEN_ENG) + menu_window = 2; + output_console("\n$%x,", menu_addr[menu_index - 1]); } } @@ -372,6 +383,10 @@ void NACT_Sys1::cmd_open_verb() // 表示する動詞のチェック int chk[MAX_VERB], page = 0, cnt = 0; + if (crc32_a == CRC32_GAKUEN || crc32_a == CRC32_GAKUEN_ENG) + menu_window = 1; + int menu_max = ags->calculate_menu_max(menu_window); + memset(chk, 0, sizeof(chk)); for(int i = 0; i < menu_index; i++) { chk[menu_verb[i]] = 1; @@ -390,7 +405,7 @@ top: ags->menu_dest_y = 0; ags->draw_menu = true; - if(cnt <= MENU_MAX) { + if(cnt <= menu_max) { // 1ページ内に全て表示できる for(int i = 0; i < MAX_VERB; i++) { if(chk[i]) { @@ -412,7 +427,7 @@ top2: ags->menu_dest_y += ags->menu_font_size + 2; } page = i + 1; - if(index == MENU_MAX - 1) { + if(index == menu_max - 1) { break; } } @@ -446,6 +461,8 @@ top2: void NACT_Sys1::cmd_open_obj(int verb) { + int menu_max = ags->calculate_menu_max(menu_window); + // 目的語メニューの表示 verb_obj = false; @@ -481,7 +498,7 @@ top: ags->menu_dest_y = 0; ags->draw_menu = true; - if(cnt <= MENU_MAX - 1) { + if(cnt <= menu_max - 1) { // 1ページ内に全て表示できる for(int i = 0; i < MAX_OBJ; i++) { if(chk[i]) { @@ -509,7 +526,7 @@ top2: ags->menu_dest_y += ags->menu_font_size + 2; } page = i + 1; - if(index == MENU_MAX - 2) { + if(index == menu_max - 2) { break; } } @@ -620,11 +637,14 @@ void NACT_Sys1::cmd_g() output_console("\nG %d:", page); - if(crc32_a == CRC32_INTRUDER) { + if (crc32_a == CRC32_INTRUDER) { page = (page == 97) ? 96 : (page == 98) ? 97 : page; + } else if (crc32_a == CRC32_GAKUEN || crc32_a == CRC32_GAKUEN_ENG) { + page = (page == 3) ? 94 : page; } - ags->load_cg(page, -1); + if (enable_graphics) + ags->load_cg(page, -1); if(crc32_a == CRC32_INTRUDER) { if(page == 94) { @@ -1011,6 +1031,41 @@ void NACT_Sys1::cmd_y() fatal_error = true; break; } + } else if (crc32_a == CRC32_GAKUEN || crc32_a == CRC32_GAKUEN_ENG) { + switch (cmd) { + case 1: + RND = (param == 0 || param == 1) ? 0 : random(param); + break; + case 2: + // Clear variables up to param, though in practice param is always 21. + for (int i = 0; i <= param; i++) { + var[i] = 0; + } + break; + case 3: + { + char buf[16]; + sprintf(buf, "%d", param); + ags->draw_text(buf); + } + break; + case 4: + if (param == 0) { + mako->play_music(1); + } else { + mako->stop_music(); + } + break; + case 5: + enable_graphics = (param == 0); + break; + case 6: + // Dummied. 0 - "Display text slowly," 1 - disable. + break; + case 7: + // Dummied. 0 - "Switch to interlace mode," 1 - disable. + break; + } } else { switch(cmd) { case 0: @@ -1190,6 +1245,64 @@ void NACT_Sys1::cmd_z() } } break; + + case CRC32_GAKUEN: + case CRC32_GAKUEN_ENG: + if (param != 30 && (param < 34 || param > 46)) { + if (enable_graphics) { + ags->load_cg(param + 250, -1); + } + } + + // Display the Gakuen Senki party status bar for party members. The Z commands make the slots appear in reverse, so 39 and 45 draw + // the box for character 1, 38 and 44 draw for character 2, and so on. Z 1, 30: draws the first character slot, which is always occupied. + // + // One the actual MSX, this bar was constructed in an irregular fashion. This code constructs a more consistent version, where each square + // is 35px wide with 2px borders on each side. Since the borders are overlapped, this means each sub-sprite is placed 37px apart. + else if (param == 46) { + // Originally this would have loaded Image 296, the party status bar, to screen 1 for future reference. However, due to trouble with + // the palette, I decided to load it before using each subcommand, instead. + /*ags->dest_screen = 1; + ags->load_cg(296, -1); + ags->dest_screen = 0;*/ + } + else if (param == 30) { + // Load party status bar to screen 1. + ags->dest_screen = 1; + ags->load_cg(296, -1); + ags->dest_screen = 0; + + // Draw Wataru's icon. + ags->src_screen = 1; + ags->copy(10, 182, 48, 224, 7, 210); + ags->src_screen = 0; + } + else if (param >= 34 && param <= 39) { + int x = (39 - param) * 37 + 45; + + // Load party status bar to screen 1. + ags->dest_screen = 1; + ags->load_cg(296, -1); + ags->dest_screen = 0; + + // Draw a blank party icon to init or clear the party box. + ags->src_screen = 1; + ags->copy(269, 182, 307, 224, x, 210); + ags->src_screen = 0; + } + else { + int x = (45 - param) * 37 + 45; + + // Load party status bar to screen 1. + ags->dest_screen = 1; + ags->load_cg(296, -1); + ags->dest_screen = 0; + + // Draw a party member's icon to the party bar. + ags->src_screen = 1; + ags->copy(x + 2, 182, x + 38, 224, x, 210); + ags->src_screen = 0; + } } } diff --git a/src/win/nact_win.cpp b/src/win/nact_win.cpp index f4a6ed7..d4ee563 100644 --- a/src/win/nact_win.cpp +++ b/src/win/nact_win.cpp @@ -26,8 +26,6 @@ void init_menu(bool mouse_move_enabled) HINSTANCE hinst = (HINSTANCE)GetModuleHandle(NULL); HMENU hmenu = LoadMenu(hinst, MAKEINTRESOURCE(IDR_MENU1)); SetMenu(get_hwnd(g_window), hmenu); - // Let SDL recalc the window size, taking menu height into account. - SDL_SetWindowSize(g_window, 640, 400); if (mouse_move_enabled) CheckMenuItem(hmenu, ID_OPTION_MOUSE_MOVE, MF_BYCOMMAND | MFS_CHECKED); }