/* ALICE SOFT SYSTEM 3 for Win32 [ AGS ] */ #include "ags.h" #include #include "crc32.h" #include "../config.h" #include "../fileio.h" extern SDL_Window* g_window; extern SDL_Renderer* g_renderer; static SDL_Surface* display_surface; #define SET_TEXT(n, x1, y1, x2, y2, f) { \ text_w[n].sx = x1; \ text_w[n].sy = y1; \ text_w[n].ex = x2; \ text_w[n].ey = y2; \ text_w[n].frame = f; \ } #define SET_MENU(n, x1, y1, x2, y2, f) { \ menu_w[n].sx = x1; \ menu_w[n].sy = y1; \ menu_w[n].ex = x2; \ menu_w[n].ey = y2; \ menu_w[n].frame = f; \ } #define SET_BOX(n, c, x1, y1, x2, y2) { \ box[n].color = c; \ box[n].sx = x1; \ box[n].sy = y1; \ box[n].ex = x2; \ box[n].ey = y2; \ } namespace { const uint32 SCANLINE_ALPHA = 0x38; // 0-255 SDL_Texture* create_scanline_texture(SDL_Renderer* renderer, int width, int height) { SDL_Surface* sf = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_ARGB8888); for (int y = 0; y < height; y++) { uint32* p = surface_line(sf, y); uint32 v = y % 2 ? (SCANLINE_ALPHA << 24) : 0; for (int x = 0; x < width; x++) { p[x] = v; } } SDL_Texture* tx = SDL_CreateTextureFromSurface(renderer, sf); SDL_SetTextureBlendMode(tx, SDL_BLENDMODE_BLEND); SDL_FreeSurface(sf); return tx; } } // namespace AGS::AGS(NACT* parent, const Config& config) : nact(parent), dirty(false) { // 画面サイズ if (nact->crc32_a == CRC32_GAKUEN || nact->crc32_a == CRC32_GAKUEN_ENG) { window_width = 582; screen_width = 512; window_height = screen_height = 424; } else { window_width = screen_width = 640; window_height = screen_height = 400; } scroll = screen_height; SDL_SetWindowSize(g_window, window_width, window_height); SDL_RenderSetLogicalSize(g_renderer, window_width, window_height); sdlTexture = SDL_CreateTexture(g_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, screen_width, screen_height); // TOOD: pixelformat? scanline_texture = NULL; // DIBSection 8bpp * 3 (表, 裏, メニュー) for(int i = 0; i < 3; i++) { hBmpScreen[i] = SDL_CreateRGBSurface(0, 640, 480, 32, 0, 0, 0, 0); SDL_LockSurface(hBmpScreen[i]); // TODO: clear surface // memset(lpBmpScreen[i], 0, 640 * 480 * sizeof(DWORD)); // 仮想VRAMへのポインタ取得 for(int j = 0; j < 480; j++) { vram[i][j] = surface_line(hBmpScreen[i], j); } } // DIBSection 24bpp * 1 (最終出力先) display_surface = hBmpDest = SDL_CreateRGBSurface(0, 640, 480, 32, 0, 0, 0, 0); SDL_LockSurface(hBmpDest); // TODO: clear surface // フォント TTF_Init(); if (!config.font_file.empty()) { rw_font = open_file(config.font_file.c_str()); if (!rw_font) parent->fatal("Cannot open font file %s", config.font_file.c_str()); } else { rw_font = open_resource(FONT_RESOURCE_NAME, "fonts"); if (!rw_font) parent->fatal("Cannot open default font"); } if (rw_font) { hFont16 = TTF_OpenFontRW(rw_font, 0, 16); SDL_RWseek(rw_font, 0, SEEK_SET); hFont24 = TTF_OpenFontRW(rw_font, 0, 24); SDL_RWseek(rw_font, 0, SEEK_SET); hFont32 = TTF_OpenFontRW(rw_font, 0, 32); SDL_RWseek(rw_font, 0, SEEK_SET); hFont48 = TTF_OpenFontRW(rw_font, 0, 48); SDL_RWseek(rw_font, 0, SEEK_SET); hFont64 = TTF_OpenFontRW(rw_font, 0, 64); if (!hFont16 || !hFont24 || !hFont32 || !hFont48 || !hFont64) { parent->fatal("TTF_OpenFontRW failed: %s", TTF_GetError()); } } if (config.no_antialias) ags_setAntialiasedStringMode(0); // カーソル初期化 for(int i = 0; i < 10; i++) { hCursor[i] = NULL; } // GAIJI.DAT読み込み memset(gaiji, 0, sizeof(gaiji)); FILEIO* fio = new FILEIO(); if(fio->Fopen("GAIJI.DAT", FILEIO_READ_BINARY)) { int d1, d2; while((d1 = fio->Fgetc()) != EOF) { d2 = fio->Fgetc(); // jis to sjis d1 += (d2 & 1) ? 0x1f : 0x7d; d1 += (d1 > 0x7f) ? 1 : 0; d2 = ((d2 - 0x21) >> 1) + 0x81; d2 += (d2 > 0x9f) ? 0x40 : 0; uint16 code = (d1 & 0xff) | ((d2 & 0xff) << 8); if(0xeb9f <= code && code <= 0xebfc) { for(int i = 0; i < 32; i++) { gaiji[code - 0xeb9f][i] = fio->Fgetc(); } } else if(0xec40 <= code && code <= 0xec9e) { for(int i = 0; i < 32; i++) { gaiji[code - 0xec40 + 94][i] = fio->Fgetc(); } } else { fio->Fseek(32, FILEIO_SEEK_CUR); } } fio->Fclose(); } delete fio; // SYSTEM3 初期化 // ACG.DAT strcpy_s(acg, 16, "ACG.DAT"); // パレット memset(program_palette, 0, sizeof(program_palette)); program_palette[0x00] = SETPALETTE16(0x0, 0x0, 0x0); program_palette[0x01] = SETPALETTE16(0x0, 0x0, 0xa); program_palette[0x02] = SETPALETTE16(0xa, 0x0, 0x0); program_palette[0x03] = SETPALETTE16(0xa, 0x0, 0xa); program_palette[0x04] = SETPALETTE16(0x0, 0x0, 0x0); program_palette[0x05] = SETPALETTE16(0x0, 0xa, 0xa); program_palette[0x06] = SETPALETTE16(0xa, 0xa, 0x0); program_palette[0x07] = SETPALETTE16(0xd, 0xd, 0xd); program_palette[0x08] = SETPALETTE16(0x7, 0x7, 0x7); program_palette[0x09] = SETPALETTE16(0x0, 0x0, 0xf); program_palette[0x0a] = SETPALETTE16(0xf, 0x0, 0x0); program_palette[0x0b] = SETPALETTE16(0xf, 0x0, 0xf); program_palette[0x0c] = SETPALETTE16(0x0, 0xf, 0x0); program_palette[0x0d] = SETPALETTE16(0x0, 0xf, 0xf); program_palette[0x0e] = SETPALETTE16(0xf, 0xf, 0x0); program_palette[0x0f] = SETPALETTE16(0xf, 0xf, 0xf); if (nact->sys_ver == 1) { program_palette[0x10] = SETPALETTE16(0x0, 0x0, 0x0); program_palette[0x11] = SETPALETTE16(0x0, 0x0, 0xf); program_palette[0x12] = SETPALETTE16(0xf, 0x0, 0x0); program_palette[0x13] = SETPALETTE16(0xf, 0x0, 0xf); program_palette[0x14] = SETPALETTE16(0x0, 0xf, 0x0); program_palette[0x15] = SETPALETTE16(0x0, 0xf, 0xf); program_palette[0x16] = SETPALETTE16(0xf, 0xf, 0x0); program_palette[0x17] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0x18] = SETPALETTE16(0x0, 0x0, 0x0); program_palette[0x19] = SETPALETTE16(0x0, 0x0, 0xf); program_palette[0x1a] = SETPALETTE16(0xf, 0x0, 0x0); program_palette[0x1b] = SETPALETTE16(0xf, 0x0, 0xf); program_palette[0x1c] = SETPALETTE16(0x0, 0xf, 0x0); program_palette[0x1d] = SETPALETTE16(0x0, 0xf, 0xf); program_palette[0x1e] = SETPALETTE16(0xf, 0xf, 0x0); } program_palette[0x1f] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0x2f] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0x3f] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0x4f] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0x5f] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0x6f] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0x7f] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0x8f] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0x9f] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0xaf] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0xbf] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0xcf] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0xdf] = SETPALETTE16(0xf, 0xf, 0xf); program_palette[0xef] = SETPALETTE16(0xf, 0xf, 0xf); // To improve the quality of text antialiasing, preset the antialiasing // colors for text drawn in the primary colors (red, green, blue, yellow, // magenta, cyan, and white) on a black background. for (int i = 1; i <= 7; i++) { for (int j = 0; j <= 7; j++) { int n = 255 * j / 7; int r = (i & 4) ? n : 0; int g = (i & 2) ? n : 0; int b = (i & 1) ? n : 0; program_palette[0xc0 + i * 8 + j] = SETPALETTE16(r, g, b); } } memcpy(screen_palette, program_palette, sizeof(program_palette)); // Bコマンド for(int i = 0; i < 10; i++) { // ウィンドウの初期位置はシステムによって異なる switch (nact->crc32_a) { case CRC32_BUNKASAI: SET_TEXT(i, 24, 304, 616, 384, false); SET_MENU(i, 440, 18, 620, 178, true); break; case CRC32_CRESCENT: SET_TEXT(i, 24, 288, 616, 378, false); // 本来は横メニュー SET_MENU(i, 464, 50, 623, 240, true); break; case CRC32_DPS: case CRC32_DPS_SG: //case CRC32_DPS_SG2: case CRC32_DPS_SG3: SET_TEXT(i, 48, 288, 594, 393, false); //SET_MENU(i, 48, 288, 584, 393, false); SET_MENU(i, 48, 288, 594, 393, false); break; case CRC32_FUKEI: SET_TEXT(i, 44, 282, 593, 396, false); SET_MENU(i, 460, 14, 635, 214, false); break; case CRC32_INTRUDER: SET_TEXT(i, 8, 280, 629, 393, false); SET_MENU(i, 448, 136, 623, 340, true); break; case CRC32_TENGU: SET_TEXT(i, 44, 282, 593, 396, false); SET_MENU(i, 452, 14, 627, 214, false); break; case CRC32_TOUSHIN_HINT: SET_TEXT(i, 8, 311, 623, 391, false); SET_MENU(i, 452, 14, 627, 214, true); break; case CRC32_VAMPIRE: 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); SET_MENU(i, 452, 14, 627, 214, false); break; case CRC32_DALK_HINT: SET_TEXT(i, 24, 308, 376, 386, false); SET_MENU(i, 404, 28, 604, 244, true); break; case CRC32_RANCE3_HINT: SET_TEXT(i, 104, 304, 615, 383, false); SET_MENU(i, 464, 24, 623, 200, true); break; case CRC32_YAKATA2: 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); break; } text_w[i].push = false; text_w[i].screen = NULL; text_w[i].window = NULL; menu_w[i].push = true; menu_w[i].screen = NULL; menu_w[i].window = NULL; } // Eコマンド for(int i = 0; i < 20; i++) { // SET_BOX(i, 0, 0, 0, 631, 399); SET_BOX(i, 0, 0, 0, 639, 399); } if (nact->sys_ver == 2) { if(nact->crc32_a == CRC32_SDPS && (nact->crc32_b == CRC32_SDPS_TONO || nact->crc32_b == CRC32_SDPS_KAIZOKU)) { SET_BOX(0, 0, 40, 8, 598, 271); } else if(nact->crc32_a == CRC32_PROSTUDENTG_FD) { SET_BOX(0, 0, 64, 13, 407, 289); SET_BOX(1, 0, 24, 298, 111, 390); SET_BOX(2, 0, 0, 0, 639, 307); SET_BOX(3, 0, 0, 0, 319, 399); SET_BOX(4, 0, (16*8), 310, ((77*8)-1), 390); SET_BOX(5, 0, (4*8), 310, ((76*8)-1), 390); SET_BOX(6, 0, (2*8), 317, ((56*8)-1), 389); SET_BOX(7, 15, 64, 13, 407, 289); SET_BOX(8, 0, 0, 0, 319, 399); SET_BOX(9, 0, 320, 0, 639, 40); } } // 画面選択 src_screen = dest_screen = 0; // メッセージ表示 text_dest_x = text_w[0].sx; text_dest_y = text_w[0].sy + 2; text_space = 2; text_font_size = 16; if (nact->sys_ver == 1) { text_font_color = 15 + 16; text_frame_color = 15 + 16; text_back_color = 0 + 16; } else { text_font_color = 15; text_frame_color = 15; text_back_color = 0; } // メニュー表示 menu_dest_x = 2; menu_dest_y = 0; menu_font_size = 16; if (nact->sys_ver == 1) { menu_font_color = 15 + 16; menu_frame_color = 15 + 16; menu_back_color = 0 + 16; } else { menu_font_color = 15; menu_frame_color = 15; menu_back_color = 0; } menu_fix = false; draw_hankaku = false; draw_menu = false; // CG表示 set_cg_dest = false; cg_dest_x = cg_dest_y = 0; for(int i = 0; i < MAX_CG; i++) { extract_palette_cg[i] = true; } get_palette = extract_palette = extract_cg = true; palette_bank = -1; // マウスカーソル cursor_color = 15; cursor_index = 0; set_scanline_mode(config.scanline); } AGS::~AGS() { // 退避領域の開放 for(int i = 0; i < 10; i++) { if(menu_w[i].screen) { free(menu_w[i].screen); } if(menu_w[i].window) { free(menu_w[i].window); } if(text_w[i].screen) { free(text_w[i].screen); } if(text_w[i].window) { free(text_w[i].window); } } // カーソル開放 for(int i = 0; i < 10; i++) { if(hCursor[i]) { SDL_FreeCursor(hCursor[i]); } } // フォント開放 if (rw_font) { TTF_CloseFont(hFont16); TTF_CloseFont(hFont24); TTF_CloseFont(hFont32); TTF_CloseFont(hFont48); TTF_CloseFont(hFont64); SDL_RWclose(rw_font); } // surface開放 for(int i = 0; i < 3; i++) { SDL_FreeSurface(hBmpScreen[i]); } SDL_FreeSurface(hBmpDest); SDL_DestroyTexture(sdlTexture); } void AGS::set_palette(int index, int r, int g, int b) { if(index < 16) { screen_palette[index] = program_palette[index] = SETPALETTE16(r, g, b); } else { screen_palette[index] = program_palette[index] = SETPALETTE256(r, g, b); } } uint8 AGS::get_pixel(int dest, int x, int y) { if(vram[dest][y][x] & 0x80000000) { return 0; } else { return (uint8)vram[dest][y][x]; } } void AGS::set_pixel(int dest, int x, int y, uint8 color) { vram[dest][y][x] = color; } void AGS::fade_out(int duration_ms, bool white) { if (fade_level) return; fade_color = white ? 255 : 0; Uint32 dwStart = SDL_GetTicks(); while (fade_level < 255) { nact->sys_sleep(16); int t = SDL_GetTicks() - dwStart; fade_level = t >= duration_ms ? 255 : t * 255 / duration_ms; dirty = true; } update_screen(); } void AGS::fade_in(int duration_ms) { if (fade_level == 0) return; Uint32 dwStart = SDL_GetTicks(); while (fade_level > 0) { nact->sys_sleep(16); int t = SDL_GetTicks() - dwStart; fade_level = t >= duration_ms ? 0 : (duration_ms - t) * 255 / duration_ms; dirty = true; } update_screen(); } void AGS::flush_screen(bool update) { if(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 < screen_width; x++) { if (nact->sys_ver == 3 && src[x] & 0x80000000) { // あゆみちゃん物語 フルカラー実写版 dest[x] = src[x] & 0xffffff; } else { dest[x] = screen_palette[src[x] & 0xff]; } } } } invalidate_screen(0, 0, screen_width, screen_height); } void AGS::draw_screen(int sx, int sy, int width, int height) { for(int y = sy; y < (sy + height) && y < 480; y++) { uint32* src = vram[0][y]; uint32* dest = surface_line(hBmpDest, y); for(int x = sx; x < (sx + width) && x < 640; x++) { uint32 a=src[x]; if (nact->sys_ver == 3 && src[x] & 0x80000000) { // あゆみちゃん物語 フルカラー実写版 dest[x] = src[x] & 0xffffff; } else { dest[x] = screen_palette[src[x] & 0xff]; } } } invalidate_screen(sx, sy, width, height); } void AGS::invalidate_screen(int sx, int sy, int width, int height) { int top = screen_height == 400 ? sy + (scroll - 400) : sy; if (top < 0) { height += top; top = 0; } uint32* pixels = surface_line(hBmpDest, top) + sx; if (sy + height > screen_height) height = screen_height - sy; if (sx + width > hBmpDest->w) width = hBmpDest->w - sx; SDL_Rect rect = {sx, sy, width, height}; SDL_UpdateTexture(sdlTexture, &rect, pixels, hBmpDest->pitch); dirty = true; } void AGS::update_screen() { if (!dirty) return; SDL_RenderClear(g_renderer); SDL_RenderCopy(g_renderer, sdlTexture, NULL, NULL); if (fade_level) { SDL_SetRenderDrawBlendMode(g_renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(g_renderer, fade_color, fade_color, fade_color, fade_level); SDL_RenderFillRect(g_renderer, NULL); SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255); SDL_SetRenderDrawBlendMode(g_renderer, SDL_BLENDMODE_NONE); } if (scanline_texture) SDL_RenderCopy(g_renderer, scanline_texture, NULL, NULL); SDL_RenderPresent(g_renderer); dirty = false; } void AGS::set_scanline_mode(bool enable) { if (enable && !scanline_texture) { scanline_texture = create_scanline_texture(g_renderer, screen_width, screen_height); dirty = true; update_screen(); } else if (!enable && scanline_texture) { SDL_DestroyTexture(scanline_texture); scanline_texture = NULL; dirty = true; update_screen(); } } void AGS::save_screenshot(const char* path) { 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); if (scanline_texture) { SDL_Renderer* renderer = SDL_CreateSoftwareRenderer(sf); SDL_Texture *tx = create_scanline_texture(renderer, screen_width, screen_height); SDL_RenderCopy(renderer, tx, NULL, NULL); SDL_DestroyTexture(tx); SDL_DestroyRenderer(renderer); } if (SDL_SaveBMP(sf, path) != 0) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "system3", SDL_GetError(), g_window); SDL_ClearError(); } 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" { void* EMSCRIPTEN_KEEPALIVE sdl_getDisplaySurface() { if (SDL_MUSTLOCK(display_surface)) return NULL; return display_surface->pixels; } } #endif