1 /*
2 BStone: A Source port of
3 Blake Stone: Aliens of Gold and Blake Stone: Planet Strike
4
5 Copyright (c) 1992-2013 Apogee Entertainment, LLC
6 Copyright (c) 2013-2015 Boris I. Bendovsky (bibendovsky@hotmail.com)
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the
20 Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23
24
25 #include "id_heads.h"
26
27
28 static const int palette_color_count = 256;
29
30
31 int bufferofs;
32
33 bool screenfaded;
34
35 uint8_t palette1[palette_color_count][3];
36 uint8_t palette2[palette_color_count][3];
37 uint8_t* vga_memory = nullptr;
38
39 int vga_scale = 0;
40 int vga_width = 0;
41 int vga_height = 0;
42 int vga_area = 0;
43 int vga_3d_view_top = 0;
44 int vga_3d_view_bottom = 0;
45
46 int screen_x = 0;
47 int screen_y = 0;
48
49 int screen_width = 0;
50 int screen_height = 0;
51
52 bool vid_has_vsync = false;
53 bool vid_widescreen = default_vid_widescreen;
54 bool vid_is_hud = false;
55 bool vid_is_3d = false;
56
57 bstone::SpriteCache vid_sprite_cache;
58
59
60 // BBi
61 namespace
62 {
63
64
65 constexpr int default_window_width = 640;
66 constexpr int default_window_height = 480;
67
68
69 class VgaColor
70 {
71 public:
72 uint8_t r;
73 uint8_t g;
74 uint8_t b;
75 }; // VgaColor
76
77 using VgaPalette = std::array<VgaColor, palette_color_count>;
78 using SdlPalette = std::array<uint32_t, palette_color_count>;
79
80
81 int window_width = 0;
82 int window_height = 0;
83
84 VgaBuffer sdl_ui_buffer;
85 UiMaskBuffer sdl_mask_buffer;
86
87
88 std::string sdl_error_message;
89
90 VgaPalette sdl_vga_palette;
91 VgaBuffer sdl_vga_buffer;
92
93 bool sdl_use_custom_window_position = false;
94 int sdl_window_x = 0;
95 int sdl_window_y = 0;
96
97 SDL_DisplayMode display_mode;
98 bool sdl_is_windowed = false;
99 SDL_Window* sdl_window = nullptr;
100 SDL_Renderer* sdl_renderer = nullptr;
101 SDL_PixelFormat* sdl_texture_pixel_format = nullptr;
102 SDL_Texture* sdl_screen_texture = nullptr;
103 SDL_Texture* sdl_ui_texture = nullptr;
104 SdlPalette sdl_palette;
105
106
sdl_initialize_vga_buffer()107 void sdl_initialize_vga_buffer()
108 {
109 const auto vga_area = 2 * ::vga_width * ::vga_height;
110
111 ::sdl_vga_buffer.resize(
112 vga_area);
113
114 ::vga_memory = ::sdl_vga_buffer.data();
115 }
116
sdl_initialize_ui_buffer()117 void sdl_initialize_ui_buffer()
118 {
119 const auto area = ::vga_ref_width * ::vga_ref_height;
120
121 ::sdl_ui_buffer.resize(
122 area);
123 }
124
sdl_initialize_window()125 bool sdl_initialize_window()
126 {
127 bstone::Log::write("VID: Creating a window...");
128
129
130 if (!::sdl_use_custom_window_position)
131 {
132 ::sdl_window_x = SDL_WINDOWPOS_CENTERED;
133 ::sdl_window_y = SDL_WINDOWPOS_CENTERED;
134 }
135
136 if (::sdl_window_x < 0)
137 {
138 ::sdl_window_x = 0;
139 }
140
141 if (::sdl_window_y < 0)
142 {
143 ::sdl_window_y = 0;
144 }
145
146 uint32_t window_flags =
147 SDL_WINDOW_OPENGL |
148 SDL_WINDOW_HIDDEN |
149 0;
150
151 if (!::sdl_is_windowed)
152 {
153 window_flags |=
154 SDL_WINDOW_BORDERLESS |
155 SDL_WINDOW_FULLSCREEN_DESKTOP;
156 }
157
158
159 std::string title = "Blake Stone: ???";
160
161 if (::is_aog_full())
162 {
163 std::string version_string;
164
165 if (::is_aog_full_v1_0())
166 {
167 version_string = "v1.0";
168 }
169 else if (::is_aog_full_v2_x())
170 {
171 version_string = "v2.x";
172 }
173 else if (::is_aog_full_v3_0())
174 {
175 version_string = "v3.0";
176 }
177
178 title = "Blake Stone: Aliens of Gold (full";
179
180 if (!version_string.empty())
181 {
182 title += ", " + version_string;
183 }
184
185 title += ')';
186 }
187 else if (::is_aog_sw())
188 {
189 title = "Blake Stone: Aliens of Gold (shareware, v3.0)";
190 }
191 else if (::is_ps())
192 {
193 title = "Blake Stone: Planet Strike (full, v1.x)";
194 }
195
196 ::sdl_window = ::SDL_CreateWindow(
197 title.c_str(),
198 ::sdl_window_x,
199 ::sdl_window_y,
200 ::window_width,
201 ::window_height,
202 window_flags);
203
204 if (!::sdl_window)
205 {
206 ::sdl_error_message = "VID: Failed to create a window: ";
207 ::sdl_error_message += ::SDL_GetError();
208
209 bstone::Log::write_error(::SDL_GetError());
210
211 return false;
212 }
213
214 auto hint_result = ::SDL_SetHint(
215 "SDL_HINT_RENDER_DRIVER",
216 "opengl");
217
218 return true;
219 }
220
sdl_initialize_renderer()221 bool sdl_initialize_renderer()
222 {
223 bstone::Log::write(
224 "VID: Initializing a renderer...");
225
226
227 bool is_succeed = true;
228
229
230 if (is_succeed)
231 {
232 bstone::Log::write(
233 "VID: Available renderer drivers:");
234
235 const auto driver_count = ::SDL_GetNumRenderDrivers();
236
237 for (int i = 0; i < driver_count; ++i)
238 {
239 SDL_RendererInfo info;
240
241 const auto sdl_result = ::SDL_GetRenderDriverInfo(
242 i,
243 &info);
244
245 bstone::Log::write(
246 "{}. {}",
247 i + 1,
248 info.name);
249 }
250 }
251
252
253 uint32_t renderer_flags = 0;
254 const char* renderer_driver = nullptr;
255
256 if (is_succeed)
257 {
258 const auto is_vsync_disabled = ::g_args.has_option(
259 "vid_no_vsync");
260
261 if (is_vsync_disabled)
262 {
263 bstone::Log::write(
264 "VID: Skipping VSync...");
265 }
266 else
267 {
268 renderer_flags = SDL_RENDERER_PRESENTVSYNC;
269
270 bstone::Log::write(
271 "VID: Using VSync...");
272 }
273
274
275 auto& ren_string = ::g_args.get_option_value(
276 "vid_renderer");
277
278 if (!ren_string.empty())
279 {
280 bstone::Log::write(
281 "VID: Setting preferred renderer...");
282
283
284 if (ren_string == "d3d")
285 {
286 renderer_driver = "direct3d";
287
288 bstone::Log::write(
289 "VID: Forcing Direct3D renderer.");
290 }
291 else if (ren_string == "ogl")
292 {
293 renderer_driver = "opengl";
294
295 bstone::Log::write(
296 "VID: Forcing OpenGL renderer.");
297 }
298 else if (ren_string == "ogles")
299 {
300 renderer_driver = "opengles";
301
302 bstone::Log::write(
303 "VID: Forcing OpenGL ES renderer.");
304 }
305 else if (ren_string == "ogles2")
306 {
307 renderer_driver = "opengles2";
308
309 bstone::Log::write(
310 "VID: Forcing OpenGL ES 2 renderer.");
311 }
312 else if (ren_string == "soft")
313 {
314 renderer_driver = "software";
315
316 bstone::Log::write(
317 "VID: Forcing software renderer.");
318 }
319 else
320 {
321 bstone::Log::write_warning(
322 "VID: Unsupported renderer: {}",
323 ren_string);
324 }
325
326
327 auto hint_result = ::SDL_SetHint(
328 SDL_HINT_RENDER_DRIVER,
329 renderer_driver);
330
331 if (hint_result == SDL_FALSE)
332 {
333 bstone::Log::write_warning(
334 ::SDL_GetError());
335 }
336 }
337 }
338
339 if (is_succeed)
340 {
341 bstone::Log::write(
342 "VID: Creating a renderer...");
343
344 ::sdl_renderer = ::SDL_CreateRenderer(
345 ::sdl_window,
346 -1,
347 renderer_flags);
348
349 if (!::sdl_renderer)
350 {
351 is_succeed = false;
352
353 ::sdl_error_message = "VID: Failed to create a renderer: ";
354 ::sdl_error_message += ::SDL_GetError();
355
356 bstone::Log::write_error(
357 ::SDL_GetError());
358 }
359 }
360
361
362 SDL_RendererInfo renderer_info;
363
364 if (is_succeed)
365 {
366 bstone::Log::write(
367 "VID: Quering renderer for info...");
368
369 auto sdl_result = ::SDL_GetRendererInfo(
370 ::sdl_renderer,
371 &renderer_info);
372
373 if (sdl_result != 0)
374 {
375 is_succeed = false;
376
377 ::sdl_error_message = "VID: Failed to query the renderer: ";
378 ::sdl_error_message += ::SDL_GetError();
379
380 bstone::Log::write_error(
381 ::SDL_GetError());
382 }
383 }
384
385
386 if (is_succeed)
387 {
388 if (renderer_driver)
389 {
390 if (::SDL_strcasecmp(
391 renderer_driver,
392 renderer_info.name) != 0)
393 {
394 bstone::Log::write_warning(
395 "VID: Unexpected renderer is selected: {0}.",
396 renderer_info.name);
397 }
398 }
399 else
400 {
401 bstone::Log::write(
402 "VID: Current renderer: {0}.",
403 renderer_info.name);
404 }
405 }
406
407
408 uint32_t pixel_format = SDL_PIXELFORMAT_UNKNOWN;
409
410 if (is_succeed)
411 {
412 bstone::Log::write(
413 "VID: Looking up for a texture pixel format...");
414
415 const auto format_count = renderer_info.num_texture_formats;
416
417 for (auto i = decltype(format_count){}; i < format_count; ++i)
418 {
419 const auto format = renderer_info.texture_formats[i];
420
421 if (
422 SDL_PIXELTYPE(format) == SDL_PIXELTYPE_PACKED32 &&
423 SDL_PIXELLAYOUT(format) == SDL_PACKEDLAYOUT_8888 &&
424 SDL_ISPIXELFORMAT_ALPHA(format))
425 {
426 pixel_format = format;
427 break;
428 }
429 }
430
431 if (pixel_format == SDL_PIXELFORMAT_UNKNOWN)
432 {
433 bstone::Log::write_warning(
434 "Falling back to a predefined pixel format.");
435
436 pixel_format = SDL_PIXELFORMAT_ARGB8888;
437 }
438
439
440 bstone::Log::write(
441 "VID: Allocating a texture pixel format...");
442
443 ::sdl_texture_pixel_format = ::SDL_AllocFormat(
444 pixel_format);
445
446 if (!::sdl_texture_pixel_format)
447 {
448 is_succeed = false;
449
450 ::sdl_error_message = "VID: Failed to allocate a texture pixel format: ";
451 ::sdl_error_message += ::SDL_GetError();
452
453 bstone::Log::write_error(
454 ::SDL_GetError());
455 }
456 }
457
458 return is_succeed;
459 }
460
sdl_initialize_screen_texture()461 bool sdl_initialize_screen_texture()
462 {
463 bstone::Log::write(
464 "VID: Creating a screen texture...");
465
466 ::sdl_screen_texture = ::SDL_CreateTexture(
467 ::sdl_renderer,
468 ::sdl_texture_pixel_format->format,
469 SDL_TEXTUREACCESS_STREAMING,
470 ::vga_width,
471 ::vga_height);
472
473 if (!::sdl_screen_texture)
474 {
475 ::sdl_error_message = "VID: Failed to create a screen texture: ";
476 ::sdl_error_message += ::SDL_GetError();
477
478 bstone::Log::write(
479 ::SDL_GetError());
480
481 return false;
482 }
483
484 return true;
485 }
486
sdl_initialize_ui_texture()487 bool sdl_initialize_ui_texture()
488 {
489 bstone::Log::write(
490 "VID: Creating an UI texture...");
491
492 ::sdl_ui_texture = ::SDL_CreateTexture(
493 ::sdl_renderer,
494 ::sdl_texture_pixel_format->format,
495 SDL_TEXTUREACCESS_STREAMING,
496 ::vga_ref_width,
497 ::vga_ref_height);
498
499 if (!::sdl_ui_texture)
500 {
501 ::sdl_error_message = "VID: Failed to create an UI texture: ";
502 ::sdl_error_message += ::SDL_GetError();
503
504 bstone::Log::write(
505 ::SDL_GetError());
506
507 return false;
508 }
509
510 return true;
511 }
512
sdl_initialize_textures()513 bool sdl_initialize_textures()
514 {
515 bstone::Log::write(
516 "VID: Initializing textures...");
517
518
519 auto is_succeed = true;
520
521 if (is_succeed)
522 {
523 is_succeed = sdl_initialize_screen_texture();
524 }
525
526 if (is_succeed)
527 {
528 is_succeed = sdl_initialize_ui_texture();
529 }
530
531 return is_succeed;
532 }
533
sdl_update_palette(int first_index,int color_count)534 void sdl_update_palette(
535 int first_index,
536 int color_count)
537 {
538 for (int i = 0; i < color_count; ++i)
539 {
540 const auto color_index = first_index + i;
541 const auto& vga_color = ::sdl_vga_palette[color_index];
542 auto& sdl_color = ::sdl_palette[color_index];
543
544 sdl_color = ::SDL_MapRGB(
545 ::sdl_texture_pixel_format,
546 (255 * vga_color.r) / 63,
547 (255 * vga_color.g) / 63,
548 (255 * vga_color.b) / 63);
549 }
550 }
551
sdl_update_viewport()552 void sdl_update_viewport()
553 {
554 auto sdl_result = ::SDL_RenderSetLogicalSize(
555 ::sdl_renderer,
556 ::screen_width,
557 ::screen_height);
558
559 if (sdl_result != 0)
560 {
561 bstone::Log::write_error(
562 "VID: Failed to update a viewport.");
563 }
564 }
565
sdl_initialize_palette()566 void sdl_initialize_palette()
567 {
568 ::sdl_vga_palette.fill({});
569
570 ::sdl_update_palette(
571 0,
572 palette_color_count);
573 }
574
sdl_calculate_dimensions()575 void sdl_calculate_dimensions()
576 {
577 ::vga_height = (10 * ::window_height) / 12;
578 ::vga_height += 4 - 1;
579 ::vga_height /= 4;
580 ::vga_height *= 4;
581
582 if (::vid_widescreen)
583 {
584 ::vga_width = ::window_width;
585 ::vga_width += 4 - 1;
586 ::vga_width /= 4;
587 ::vga_width *= 4;
588 }
589 else
590 {
591 ::vga_width = (::vga_ref_width * ::vga_height) / ::vga_ref_height;
592 }
593
594 ::vga_area = ::vga_width * ::vga_height;
595
596 ::screen_width = ::vga_width;
597 ::screen_height = (12 * ::vga_height) / 10;
598 }
599
sdl_initialize_video()600 void sdl_initialize_video()
601 {
602 bstone::Log::write(
603 "VID: Initializing a system...");
604
605
606 bool is_custom_scale = false;
607
608 //
609 // Option "vid_windowed"
610 //
611
612 ::sdl_is_windowed = ::g_args.has_option(
613 "vid_windowed");
614
615 ::sdl_use_custom_window_position = false;
616
617
618 //
619 // Option "vid_window_x"
620 //
621
622 const auto& vid_window_x_str = ::g_args.get_option_value(
623 "vid_window_x");
624
625 if (bstone::StringHelper::lexical_cast(
626 vid_window_x_str,
627 ::sdl_window_x))
628 {
629 ::sdl_use_custom_window_position = true;
630 }
631
632
633 //
634 // Option "vid_window_y"
635 //
636
637 const auto& vid_window_y_str = ::g_args.get_option_value(
638 "vid_window_y");
639
640 if (bstone::StringHelper::lexical_cast(
641 vid_window_y_str,
642 ::sdl_window_y))
643 {
644 ::sdl_use_custom_window_position = true;
645 }
646
647
648 //
649 // Option "vid_mode"
650 //
651
652 std::string width_str;
653 std::string height_str;
654
655 ::g_args.get_option_values(
656 "vid_mode",
657 width_str,
658 height_str);
659
660 static_cast<void>(bstone::StringHelper::lexical_cast(
661 width_str,
662 ::window_width));
663
664 static_cast<void>(bstone::StringHelper::lexical_cast(
665 height_str,
666 ::window_height));
667
668 if (::window_width == 0)
669 {
670 ::window_width = ::default_window_width;
671 }
672
673 if (::window_height == 0)
674 {
675 ::window_height = ::default_window_height;
676 }
677
678 if (::window_width < ::vga_ref_width)
679 {
680 ::window_width = ::vga_ref_width;
681 }
682
683 if (::window_height < ::vga_ref_height_4x3)
684 {
685 ::window_height = ::vga_ref_height_4x3;
686 }
687
688
689 //
690 // Option "vid_scale"
691 //
692
693 const auto& vid_scale_str = ::g_args.get_option_value(
694 "vid_scale");
695
696 if (!vid_scale_str.empty())
697 {
698 int scale_value = 0;
699
700 if (bstone::StringHelper::lexical_cast(
701 vid_scale_str,
702 scale_value))
703 {
704 is_custom_scale = true;
705
706 if (scale_value < 1)
707 {
708 scale_value = 1;
709 }
710
711 ::vga_scale = scale_value;
712 }
713 }
714
715
716 int sdl_result = 0;
717
718 sdl_result = ::SDL_GetDesktopDisplayMode(
719 0,
720 &::display_mode);
721
722 if (sdl_result != 0)
723 {
724 ::Quit("VID: Failed to get a display mode.");
725 }
726
727 if (!::sdl_is_windowed)
728 {
729 ::window_width = ::display_mode.w;
730 ::window_height = ::display_mode.h;
731 }
732
733
734 ::sdl_calculate_dimensions();
735
736 bool is_succeed = true;
737
738 if (is_succeed)
739 {
740 is_succeed = ::sdl_initialize_window();
741 }
742
743 if (is_succeed)
744 {
745 is_succeed = ::sdl_initialize_renderer();
746 }
747
748 if (is_succeed)
749 {
750 is_succeed = ::sdl_initialize_textures();
751 }
752
753 if (is_succeed)
754 {
755 ::sdl_initialize_palette();
756 }
757
758 if (is_succeed)
759 {
760 ::sdl_initialize_vga_buffer();
761 ::sdl_initialize_ui_buffer();
762 }
763
764 if (is_succeed)
765 {
766 ::SDL_ShowWindow(
767 ::sdl_window);
768
769 ::in_grab_mouse(
770 true);
771 }
772 else
773 {
774 ::Quit(
775 ::sdl_error_message);
776 }
777 }
778
sdl_uninitialize_screen_texture()779 void sdl_uninitialize_screen_texture()
780 {
781 if (::sdl_screen_texture)
782 {
783 ::SDL_DestroyTexture(
784 ::sdl_screen_texture);
785
786 ::sdl_screen_texture = nullptr;
787 }
788 }
789
sdl_uninitialize_ui_texture()790 void sdl_uninitialize_ui_texture()
791 {
792 if (::sdl_ui_texture)
793 {
794 ::SDL_DestroyTexture(
795 ::sdl_ui_texture);
796
797 ::sdl_ui_texture = nullptr;
798 }
799 }
800
sdl_uninitialize_vga_buffer()801 void sdl_uninitialize_vga_buffer()
802 {
803 ::sdl_vga_buffer.clear();
804 ::sdl_vga_buffer.shrink_to_fit();
805
806 ::vga_memory = nullptr;
807 }
808
sdl_uninitialize_video()809 void sdl_uninitialize_video()
810 {
811 if (::sdl_texture_pixel_format)
812 {
813 ::SDL_FreeFormat(
814 ::sdl_texture_pixel_format);
815
816 ::sdl_texture_pixel_format = nullptr;
817 }
818
819 ::sdl_uninitialize_screen_texture();
820 ::sdl_uninitialize_ui_texture();
821
822 if (::sdl_renderer)
823 {
824 ::SDL_DestroyRenderer(
825 ::sdl_renderer);
826
827 ::sdl_renderer = nullptr;
828 }
829
830 if (::sdl_window)
831 {
832 ::SDL_DestroyWindow(
833 ::sdl_window);
834
835 ::sdl_window = nullptr;
836 }
837
838 ::sdl_uninitialize_vga_buffer();
839 }
840
sdl_refresh_screen()841 void sdl_refresh_screen()
842 {
843 int sdl_result = 0;
844
845 // HUD+3D stuff
846 //
847 if (::vid_is_hud)
848 {
849 const auto src_pixels = ::sdl_vga_buffer.data();
850 const auto src_pitch = ::vga_width;
851
852 void* dst_raw_pixels = nullptr;
853 int dst_pitch = 0;
854
855 sdl_result = ::SDL_LockTexture(
856 ::sdl_screen_texture,
857 nullptr,
858 &dst_raw_pixels,
859 &dst_pitch);
860
861 if (sdl_result != 0)
862 {
863 ::Quit(
864 "VID: Failed to lock a screen texture: {}",
865 ::SDL_GetError());
866 }
867
868 auto dst_pixels = static_cast<uint32_t*>(
869 dst_raw_pixels);
870
871 for (int y = 0; y < ::vga_height; ++y)
872 {
873 const auto src_line = &src_pixels[y * src_pitch];
874 auto dst_line = &dst_pixels[y * (dst_pitch / 4)];
875
876 for (int x = 0; x < ::vga_width; ++x)
877 {
878 dst_line[x] = sdl_palette[src_line[x]];
879 }
880 }
881
882 ::SDL_UnlockTexture(
883 ::sdl_screen_texture);
884 }
885
886
887 // 2D stuff
888 //
889 {
890 void* dst_raw_pixels = nullptr;
891 int dst_pitch = 0;
892
893 sdl_result = ::SDL_LockTexture(
894 ::sdl_ui_texture,
895 nullptr,
896 &dst_raw_pixels,
897 &dst_pitch);
898
899 if (sdl_result != 0)
900 {
901 ::Quit(
902 "VID: Failed to lock an UI texture: {}",
903 ::SDL_GetError());
904 }
905
906 const auto alpha_0_mask = ~sdl_texture_pixel_format->Amask;
907
908 auto dst_pixels = static_cast<uint32_t*>(
909 dst_raw_pixels);
910
911 for (int y = 0; y < ::vga_ref_height; ++y)
912 {
913 auto dst_line = &dst_pixels[y * (dst_pitch / 4)];
914
915 for (int x = 0; x < ::vga_ref_width; ++x)
916 {
917 const auto src_offset = (y * ::vga_ref_width) + x;
918 auto dst_color = ::sdl_palette[::sdl_ui_buffer[src_offset]];
919
920 if (::vid_is_hud)
921 {
922 if (!::sdl_mask_buffer[src_offset])
923 {
924 dst_color &= alpha_0_mask;
925 }
926 }
927
928 dst_line[x] = dst_color;
929 }
930 }
931
932 ::SDL_UnlockTexture(
933 ::sdl_ui_texture);
934 }
935
936
937 // Clear all
938 //
939 sdl_result = ::SDL_RenderClear(
940 sdl_renderer);
941
942 if (sdl_result != 0)
943 {
944 ::Quit(
945 "VID: Failed to clear a render target: {}",
946 ::SDL_GetError());
947 }
948
949
950 // Copy HUD+3D stuff
951 //
952 if (::vid_is_hud)
953 {
954 sdl_result = ::SDL_RenderCopy(
955 sdl_renderer,
956 sdl_screen_texture,
957 nullptr,
958 nullptr);
959
960 if (sdl_result != 0)
961 {
962 ::Quit(
963 "VID: Failed to copy a screen texture on a render target: {}",
964 ::SDL_GetError());
965 }
966 }
967
968
969 // Copy 2D stuff
970 //
971
972 if (::vid_is_hud)
973 {
974 sdl_result = ::SDL_SetTextureBlendMode(
975 ::sdl_ui_texture,
976 SDL_BLENDMODE_BLEND);
977
978 if (sdl_result != 0)
979 {
980 ::Quit(
981 "VID: Failed to set blend mode for an UI texture: {}",
982 ::SDL_GetError());
983 }
984 }
985
986 sdl_result = ::SDL_RenderCopy(
987 ::sdl_renderer,
988 ::sdl_ui_texture,
989 nullptr,
990 nullptr);
991
992 if (sdl_result != 0)
993 {
994 ::Quit(
995 "VID: Failed to copy an UI texture on a render target: {}",
996 ::SDL_GetError());
997 }
998
999 if (::vid_is_hud)
1000 {
1001 sdl_result = ::SDL_SetTextureBlendMode(
1002 ::sdl_ui_texture,
1003 SDL_BLENDMODE_NONE);
1004
1005 if (sdl_result != 0)
1006 {
1007 ::Quit(
1008 "VID: Failed to set blend mode for an UI texture: {}",
1009 ::SDL_GetError());
1010 }
1011 }
1012
1013
1014 // Present
1015 //
1016 ::SDL_RenderPresent(
1017 sdl_renderer);
1018
1019 if (sdl_result != 0)
1020 {
1021 ::Quit(
1022 "VID: Failed to present a render target: {}",
1023 ::SDL_GetError());
1024 }
1025 }
1026
sdl_check_vsync()1027 void sdl_check_vsync()
1028 {
1029 constexpr int draw_count = 10;
1030
1031 constexpr int duration_tolerance_pct = 25;
1032
1033 const int expected_duration_ms =
1034 (1000 * draw_count) / ::display_mode.refresh_rate;
1035
1036 const int min_expected_duration_ms =
1037 ((100 - duration_tolerance_pct) * expected_duration_ms) / 100;
1038
1039 const auto before_timestamp = Clock::now();
1040
1041 for (int i = 0; i < draw_count; ++i)
1042 {
1043 ::sdl_refresh_screen();
1044 }
1045
1046 const auto after_timestamp = Clock::now();
1047
1048 const auto duration = after_timestamp - before_timestamp;
1049
1050 const auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
1051 duration).count();
1052
1053 ::vid_has_vsync = (duration_ms >= min_expected_duration_ms);
1054 }
1055
sdl_update_widescreen()1056 void sdl_update_widescreen()
1057 {
1058 ::sdl_uninitialize_screen_texture();
1059 ::sdl_uninitialize_vga_buffer();
1060 ::sdl_calculate_dimensions();
1061 ::sdl_initialize_vga_buffer();
1062 ::sdl_initialize_screen_texture();
1063 ::sdl_update_viewport();
1064 }
1065
1066
1067 } // namespace
1068 // BBi
1069
1070
1071 // ===========================================================================
1072
1073 // asm
1074
VL_WaitVBL(uint32_t vbls)1075 void VL_WaitVBL(
1076 uint32_t vbls)
1077 {
1078 if (vbls == 0)
1079 {
1080 return;
1081 }
1082
1083 ::sys_sleep_for(1000 * vbls / TickBase);
1084 }
1085
1086 // ===========================================================================
1087
1088
1089 // BBi Moved from jm_free.cpp
VL_Startup()1090 void VL_Startup()
1091 {
1092 ::sdl_initialize_video();
1093 ::sdl_refresh_screen();
1094
1095 ::in_handle_events();
1096
1097 ::sdl_check_vsync();
1098 }
1099 // BBi
1100
VL_Shutdown()1101 void VL_Shutdown()
1102 {
1103 ::sdl_uninitialize_video();
1104 }
1105
1106 // ===========================================================================
1107
1108 /*
1109 =============================================================================
1110
1111 PALETTE OPS
1112
1113 To avoid snow, do a WaitVBL BEFORE calling these
1114
1115 =============================================================================
1116 */
1117
VL_FillPalette(uint8_t red,uint8_t green,uint8_t blue)1118 void VL_FillPalette(
1119 uint8_t red,
1120 uint8_t green,
1121 uint8_t blue)
1122 {
1123 for (auto& vga_color : ::sdl_vga_palette)
1124 {
1125 vga_color.r = red;
1126 vga_color.g = green;
1127 vga_color.b = blue;
1128 }
1129
1130 ::sdl_update_palette(
1131 0,
1132 palette_color_count);
1133 }
1134
VL_SetPalette(int first,int count,const uint8_t * palette)1135 void VL_SetPalette(
1136 int first,
1137 int count,
1138 const uint8_t* palette)
1139 {
1140 for (int i = 0; i < count; ++i)
1141 {
1142 auto& vga_color = ::sdl_vga_palette[first + i];
1143
1144 vga_color.r = palette[(3 * i) + 0];
1145 vga_color.g = palette[(3 * i) + 1];
1146 vga_color.b = palette[(3 * i) + 2];
1147 }
1148
1149 ::sdl_update_palette(
1150 first,
1151 count);
1152 }
1153
VL_GetPalette(int first,int count,uint8_t * palette)1154 void VL_GetPalette(
1155 int first,
1156 int count,
1157 uint8_t* palette)
1158 {
1159 for (int i = 0; i < count; ++i)
1160 {
1161 const auto& vga_color = ::sdl_vga_palette[first + i];
1162
1163 palette[(3 * i) + 0] = vga_color.r;
1164 palette[(3 * i) + 1] = vga_color.g;
1165 palette[(3 * i) + 2] = vga_color.b;
1166 }
1167 }
1168
1169 /*
1170 =================
1171 =
1172 = VL_FadeOut
1173 =
1174 = Fades the current palette to the given color in the given number of steps
1175 =
1176 =================
1177 */
1178
VL_FadeOut(int start,int end,int red,int green,int blue,int steps)1179 void VL_FadeOut(
1180 int start,
1181 int end,
1182 int red,
1183 int green,
1184 int blue,
1185 int steps)
1186 {
1187 int orig;
1188 int delta;
1189
1190 ::VL_GetPalette(
1191 0,
1192 256,
1193 &::palette1[0][0]);
1194
1195 std::uninitialized_copy_n(
1196 &::palette1[0][0],
1197 768,
1198 &::palette2[0][0]);
1199
1200 //
1201 // fade through intermediate frames
1202 //
1203 for (int i = 0; i < steps; ++i)
1204 {
1205 auto origptr = &::palette1[start][0];
1206 auto newptr = &::palette2[start][0];
1207
1208 for (int j = start; j <= end; ++j)
1209 {
1210 orig = *origptr++;
1211 delta = red - orig;
1212 *newptr++ = static_cast<uint8_t>(orig + ((delta * i) / steps));
1213
1214 orig = *origptr++;
1215 delta = green - orig;
1216 *newptr++ = static_cast<uint8_t>(orig + ((delta * i) / steps));
1217
1218 orig = *origptr++;
1219 delta = blue - orig;
1220 *newptr++ = static_cast<uint8_t>(orig + ((delta * i) / steps));
1221 }
1222
1223 ::VL_SetPalette(
1224 0,
1225 256,
1226 &::palette2[0][0]);
1227
1228 ::VL_RefreshScreen();
1229
1230 if (!::vid_has_vsync)
1231 {
1232 ::VL_WaitVBL(1);
1233 }
1234 }
1235
1236 //
1237 // final color
1238 //
1239 ::VL_FillPalette(
1240 static_cast<uint8_t>(red),
1241 static_cast<uint8_t>(green),
1242 static_cast<uint8_t>(blue));
1243
1244 if (!::vid_has_vsync)
1245 {
1246 ::VL_WaitVBL(1);
1247 }
1248
1249 ::screenfaded = true;
1250 }
1251
VL_FadeIn(int start,int end,const uint8_t * palette,int steps)1252 void VL_FadeIn(
1253 int start,
1254 int end,
1255 const uint8_t* palette,
1256 int steps)
1257 {
1258 ::VL_GetPalette(
1259 0,
1260 256,
1261 &::palette1[0][0]);
1262
1263 std::uninitialized_copy_n(
1264 &::palette1[0][0],
1265 768,
1266 &::palette2[0][0]);
1267
1268 start *= 3;
1269 end = (end * 3) + 2;
1270
1271 //
1272 // fade through intermediate frames
1273 //
1274 for (int i = 0; i < steps; ++i)
1275 {
1276 for (int j = start; j <= end; ++j)
1277 {
1278 int delta = palette[j] - ::palette1[0][j];
1279
1280 ::palette2[0][j] =
1281 static_cast<uint8_t>(::palette1[0][j] + ((delta * i) / steps));
1282 }
1283
1284 ::VL_SetPalette(
1285 0,
1286 256,
1287 &::palette2[0][0]);
1288
1289 ::VL_RefreshScreen();
1290
1291 if (!::vid_has_vsync)
1292 {
1293 ::VL_WaitVBL(1);
1294 }
1295 }
1296
1297 //
1298 // final color
1299 //
1300 ::VL_SetPalette(0, 256, palette);
1301
1302 if (!::vid_has_vsync)
1303 {
1304 ::VL_WaitVBL(1);
1305 }
1306
1307 ::screenfaded = false;
1308 }
1309
VL_SetPaletteIntensity(int start,int end,const uint8_t * palette,int intensity)1310 void VL_SetPaletteIntensity(
1311 int start,
1312 int end,
1313 const uint8_t* palette,
1314 int intensity)
1315 {
1316 auto cmap = &::palette1[0][0] + (start * 3);
1317
1318 intensity = 63 - intensity;
1319
1320 for (int loop = start; loop <= end; ++loop)
1321 {
1322 int red = (*palette++) - intensity;
1323
1324 if (red < 0)
1325 {
1326 red = 0;
1327 }
1328
1329 *cmap++ = static_cast<uint8_t>(red);
1330
1331 int green = *palette++ - intensity;
1332
1333 if (green < 0)
1334 {
1335 green = 0;
1336 }
1337
1338 *cmap++ = static_cast<uint8_t>(green);
1339
1340 int blue = *palette++ - intensity;
1341
1342 if (blue < 0)
1343 {
1344 blue = 0;
1345 }
1346
1347 *cmap++ = static_cast<uint8_t>(blue);
1348 }
1349
1350 ::VL_SetPalette(
1351 start,
1352 end - start + 1,
1353 &::palette1[0][0]);
1354 }
1355
1356 /*
1357 =============================================================================
1358
1359 PIXEL OPS
1360
1361 =============================================================================
1362 */
1363
VL_Plot(int x,int y,uint8_t color,const bool is_transparent)1364 void VL_Plot(
1365 int x,
1366 int y,
1367 uint8_t color,
1368 const bool is_transparent)
1369 {
1370 const auto offset = (y * ::vga_ref_width) + x;
1371
1372 ::sdl_ui_buffer[offset] = color;
1373 ::sdl_mask_buffer[offset] = !is_transparent;
1374 }
1375
VL_Hlin(int x,int y,int width,uint8_t color)1376 void VL_Hlin(
1377 int x,
1378 int y,
1379 int width,
1380 uint8_t color)
1381 {
1382 ::VL_Bar(x, y, width, 1, color);
1383 }
1384
VL_Vlin(int x,int y,int height,uint8_t color)1385 void VL_Vlin(
1386 int x,
1387 int y,
1388 int height,
1389 uint8_t color)
1390 {
1391 ::VL_Bar(x, y, 1, height, color);
1392 }
1393
VL_Bar(int x,int y,int width,int height,uint8_t color,const bool is_transparent)1394 void VL_Bar(
1395 int x,
1396 int y,
1397 int width,
1398 int height,
1399 uint8_t color,
1400 const bool is_transparent)
1401 {
1402 if (x == 0 && width == ::vga_ref_width)
1403 {
1404 const auto offset = y * ::vga_ref_width;
1405 const auto count = height * ::vga_ref_width;
1406
1407 std::uninitialized_fill(
1408 ::sdl_ui_buffer.begin() + offset,
1409 ::sdl_ui_buffer.begin() + offset + count,
1410 color);
1411
1412 std::uninitialized_fill(
1413 ::sdl_mask_buffer.begin() + offset,
1414 ::sdl_mask_buffer.begin() + offset + count,
1415 !is_transparent);
1416 }
1417 else
1418 {
1419 for (int i = 0; i < height; ++i)
1420 {
1421 const auto offset = ((y + i) * ::vga_ref_width) + x;
1422
1423 std::uninitialized_fill(
1424 ::sdl_ui_buffer.begin() + offset,
1425 ::sdl_ui_buffer.begin() + offset + width,
1426 color);
1427
1428 std::uninitialized_fill(
1429 ::sdl_mask_buffer.begin() + offset,
1430 ::sdl_mask_buffer.begin() + offset + width,
1431 !is_transparent);
1432 }
1433 }
1434 }
1435
1436 /*
1437 ============================================================================
1438
1439 MEMORY OPS
1440
1441 ============================================================================
1442 */
1443
VL_MemToLatch(const uint8_t * source,int width,int height,int dest)1444 void VL_MemToLatch(
1445 const uint8_t* source,
1446 int width,
1447 int height,
1448 int dest)
1449 {
1450 for (int p = 0; p < 4; ++p)
1451 {
1452 for (int h = 0; h < height; ++h)
1453 {
1454 for (int w = p; w < width; w += 4)
1455 {
1456 const auto pixel = *source++;
1457 const auto offset = dest + ((h * width) + w);
1458
1459 ::latches_cache[offset] = pixel;
1460 }
1461 }
1462 }
1463 }
1464
VL_MemToScreen(const uint8_t * source,int width,int height,int x,int y)1465 void VL_MemToScreen(
1466 const uint8_t* source,
1467 int width,
1468 int height,
1469 int x,
1470 int y)
1471 {
1472 for (int p = 0; p < 4; ++p)
1473 {
1474 for (int h = 0; h < height; ++h)
1475 {
1476 for (int w = p; w < width; w += 4)
1477 {
1478 ::VL_Plot(x + w, y + h, *source++);
1479 }
1480 }
1481 }
1482 }
1483
VL_MaskMemToScreen(const uint8_t * source,int width,int height,int x,int y,uint8_t mask)1484 void VL_MaskMemToScreen(
1485 const uint8_t* source,
1486 int width,
1487 int height,
1488 int x,
1489 int y,
1490 uint8_t mask)
1491 {
1492 for (int p = 0; p < 4; ++p)
1493 {
1494 for (int h = 0; h < height; ++h)
1495 {
1496 for (int w = p; w < width; w += 4)
1497 {
1498 const auto color = *source++;
1499
1500 if (color != mask)
1501 {
1502 ::VL_Plot(x + w, y + h, color);
1503 }
1504 }
1505 }
1506 }
1507 }
1508
VL_ScreenToMem(uint8_t * dest,int width,int height,int x,int y)1509 void VL_ScreenToMem(
1510 uint8_t* dest,
1511 int width,
1512 int height,
1513 int x,
1514 int y)
1515 {
1516 for (int p = 0; p < 4; ++p)
1517 {
1518 for (int h = 0; h < height; ++h)
1519 {
1520 for (int w = p; w < width; w += 4)
1521 {
1522 *dest++ = ::vl_get_pixel(::bufferofs, x + w, y + h);
1523 }
1524 }
1525 }
1526 }
1527
VL_LatchToScreen(int source,int width,int height,int x,int y)1528 void VL_LatchToScreen(
1529 int source,
1530 int width,
1531 int height,
1532 int x,
1533 int y)
1534 {
1535 for (int h = 0; h < height; ++h)
1536 {
1537 const auto src_offset = source + (h * width);
1538 const auto dst_offset = (::vga_ref_width * (y + h)) + x;
1539
1540 std::uninitialized_copy(
1541 ::latches_cache.cbegin() + src_offset,
1542 ::latches_cache.cbegin() + src_offset + width,
1543 ::sdl_ui_buffer.begin() + dst_offset);
1544
1545 std::uninitialized_fill(
1546 ::sdl_mask_buffer.begin() + dst_offset,
1547 ::sdl_mask_buffer.begin() + dst_offset + width,
1548 true);
1549 }
1550 }
1551
VL_ScreenToScreen(int source,int dest,int width,int height)1552 void VL_ScreenToScreen(
1553 int source,
1554 int dest,
1555 int width,
1556 int height)
1557 {
1558 for (int h = 0; h < height; ++h)
1559 {
1560 const auto src_offset = source + (h * ::vga_ref_width);
1561 const auto dst_offset = dest + (h * ::vga_ref_width);
1562
1563 std::uninitialized_copy(
1564 ::sdl_ui_buffer.cbegin() + src_offset,
1565 ::sdl_ui_buffer.cbegin() + src_offset + width,
1566 ::sdl_ui_buffer.begin() + dst_offset);
1567
1568 std::uninitialized_fill(
1569 ::sdl_mask_buffer.begin() + dst_offset,
1570 ::sdl_mask_buffer.begin() + dst_offset + width,
1571 true);
1572 }
1573 }
1574
1575
JM_VGALinearFill(int start,int length,uint8_t fill)1576 void JM_VGALinearFill(
1577 int start,
1578 int length,
1579 uint8_t fill)
1580 {
1581 std::uninitialized_fill(
1582 ::sdl_ui_buffer.begin() + start,
1583 ::sdl_ui_buffer.begin() + start + length,
1584 fill);
1585
1586 std::uninitialized_fill(
1587 ::sdl_mask_buffer.begin() + start,
1588 ::sdl_mask_buffer.begin() + start + length,
1589 true);
1590 }
1591
VL_RefreshScreen()1592 void VL_RefreshScreen()
1593 {
1594 ::sdl_refresh_screen();
1595 }
1596
VH_UpdateScreen()1597 void VH_UpdateScreen()
1598 {
1599 ::sdl_refresh_screen();
1600 }
1601
vl_get_offset(int base_offset,int x,int y)1602 int vl_get_offset(
1603 int base_offset,
1604 int x,
1605 int y)
1606 {
1607 return base_offset + (y * ::vga_width) + x;
1608 }
1609
vl_get_pixel(int base_offset,int x,int y)1610 uint8_t vl_get_pixel(
1611 int base_offset,
1612 int x,
1613 int y)
1614 {
1615 return ::sdl_ui_buffer[(y * ::vga_ref_width) + x];
1616 }
1617
vl_minimize_fullscreen_window(bool value)1618 void vl_minimize_fullscreen_window(
1619 bool value)
1620 {
1621 if (value)
1622 {
1623 ::SDL_MinimizeWindow(
1624 ::sdl_window);
1625 }
1626 else
1627 {
1628 ::SDL_RestoreWindow(
1629 ::sdl_window);
1630 }
1631 }
1632
vl_update_widescreen()1633 void vl_update_widescreen()
1634 {
1635 ::sdl_update_widescreen();
1636 }
1637
vid_set_ui_mask(bool value)1638 void vid_set_ui_mask(
1639 bool value)
1640 {
1641 ::sdl_mask_buffer.fill(
1642 value);
1643 }
1644
vid_set_ui_mask(int x,int y,int width,int height,bool value)1645 void vid_set_ui_mask(
1646 int x,
1647 int y,
1648 int width,
1649 int height,
1650 bool value)
1651 {
1652 for (int h = 0; h < height; ++h)
1653 {
1654 const auto offset = ((y + h) * ::vga_ref_width) + x;
1655
1656 std::uninitialized_fill(
1657 ::sdl_mask_buffer.begin() + offset,
1658 ::sdl_mask_buffer.begin() + offset + width,
1659 value);
1660 }
1661 }
1662
vid_set_ui_mask_3d(bool value)1663 void vid_set_ui_mask_3d(
1664 bool value)
1665 {
1666 ::vid_set_ui_mask(
1667 0,
1668 ::ref_3d_view_top,
1669 ::vga_ref_width,
1670 ::ref_3d_view_height,
1671 value);
1672 }
1673
vid_clear_3d()1674 void vid_clear_3d()
1675 {
1676 std::uninitialized_fill(
1677 ::sdl_vga_buffer.begin(),
1678 ::sdl_vga_buffer.end(),
1679 0);
1680 }
1681
vid_export_ui(VgaBuffer & dst_buffer)1682 void vid_export_ui(
1683 VgaBuffer& dst_buffer)
1684 {
1685 dst_buffer = ::sdl_ui_buffer;
1686 }
1687
vid_import_ui(const VgaBuffer & src_buffer,bool is_transparent)1688 void vid_import_ui(
1689 const VgaBuffer& src_buffer,
1690 bool is_transparent)
1691 {
1692 ::sdl_ui_buffer = src_buffer;
1693 ::vid_set_ui_mask(!is_transparent);
1694 }
1695
vid_export_ui_mask(UiMaskBuffer & dst_buffer)1696 void vid_export_ui_mask(
1697 UiMaskBuffer& dst_buffer)
1698 {
1699 dst_buffer = ::sdl_mask_buffer;
1700 }
1701
vid_import_ui_mask(const UiMaskBuffer & src_buffer)1702 void vid_import_ui_mask(
1703 const UiMaskBuffer& src_buffer)
1704 {
1705 ::sdl_mask_buffer = src_buffer;
1706 }
1707
vid_draw_ui_sprite(const int sprite_id,const int center_x,const int center_y,const int new_side)1708 void vid_draw_ui_sprite(
1709 const int sprite_id,
1710 const int center_x,
1711 const int center_y,
1712 const int new_side)
1713 {
1714 constexpr auto side = bstone::Sprite::side;
1715
1716 const auto sprite_ptr = ::vid_sprite_cache.cache(
1717 sprite_id);
1718
1719 const auto sprite_width = sprite_ptr->get_width();
1720 const auto sprite_height = sprite_ptr->get_height();
1721
1722 const auto left = sprite_ptr->get_left();
1723 const auto x1 = center_x + ((new_side * (left - (side / 2))) / side);
1724 const auto x2 = x1 + ((sprite_width * new_side) / side);
1725
1726 const auto top = sprite_ptr->get_top();
1727 const auto y1 = center_y + ((new_side * (top - (side / 2))) / side) - 2;
1728 const auto y2 = y1 + ((sprite_height * new_side) / side);
1729
1730 for (int x = x1; x < x2; ++x)
1731 {
1732 if (x < 0)
1733 {
1734 continue;
1735 }
1736
1737 if (x >= ::vga_ref_width)
1738 {
1739 break;
1740 }
1741
1742 const auto column_index = ((sprite_width - 1) * (x - x1)) / (x2 - x1 - 1);
1743 const auto column = sprite_ptr->get_column(column_index);
1744
1745 for (int y = y1; y < y2; ++y)
1746 {
1747 if (y < 0)
1748 {
1749 continue;
1750 }
1751
1752 if (y >= ::vga_ref_height)
1753 {
1754 break;
1755 }
1756
1757 const auto row_index = ((sprite_height - 1) * (y - y1)) / (y2 - y1 - 1);
1758 const auto sprite_color = column[row_index];
1759
1760 if (sprite_color < 0)
1761 {
1762 continue;
1763 }
1764
1765 const auto color_index = static_cast<uint8_t>(sprite_color);
1766
1767 ::VL_Plot(x, y, color_index);
1768 }
1769 }
1770 }
1771 // BBi
1772