rewrite config, options, work with path

This commit is contained in:
Ondřej Novák 2025-01-31 12:46:48 +01:00
parent 4a0c7d4fd0
commit 77f1700902
30 changed files with 466 additions and 532 deletions

View file

@ -14,6 +14,7 @@ target_sources(skeldal_platform PRIVATE
config.cpp
error.cpp
timer.cpp
getopt.c
)
# Podmínky pro platformu Windows

View file

@ -5,10 +5,16 @@
#include <map>
#include <string>
#include <string_view>
typedef struct ini_config_section_tag {
using Section = std::map<std::string, std::string, std::less<>>;
Section data;
} INI_CONFIG_SECTION;
typedef struct ini_config_tag {
using Section = std::map<std::string, std::string, std::less<>>;
using Config = std::map<std::string, Section , std::less<>>;
using Config = std::map<std::string, INI_CONFIG_SECTION , std::less<>>;
Config data;
} INI_CONFIG;
@ -44,7 +50,7 @@ void parseIniStream(std::istream& input, Callback &&callback) {
if (eqPos != std::string_view::npos) {
std::string_view key_view = trim(line_view.substr(0, eqPos));
std::string_view value_view = trim(line_view.substr(eqPos + 1));
callback(currentSection, std::string(key_view), std::string(value_view));
callback(currentSection, key_view, value_view);
}
}
}
@ -59,9 +65,9 @@ INI_CONFIG* ini_open(const char *filename) {
parseIniStream(input, [&](std::string_view section, std::string_view key, std::string_view value) {
INI_CONFIG::Config::iterator iter = c->data.find(section);
if (iter == c->data.end()) {
iter = c->data.emplace(std::string(section), INI_CONFIG::Section()).first;
iter = c->data.emplace(std::string(section), INI_CONFIG_SECTION()).first;
}
iter->second.emplace(std::string(key), std::string(value));
iter->second.data.emplace(std::string(key), std::string(value));
});
return c;
}
@ -73,15 +79,14 @@ void ini_close(INI_CONFIG *config) {
const INI_CONFIG_SECTION* ini_section_open(const INI_CONFIG *cfg, const char *section) {
auto iter = cfg->data.find(std::string_view(section));
if (iter == cfg->data.end()) return NULL;
else return reinterpret_cast<const INI_CONFIG_SECTION *>(&iter->second);
else return &iter->second;
}
const char* ini_find_key(const INI_CONFIG_SECTION *section,
const char *key) {
if (section == NULL) return NULL;
const INI_CONFIG::Section *s = reinterpret_cast<const INI_CONFIG::Section *>(section);
auto iter = s->find(std::string_view(key));
if (iter == s->end()) return NULL;
auto iter = section->data.find(std::string_view(key));
if (iter == section->data.end()) return NULL;
return iter->second.c_str();
}
@ -171,8 +176,7 @@ int ini_get_boolean(const INI_CONFIG_SECTION *section, const char *key,
}
void ini_replace_key( INI_CONFIG_SECTION *section, const char *key, const char *value) {
auto s = reinterpret_cast<INI_CONFIG::Section *>(section);
(*s)[std::string(key)] = std::string(value);
section->data[std::string(key)] = std::string(value);
}
INI_CONFIG_SECTION* ini_create_section(INI_CONFIG *cfg, const char *section_name) {
@ -189,7 +193,7 @@ void ini_store_to_file(const INI_CONFIG *config, const char *filename) {
std::ofstream out(filename,std::ios::out|std::ios::trunc);
for (const auto &[sname, s]: config->data) {
out << '[' << sname << ']' << std::endl;
for (const auto &[k,v]: s) {
for (const auto &[k,v]: s.data) {
out << k << '=' << v << std::endl;
}
}

View file

@ -1,10 +1,10 @@
#include "legacy_coroutines.h"
#include <cassert>
#include <cstdarg>
#include <cstdint>
#include <iostream>
extern "C" {
#include "error.h"
}
#include "platform.h"
@ -15,8 +15,9 @@ void display_error(const char *text) {
static std::uint32_t gtick = get_game_tick_count();
void send_log_impl(int task, const char *format, ...) {
void send_log_impl(const char *format, ...) {
va_list args;
int task = q_current_task();
char buff2[1000];
va_start(args, format);
auto reltik = get_game_tick_count() - gtick;

View file

@ -1,4 +1,14 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void display_error(const char *text);
void send_log_impl(int task, const char *format, ...);
void send_log_impl(const char *format, ...);
#ifdef __cplusplus
}
#endif

View file

@ -2,6 +2,7 @@
#include <cstdarg>
#include <filesystem>
#include "../libs/logfile.h"
std::filesystem::path break_and_compose_path(const std::string_view &pathname, char sep) {
@ -67,6 +68,8 @@ FILE *fopen_icase(const char *pathname, const char *mode) {
static thread_local std::string build_pathname_buffer;
const char * build_pathname(size_t nparts, const char *part1, ...) {
va_list lst;
va_start(lst, part1);
@ -76,6 +79,7 @@ const char * build_pathname(size_t nparts, const char *part1, ...) {
p = p / va_arg(lst, const char *);
}
build_pathname_buffer = p;
SEND_LOG("(BUILD_PATHNAME) %s", build_pathname_buffer.c_str());
return build_pathname_buffer.c_str();
}
@ -84,3 +88,10 @@ char create_directories(const char *path) {
std::error_code ec;
return std::filesystem::create_directories(p, ec)?1:0;
}
char change_current_directory(const char *path) {
std::error_code ec;
std::filesystem::current_path(std::filesystem::path(path), ec);
return ec == std::error_code{}?1:0;
}

117
platform/getopt.c Normal file
View file

@ -0,0 +1,117 @@
// Put this in a separate .h file (called "getopt.h").
// The prototype for the header file is:
/*
#ifndef GETOPT_H
#define GETOPT_H
int getopt(int nargc, char * const nargv[], const char *ostr) ;
#endif
*/
#include "getopt.h" // make sure you construct the header file as dictated above
/*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <string.h>
#include <stdio.h>
int opterr = 1, /* if error message should be printed */
optind = 1, /* index into parent argv vector */
optopt, /* character checked for validity */
optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
/*
* getopt --
* Parse argc/argv argument vector.
*/
int getopt(int nargc, char * const nargv[], const char *ostr)
{
static char *place = EMSG; /* option letter processing */
const char *oli; /* option letter list index */
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc || *(place = nargv[optind]) != '-') {
place = EMSG;
return (-1);
}
if (place[1] && *++place == '-') { /* found "--" */
++optind;
place = EMSG;
return (-1);
}
} /* option letter okay? */
if ((optopt = (int)*place++) == (int)':' ||
!(oli = strchr(ostr, optopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (optopt == (int)'-')
return (-1);
if (!*place)
++optind;
if (opterr && *ostr != ':')
(void)printf("illegal option -- %c\n", optopt);
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
optarg = NULL;
if (!*place)
++optind;
}
else { /* need an argument */
if (*place) /* no white space */
optarg = place;
else if (nargc <= ++optind) { /* no arg */
place = EMSG;
if (*ostr == ':')
return (BADARG);
if (opterr)
(void)printf("option requires an argument -- %c\n", optopt);
return (BADCH);
}
else /* white space */
optarg = nargv[optind];
place = EMSG;
++optind;
}
return (optopt); /* dump back option letter */
}

13
platform/getopt.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef GETOPT_H
#define GETOPT_H
extern int opterr; /* if error message should be printed */
extern int optind; /* index into parent argv vector */
extern int optopt; /* character checked for validity */
extern int optreset; /* reset getopt */
extern char *optarg; /* argument associated with option */
int getopt(int nargc, char * const nargv[], const char *ostr);
#endif

View file

@ -17,9 +17,10 @@ static std::string get_default_savegame_dir() {
const char *get_default_savegame_directory() {
const char *get_default_savegame_directory(void) {
static std::string dir = get_default_savegame_dir();
return dir.c_str();
}

View file

@ -41,6 +41,13 @@ const char * build_pathname(size_t nparts, const char *part1, ...);
* @retval 0 failure
*/
char create_directories(const char *path);
///change current directory
/**
* @param path path
* @retval 1 success
* @retval 0 failure
*/
char change_current_directory(const char *path);
@ -71,7 +78,7 @@ void sleep_ms(uint32_t);
}
#endif
//------------- BGRAPH DX wrapper -------------------
#include "sdl/BGraph2.h"
#define WM_RELOADMAP (WM_APP+215)

View file

@ -1,11 +1,15 @@
#ifdef __cplusplus__
#ifndef _SKELDAL_PLATFORM_SAVE_FOLDER
#define _SKELDAL_PLATFORM_SAVE_FOLDER
#ifdef __cplusplus
extern "C" {
#endif
#define SAVEGAME_FOLDERNAME "Skeldal";
const char *get_default_savegame_directory();
const char *get_default_savegame_directory(void);
#ifdef __cplusplus__
#ifdef __cplusplus
}
#endif
#endif

View file

@ -1,4 +1,5 @@
#include "BGraph2.h"
#include "../platform.h"
#include "sdl_context.h"
#include "global_context.h"
@ -9,34 +10,50 @@ static std::unique_ptr<uint16_t[]> buffer2nd;
static uint16_t *render_target;
static uint16_t screen_pitch = 640;
char DXInit64(char inwindow,int zoom,int monitor, int refresh) {
char game_display_init(const INI_CONFIG_SECTION *display_section, const char *title) {
SDLContext::DisplayMode mode;
if (inwindow) {
if (zoom) {
mode = SDLContext::double_window;
} else {
mode = SDLContext::native_window;
}
} else {
mode = SDLContext::fullscreen;
SDLContext::Config cfg = {};
const char *aspect_str;
aspect_str = ini_get_string(display_section, "aspect_ratio", "4:3");
if (sscanf(aspect_str, "%d:%d",&cfg.aspect_x, &cfg.aspect_y) != 2) {
cfg.aspect_x = cfg.aspect_y = 0;
}
cfg.fullscreen = ini_get_boolean(display_section, "fullscreen", 1) == 1;
const char *comp = ini_get_string(display_section, "composer", "auto");
if (stricmp(comp, "hardware") == 0 || stricmp(comp, "hw") == 0) {
cfg.composer = SDL_RENDERER_ACCELERATED;
} else if (stricmp(comp, "software") == 0 || stricmp(comp, "sw") == 0) {
cfg.composer = SDL_RENDERER_SOFTWARE;
} else {
cfg.composer = 0;
}
cfg.scale_quality = ini_get_string(display_section, "scale_quality", "auto");
cfg.window_height = ini_get_int(display_section, "window_height", 480);
cfg.window_width = ini_get_int(display_section, "window_width", 640);
cfg.crt_filter = ini_get_boolean(display_section, "crt_filter", 1) == 1;
screen_pitch = 640;
get_sdl_global_context().init_screen(mode, "Skeldal"); //todo allow change
get_sdl_global_context().init_video(cfg, title);
screen_buffer = std::make_unique<uint16_t[]>(screen_pitch*480);
buffer2nd = std::make_unique<uint16_t[]>(screen_pitch*480);
std::fill(screen_buffer.get(), screen_buffer.get()+screen_pitch*480,0);
render_target = screen_buffer.get();
return 1;
}
void DXCloseMode() {
get_sdl_global_context().close_screen();
void game_display_close(void) {
get_sdl_global_context().close_video();
}
uint16_t *GetScreenAdr() {
return render_target;
}
@ -84,11 +101,26 @@ void StripBlt(void *data, unsigned int startline, uint32_t width) {
}
void DXCopyRects64(unsigned short x,unsigned short y,unsigned short xs,unsigned short ys) {
static void DXCopyRects64(unsigned short x,unsigned short y,unsigned short xs,unsigned short ys) {
get_sdl_global_context().present_rect(screen_buffer.get(), screen_pitch, x,y,xs,ys);
}
void game_display_update_rect(unsigned short x,unsigned short y,unsigned short xs,unsigned short ys)
{
if (x>DxGetResX() || y>DxGetResY()) return;
if (xs==0) xs=DxGetResX();
if (ys==0) ys=DxGetResY();
if (x+xs>DxGetResX()) xs=DxGetResX()-x;
if (y+ys>DxGetResY()) ys=DxGetResY()-y;
DXCopyRects64(x,y,xs,ys);
}
void *DxPrepareWalk(int ypos) {
auto &sdl = get_sdl_global_context();
sdl.swap_render_buffers();

View file

@ -1,4 +1,5 @@
#include <stdint.h>
#include "../config.h"
#ifndef __BGRAPH_DX_WRAPPER_
#define __BGRAPH_DX_WRAPPER_
@ -17,16 +18,11 @@ void RedirectScreen(uint16_t *newaddr);
void RestoreScreen(void);
void RedirectScreenBufferSecond(void);
char game_display_init(const INI_CONFIG_SECTION *display_section, const char *title);
void game_display_close(void);
void game_display_update_rect(unsigned short x,unsigned short y,unsigned short xs,unsigned short ys);
//inicializuje a otevira rezim 640x480x16b v DX - otevre okno, pripravi vse pro beh hry
//Vraci 1 pri uspechu
char DXInit64(char inwindow,int zoom,int monitor, int refresh);
//uzavre rezim grafiky
void DXCloseMode(void);
//void DXCopyRects32(unsigned short x,unsigned short y,unsigned short xs,unsigned short ys);
void DXCopyRects64(unsigned short x,unsigned short y,unsigned short xs,unsigned short ys);
void *DxPrepareWalk(int ypos);
void DxZoomWalk(void *handle, int ypos, int *points,float phase, void *lodka);

View file

@ -60,11 +60,10 @@ void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** textur
int pitch;
SDL_LockTexture(*texture, nullptr, &pixels, &pitch);
bool wider_lines = height > 1024;
bool wider_lines = height > 1350;
// Vyplň texturu patternem (liché řádky tmavší)
Uint32* pixelArray = (Uint32*)pixels;
Uint32 darkPixel = wider_lines?0x808080FF:0xC0C0C0FF; // Černá s částečnou průhledností
Uint32 darkPixel = wider_lines?0x808080FF:0xA0A0A0FF;
Uint32 transparentPixel = wider_lines ?0xFFFFFFE0:0xFFFFFFC0;
int step = wider_lines ?3:2;
@ -81,7 +80,7 @@ void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** textur
void SDLContext::init_screen(DisplayMode mode, const char *title) {
void SDLContext::init_video(const Config &config, const char *title) {
char buff[256];
static Uint32 update_request_event = SDL_RegisterEvents(1);
_update_request_event = update_request_event;
@ -89,14 +88,14 @@ void SDLContext::init_screen(DisplayMode mode, const char *title) {
assert(!_render_thread.joinable());
int width = 640;
int height = 480;
if (mode == double_window) {
width*=2;
height*=2;
}
int width = config.window_width;
int height = config.window_height;
aspect_x = config.aspect_x;
aspect_y = config.aspect_y;
crt_filter_enabled = config.crt_filter;
_fullscreen_mode = mode == fullscreen;
_fullscreen_mode = config.fullscreen;
std::atomic<bool> done = false;
std::exception_ptr e;
@ -105,20 +104,31 @@ void SDLContext::init_screen(DisplayMode mode, const char *title) {
try {
SDL_Window *window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
width, height, SDL_WINDOW_RESIZABLE|(mode==fullscreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0));
width, height, SDL_WINDOW_RESIZABLE|(_fullscreen_mode?SDL_WINDOW_FULLSCREEN_DESKTOP:0));
if (!window) {
snprintf(buff, sizeof(buff), "SDL Error create window: %s\n", SDL_GetError());
throw std::runtime_error(buff);
}
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
_window.reset(window);
SDL_Renderer *renderer = SDL_CreateRenderer(_window.get(), -1, 0);
SDL_Renderer *renderer = SDL_CreateRenderer(_window.get(), -1, config.composer);
if (!renderer) {
snprintf(buff,sizeof(buff), "Chyba při vytváření rendereru: %s\n", SDL_GetError());
snprintf(buff,sizeof(buff), "Failed to create composer: %s\n", SDL_GetError());
throw std::runtime_error(buff);
}
SDL_RendererInfo rinfo;
SDL_GetRendererInfo(renderer, &rinfo);
if (stricmp(config.scale_quality, "auto") == 0) {
if (rinfo.flags & SDL_RENDERER_ACCELERATED) {
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
}
} else {
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, config.scale_quality);
}
_renderer.reset(renderer);
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, 640, 480);
if (!texture) {
@ -159,7 +169,7 @@ void SDLContext::init_screen(DisplayMode mode, const char *title) {
}
void SDLContext::close_screen() {
void SDLContext::close_video() {
_render_thread.request_stop();
_render_thread.join();
}
@ -358,7 +368,7 @@ void SDLContext::update_screen() {
SDL_SetTextureAlphaMod(_visible_texture, 255);
SDL_RenderCopy(_renderer.get(), _visible_texture, NULL, &winrc);
}
if (winrc.h > 900) {
if (winrc.h > 900 && crt_filter_enabled) {
if (!_crt_effect) {
SDL_Texture *txt;
generateCRTTexture(_renderer.get(), &txt, 128, std::min<int>(1440, winrc.h));
@ -431,21 +441,28 @@ SDL_Rect SDLContext::get_window_aspect_rect() const {
int ww;
int wh;
SDL_GetWindowSizeInPixels(_window.get(), &ww, &wh);
int apw = wh * 4 / 3;
int aph = ww * 3 / 4;
int fw;
int fh;
if (apw > ww) {
fw = ww;
fh = aph;
if (aspect_x && aspect_y) {
int apw = wh * aspect_x / aspect_y;
int aph = ww * aspect_y / aspect_x;
int fw;
int fh;
if (apw > ww) {
fw = ww;
fh = aph;
} else {
fw = apw;
fh = wh;
}
w.h = fh;
w.w = fw;
w.x = (ww - fw)/2;
w.y = (wh - fh)/2;
} else {
fw = apw;
fh = wh;
w.x = 0;
w.y = 0;
w.w = ww;
w.h = wh;
}
w.h = fh;
w.w = fw;
w.x = (ww - fw)/2;
w.y = (wh - fh)/2;
return w;
}

View file

@ -14,16 +14,21 @@ public:
SDLContext();
enum DisplayMode {
native_window,
double_window,
fullscreen
struct Config {
int window_width;
int window_height;
bool crt_filter;
int composer;
const char *scale_quality;
bool fullscreen;
int aspect_x;
int aspect_y;
};
void init_screen(DisplayMode mode, const char *title);
void init_video(const Config &config, const char *title);
void close_screen();
void close_video();
void present_rect(uint16_t *pixels, unsigned int pitch, unsigned int x, unsigned int y, unsigned int xs,unsigned ys);
@ -89,6 +94,9 @@ protected:
MS_EVENT ms_event;
mutable std::mutex _mx;
int aspect_x = 4;
int aspect_y = 3;
bool crt_filter_enabled = false;
std::unique_ptr<SDL_Window, SDL_Deleter> _window;
std::unique_ptr<SDL_Renderer, SDL_Deleter> _renderer;