/* * Copyright 2022 Frederico de Oliveira Linhares * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "core.h" #include /* Stretches the image to fill the screen vertically, horizontally, or both; without changing the game aspect ratio. */ void cg_Engine_calculate_full_scale() { double screen_ratio, game_ratio, scale; screen_ratio = (double)cg_core.screen_width/(double)cg_core.screen_height; game_ratio = (double)cg_core.game_width/(double)cg_core.game_height; // If screen is proportionally taller than game. if(screen_ratio < game_ratio) { scale = (double)cg_core.screen_width/(double)cg_core.game_width; cg_core.screen_rect.w = cg_core.game_width * scale; cg_core.screen_rect.h = cg_core.game_height * scale; cg_core.screen_rect.x = 0; cg_core.screen_rect.y = cg_core.screen_height/2 - cg_core.screen_rect.h/2; } // If screen is proportionally wider than game. else if(screen_ratio > game_ratio) { scale = (double)cg_core.screen_height/(double)cg_core.game_height; cg_core.screen_rect.w = cg_core.game_width * scale; cg_core.screen_rect.h = cg_core.game_height * scale; cg_core.screen_rect.x = cg_core.screen_width/2 - cg_core.screen_rect.w/2; cg_core.screen_rect.y = 0; } // If they have the same aspect ratio. else { cg_core.screen_rect.x = 0; cg_core.screen_rect.y = 0; cg_core.screen_rect.w = cg_core.screen_width; cg_core.screen_rect.h = cg_core.screen_height; } } static SDL_bool load_variables(void *obj, LoaderStack *ls) { YAML::Node root = YAML::LoadFile(cg_core.config_file); std::string game_name = root["name"].as().c_str(); cg_core.game_name = new char[game_name.size() + 1]; strcpy(cg_core.game_name, game_name.c_str()); // Based on NES. cg_core.game_width = root["game"]["width"].as(); cg_core.game_height = root["game"]["height"].as(); cg_core.screen_width = root["screen"]["width"].as(); cg_core.screen_height = root["screen"]["height"].as(); cg_core.fps = 30; cg_core.max_frame_duration_ms = 1000 / cg_core.fps; return SDL_TRUE; } static void unload_variables(void *obj, LoaderStack *ls) { delete[] cg_core.game_name; } static SDL_bool load_sdl(void *obj, LoaderStack *ls) { if(SDL_Init(SDL_INIT_EVERYTHING) < 0) { const char* base_error = "SDL could not initialize! SDL Error → "; LoaderStack_set_error(ls, base_error, SDL_GetError()); return SDL_FALSE; } return SDL_TRUE; } static void unload_sdl(void *obj, LoaderStack *ls) { SDL_Quit(); } static SDL_bool load_sdl_image(void *obj, LoaderStack *ls) { int flags = IMG_INIT_JPG|IMG_INIT_PNG|IMG_INIT_TIF; if(!(IMG_Init(flags) & flags)) { const char* base_error = "Could not initialize SDL image → "; LoaderStack_set_error(ls, base_error, IMG_GetError()); return SDL_FALSE; } return SDL_TRUE; } static void unload_sdl_image(void *obj, LoaderStack *ls) { IMG_Quit(); } static SDL_bool load_sdl_mixer(void *obj, LoaderStack *ls) { int flags = MIX_INIT_OGG|MIX_INIT_MOD; int initted = Mix_Init(flags); if(initted&flags != flags) { const char* base_error = "Could not initialize SDL mixer → "; LoaderStack_set_error(ls, base_error, Mix_GetError()); return SDL_FALSE; } return SDL_TRUE; } static void unload_sdl_mixer(void *obj, LoaderStack *ls) { while(Mix_Init(0)) Mix_Quit(); } static SDL_bool load_sdl_open_audio(void *obj, LoaderStack *ls) { if(Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 1024) == -1) { const char* base_error = "Could not open SDL mixer audio → "; LoaderStack_set_error(ls, base_error, Mix_GetError()); return SDL_FALSE; } return SDL_TRUE; } static void unload_sdl_open_audio(void *obj, LoaderStack *ls) { Mix_CloseAudio(); } static SDL_bool load_sdl_ttf(void *obj, LoaderStack *ls) { if(TTF_Init()==-1) { const char* base_error = "Could not initialize SDL ttf → "; LoaderStack_set_error(ls, base_error, TTF_GetError()); return SDL_FALSE; } return SDL_TRUE; } static void unload_sdl_ttf(void *obj, LoaderStack *ls) { TTF_Quit(); } static SDL_bool load_window(void *obj, LoaderStack *ls) { cg_core.window = NULL; cg_core.window = SDL_CreateWindow( cg_core.game_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, cg_core.screen_width, cg_core.screen_height, SDL_WINDOW_SHOWN); if(cg_core.window == NULL) { const char* base_error = "Window could not be created! SDL_Error → "; LoaderStack_set_error(ls, base_error, SDL_GetError()); return SDL_FALSE; } return SDL_TRUE; } static void unload_window(void *obj, LoaderStack *ls) { SDL_DestroyWindow(cg_core.window); } static SDL_bool load_sdl_renderer(void *obj, LoaderStack *ls) { cg_core.renderer = NULL; cg_core.renderer = SDL_CreateRenderer( cg_core.window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); if(cg_core.renderer == NULL) { const char* base_error = "Could not create SDL renderer → "; LoaderStack_set_error(ls, base_error, SDL_GetError()); return SDL_FALSE; } return SDL_TRUE; } static void unload_sdl_renderer(void *obj, LoaderStack *ls) { SDL_DestroyRenderer(cg_core.renderer); } static SDL_bool load_pre_screen(void *obj, LoaderStack *ls) { cg_core.pre_screen_buffer = SDL_CreateTexture( cg_core.renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, cg_core.game_width, cg_core.game_height); if(cg_core.pre_screen_buffer == NULL) { const char* base_error = "Could not create renderering buffer → "; LoaderStack_set_error(ls, base_error, SDL_GetError()); return SDL_FALSE; } cg_Engine_calculate_full_scale(); SDL_SetRenderTarget(cg_core.renderer, cg_core.pre_screen_buffer); return SDL_TRUE; } static void unload_pre_screen(void *obj, LoaderStack *ls) { SDL_DestroyTexture(cg_core.pre_screen_buffer); } void cg_Core_init(LoaderStack *loader) { LoaderStack_constructor(loader, NULL); LoaderStack_add(loader, &load_variables, &unload_variables); LoaderStack_add(loader, &load_sdl, &unload_sdl); LoaderStack_add(loader, &load_sdl_image, &unload_sdl_image); LoaderStack_add(loader, &load_sdl_mixer, &unload_sdl_mixer); LoaderStack_add(loader, &load_sdl_open_audio, &unload_sdl_open_audio); LoaderStack_add(loader, &load_sdl_ttf, &unload_sdl_ttf); LoaderStack_add(loader, &load_window, &unload_window); LoaderStack_add(loader, &load_sdl_renderer, &unload_sdl_renderer); LoaderStack_add(loader, &load_pre_screen, &unload_pre_screen); } SDL_bool cg_Core_load(LoaderStack *loader) { if(!LoaderStack_load(loader)) return SDL_FALSE; return SDL_TRUE; } void cg_Core_unload(LoaderStack *loader) { LoaderStack_destructor(loader); }