This commit is contained in:
Your Name 2025-06-01 21:58:31 +03:00
parent 19f0b80394
commit b75523115a
25 changed files with 1672 additions and 14 deletions

67
src/input.c Normal file
View file

@ -0,0 +1,67 @@
#include "sdl.h"
#include "interface.h"
#include "input.h"
int Input(APP *app, char *run, FONTS *fonts, char flag) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_q) {
*run = 0;
return EXIT;
}
else if (event.key.keysym.sym == SDLK_k)
return SAVE;
else if (flag == IN_MENU) {
if (event.key.keysym.sym == SDLK_s)
return SETTINGS;
else if (event.key.keysym.sym == SDLK_c)
return CONTINUE;
else if (event.key.keysym.sym == SDLK_n)
return NEW_GAME;
}
else if (flag == IN_SETTINGS || flag == IN_GAME) {
if (event.key.keysym.sym == SDLK_DOWN)
return KEY_DOWN;
else if (event.key.keysym.sym == SDLK_UP)
return KEY_UP;
else if (event.key.keysym.sym == SDLK_RIGHT)
return KEY_RIGHT;
else if (event.key.keysym.sym == SDLK_LEFT)
return KEY_LEFT;
}
break;
case SDL_QUIT:
if (flag == IN_MENU)
*run = 0;
break;
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
SDL_GetWindowSize(app->window, &app->scr_width, &app->scr_height);
SDL_UpdateWindowSurface(app->window);
}
CloseFonts(fonts);
InitFonts(fonts, *app);
break;
default:
break;
}
}
return INPUT_NORMAL;
}

29
src/input.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef _INPUT_H
#define _INPUT_H
#include "sdl.h"
#include "interface.h"
/* Game modes */
enum {
IN_MENU,
IN_SETTINGS,
IN_GAME
};
/* Input events */
enum {
INPUT_NORMAL,
EXIT,
SETTINGS,
KEY_DOWN,
KEY_UP,
KEY_LEFT,
KEY_RIGHT,
SAVE,
CONTINUE,
NEW_GAME
};
int Input(APP *app, char *run, FONTS *fonts, char flag);
#endif

95
src/interface.c Normal file
View file

@ -0,0 +1,95 @@
#include <stdio.h>
#include "sdl.h"
#include "interface.h"
int TextArray(TEXT text[], size_t n, char flag) {
for (size_t i = 0; i < n; i++) {
if (flag == TEXT_ARRAY_FREE && text[i].texture)
SDL_DestroyTexture(text[i].texture);
else if (flag == TEXT_ARRAY_SCAN && text[i].init == 0) {
fputs("Found uninitialized font\n", stderr);
return 1;
}
}
return 0;
}
void PrintText(TTF_Font *font, APP *app, TEXT *dst, const char *text) {
SDL_Surface *temp = TTF_RenderUTF8_Solid(font, text, dst->color);
if (!temp) {
fprintf(stderr, "AddBanner Font: %s\n", TTF_GetError());
return;
}
dst->texture = SDL_CreateTextureFromSurface(app->render, temp);
if (!dst->texture) {
SDL_FreeSurface(temp);
fprintf(stderr, "AddBanner Font: %s\n", TTF_GetError());
return;
}
SDL_FreeSurface(temp);
/* Write text size in to a structure */
if (TTF_SizeText(font, text, &dst->w, &dst->h) < 0) {
SDL_DestroyTexture(dst->texture);
return;
}
dst->init = 1;
}
void AddBanner(APP *app, TEXT text, SDL_Rect rect) {
/* Box */
SDL_SetRenderDrawColor(app->render, 0, 0, 0, 0);
SDL_RenderDrawRect(app->render, &rect);
/* Text */
if (text.w < rect.w)
rect.w = text.w;
SDL_RenderCopy(app->render, text.texture, NULL, &rect);
}
void AddPseudoButton(APP *app, TEXT text, const int bttn_num) {
int rect_x = APPROX2ORIG(app->scr_width, -0.8);
SDL_Rect rect = {
.x = rect_x,
.y = APPROX2ORIG(app->scr_height, (0.75 - (0.2 * bttn_num))),
.w = app->scr_width - (rect_x * 2),
.h = 45
};
AddBanner(app, text, rect);
}
void CloseFonts(FONTS *fonts) {
if (fonts->font1)
TTF_CloseFont(fonts->font1);
if (fonts->font2)
TTF_CloseFont(fonts->font2);
memset(fonts, 0, sizeof(FONTS));
}
int InitFonts(FONTS *fonts, const APP app) {
memset(fonts, 0, sizeof(FONTS));
size_t font_size = app.scr_width * app.scr_height * 8 / 403200;
fonts->font1 = TTF_OpenFont(FONT1, font_size);
font_size = app.scr_width * app.scr_height * 15 / 403200;
fonts->font2 = TTF_OpenFont(FONT1, font_size);
if (fonts->font1 == NULL || fonts->font2 == NULL) {
fprintf(stderr, "Can not open font: %s\n", TTF_GetError());
CloseFonts(fonts);
return 1;
}
return 0;
}

36
src/interface.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef _INTERFACE_SDL
#define _INTERFACE_SDL
#include "sdl.h"
#define FONT1 "files/fonts/FiraCode-Regular.ttf"
#define FONT2 "files/fonts/FiraCode-Medium.ttf"
enum {
TEXT_ARRAY_FREE = 1,
TEXT_ARRAY_SCAN
};
typedef struct {
TTF_Font *font1;
TTF_Font *font2;
} FONTS;
typedef struct {
SDL_Texture *texture;
SDL_Color color;
int w;
int h;
/* Status of initialization */
int init;
} TEXT;
int TextArray(TEXT text[], size_t n, char flag);
void PrintText(TTF_Font *font, APP *app, TEXT *dst, const char *text);
void AddBanner(APP *app, TEXT text, SDL_Rect rect);
void AddPseudoButton(APP *app, TEXT text, const int bttn_num);
void CloseFonts(FONTS *fonts);
int InitFonts(FONTS *fonts, const APP app);
#endif

265
src/lpt_parser.c Normal file
View file

@ -0,0 +1,265 @@
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "interface.h"
#include "sdl.h"
#include "parser.h"
#include "lpt_parser.h"
#include "input.h"
enum {
PARSER_NORMAL,
BACKGROUND,
ADD_ENTITY,
ADD_ENTITY_ARG2,
ADD_MESSAGE,
ADD_MESSAGE_ARG2
};
struct ENTITY {
float x;
float scale;
SDL_Texture *texture;
struct ENTITY *next;
};
void free_chain(struct ENTITY **ent) {
while (*ent != NULL) {
struct ENTITY *i = (*ent)->next;
if ((*ent)->texture)
SDL_DestroyTexture((*ent)->texture);
free(*ent);
*ent = i;
}
}
int DisplayScene(SDL_Texture *background, TEXT author, TEXT message, APP *app, FONTS *fonts, CONFIG cfg, struct ENTITY *ent) {
char run = LPTP_NORMAL;
while (run == LPTP_NORMAL) {
int status = Input(app, &run, fonts, IN_GAME);
switch (status) {
case EXIT:
run = MENU;
/* fallthrough */
case SAVE:
WriteCfg(cfg);
break;
case KEY_RIGHT:
run = NEXT;
break;
default:
break;
}
/* Background */
SDL_SetRenderDrawColor(app->render, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_RenderClear(app->render);
SDL_RenderCopy(app->render, background, NULL, NULL);
/* Entities */
struct ENTITY *bckp = ent;
while (bckp != NULL) {
struct ENTITY *i = bckp->next;
int w = 0, h = 0;
SDL_QueryTexture(bckp->texture, NULL, NULL, &w, &h);
SDL_Rect rect = {
.x = APPROX2ORIG(app->scr_width, bckp->x),
.y = APPROX2ORIG(app->scr_height, 0),
.w = w * bckp->scale,
.h = h * bckp->scale
};
if (bckp->texture)
SDL_RenderCopy(app->render, bckp->texture, NULL, &rect);
bckp = i;
}
/* Dialog */
AddPseudoButton(app, message, 0);
AddPseudoButton(app, author, 1);
/* Render */
SDL_RenderPresent(app->render);
SDL_Delay(20);
}
return run;
}
int lpt_parser(APP *app, FONTS *fonts, CONFIG *cfg) {
char path[PATH_MAX + 1];
snprintf(path, sizeof(path), "files/scenes/%d.lpt", cfg->saved_scene);
FILE *fp = OpenFile(path, "r");
if (fp == NULL) {
fputs("Maybe it's error?? Or endgame\n", stderr);
return ENDGAME;
}
char ret = ERROR;
/* Parser */
char *raw_str = NULL;
ssize_t bytes = 0;
size_t n = 0;
size_t line = 0;
char flag = PARSER_NORMAL;
/* For scene */
SDL_Texture *background = NULL;
TEXT author, msg;
memset(&author, 0, sizeof(author));
msg = author;
struct ENTITY *entities = NULL;
struct ENTITY *ptr = NULL;
while ((bytes = getline(&raw_str, &n, fp)) > 0) {
line++;
raw_str[strcspn(raw_str, "\n")] = '\0';
char *str = raw_str;
while (isspace(*str))
str++;
if (*str == '#')
continue;
if (flag == BACKGROUND) {
if (background)
SDL_DestroyTexture(background);
background = LoadIMG_SDL(str, app);
if (background == NULL)
goto LPT_PARSER_CLOSE_UP;
}
else if (flag == ADD_MESSAGE) {
if (author.init)
SDL_DestroyTexture(author.texture);
memset(&author, 0, sizeof(author));
PrintText(fonts->font1, app, &author, str);
if (author.init == 0)
goto LPT_PARSER_CLOSE_UP;
}
else if (flag == ADD_MESSAGE_ARG2) {
if (msg.init)
SDL_DestroyTexture(msg.texture);
memset(&msg, 0, sizeof(msg));
PrintText(fonts->font1, app, &msg, str);
if (msg.init == 0)
goto LPT_PARSER_CLOSE_UP;
if (DisplayScene(background, author, msg, app, fonts, *cfg, entities) == MENU) {
ret = MENU;
break;
}
}
else if (flag == ADD_ENTITY) {
entities = malloc(sizeof(struct ENTITY));
if (entities == NULL) {
fprintf(stderr, "LPT_parser: failed to add entitiy: malloc: %s\n", strerror(errno));
goto LPT_PARSER_CLOSE_UP;
}
memset(entities, 0, sizeof(struct ENTITY));
entities->texture = LoadIMG_SDL(str, app);
if (entities->texture == NULL)
goto LPT_PARSER_CLOSE_UP;
}
else if (flag == ADD_ENTITY_ARG2) {
char *tok_rest = str;
char *x_pos = strtok_r(tok_rest, " ", &tok_rest);
char *scale = strtok_r(tok_rest, " ", &tok_rest);
if (scale == NULL || x_pos == NULL) {
fprintf(stderr, "SCENE %d:%ld: invalid syntax: %s\n", cfg->saved_scene, line, str);
goto LPT_PARSER_CLOSE_UP;
}
entities->x = atof(x_pos);
entities->scale = atof(scale);
entities->next = ptr;
ptr = entities;
}
/* Parsing file */
if (flag == PARSER_NORMAL && bytes > 1) {
if (!strcmp(str, "Background:"))
flag = BACKGROUND;
else if (!strcmp(str, "AddEntity:"))
flag = ADD_ENTITY;
else if (!strcmp(str, "AddMessage:"))
flag = ADD_MESSAGE;
else if (!strcmp(str, "ClearEntity")) {
free_chain(&entities);
ptr = NULL;
}
else {
fprintf(stderr, "SCENE %d:%ld : invalid syntax: %s\n", cfg->saved_scene, line, str);
goto LPT_PARSER_CLOSE_UP;
}
}
else {
if (flag == ADD_MESSAGE)
flag = ADD_MESSAGE_ARG2;
else if (flag == ADD_ENTITY)
flag = ADD_ENTITY_ARG2;
else
flag = PARSER_NORMAL;
}
}
if (ret != MENU)
ret = LPTP_NORMAL;
/* Close up */
LPT_PARSER_CLOSE_UP:
if (raw_str)
free(raw_str);
if (background)
SDL_DestroyTexture(background);
if (author.init)
SDL_DestroyTexture(author.texture);
if (msg.init)
SDL_DestroyTexture(msg.texture);
free_chain(&entities);
fclose(fp);
return ret;
}

17
src/lpt_parser.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef _LPT_PARSER_H
#define _LPT_PARSER_H
#include "sdl.h"
#include "interface.h"
#include "parser.h"
enum {
LPTP_NORMAL = 1,
ERROR,
ENDGAME,
MENU,
NEXT
};
int lpt_parser(APP *app, FONTS *fonts, CONFIG *cfg);
#endif

318
src/main.c Normal file
View file

@ -0,0 +1,318 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include "lpt_parser.h"
#include "parser.h"
#include "sdl.h"
#include "interface.h"
#include "input.h"
void AddOption(TTF_Font *font, APP *app, TEXT text, const int bttn_num, const int opt_value, const int flag) {
AddPseudoButton(app, text, bttn_num);
/* Create texture for value */
char str[16];
if (flag == 1)
snprintf(str, sizeof(str), "%s", (opt_value) ? "Y" : "N");
else if (flag == 2)
snprintf(str, sizeof(str), "%d%%", opt_value);
else
snprintf(str, sizeof(str), "%d", opt_value);
TEXT value;
memset(&value, 0, sizeof(value));
PrintText(font, app, &value, str);
if (value.init == 0)
return;
/* Print it */
int rect_x = APPROX2ORIG(app->scr_width, -0.8);
SDL_Rect rect = {
.x = app->scr_width - rect_x - value.w - 20,
.y = APPROX2ORIG(app->scr_height, (0.75 - (0.2 * bttn_num))),
.w = value.w + 20,
.h = 45
};
SDL_RenderCopy(app->render, value.texture, NULL, &rect);
SDL_DestroyTexture(value.texture);
}
int main_menu(APP *app, FONTS *fonts, CONFIG *cfg) {
char run = 1;
char mode = IN_MENU;
size_t index = 0;
int *value = NULL;
char changed = 0;
/* LPT LOGO */
SDL_Texture *logo = LoadIMG_SDL("files/images/lpt.png", app);
if (logo == NULL)
run = -1;
/* Setup menu text */
TEXT text_new_game, text_continue, text_settings, text_quit, text_save;
memset(&text_new_game, 0, sizeof(text_new_game));
text_continue = text_settings = text_quit = text_save = text_new_game;
PrintText(fonts->font1, app, &text_new_game, "[n] New Game");
PrintText(fonts->font1, app, &text_continue, "[c] Continue");
PrintText(fonts->font1, app, &text_settings, "[s] Settings");
PrintText(fonts->font1, app, &text_save, "[k] Save");
PrintText(fonts->font1, app, &text_quit, "[q] Quit");
TEXT menu_msgs[] = {text_quit, text_settings, text_new_game, text_save, text_continue};
size_t menu_size = sizeof(menu_msgs) / sizeof(TEXT);
if (TextArray(menu_msgs, menu_size, TEXT_ARRAY_SCAN))
run = -1;
/* Setup settings text */
TEXT settings, fullscreen, fullscreen_custom, vsync, sound_vol, music_vol, screen_h, screen_w, pointer, text_back;
memset(&settings, 0, sizeof(settings));
fullscreen = fullscreen_custom = vsync = sound_vol = music_vol = screen_h = screen_w = pointer = text_back = settings;
PrintText(fonts->font1, app, &fullscreen, "Fullscreen mode:");
PrintText(fonts->font1, app, &fullscreen_custom, "Custom resolution for fullscreen: Y");
PrintText(fonts->font1, app, &vsync, "VSync:");
PrintText(fonts->font1, app, &sound_vol, "Sound volume:");
PrintText(fonts->font1, app, &music_vol, "Music volume:");
PrintText(fonts->font1, app, &screen_h, "Screen height:");
PrintText(fonts->font1, app, &screen_w, "Screen width:");
PrintText(fonts->font1, app, &text_back, "[q] Back");
PrintText(fonts->font1, app, &settings, "[settings] Restart requied...");
PrintText(fonts->font2, app, &pointer, ">");
TEXT settings_msgs[] = {fullscreen, fullscreen_custom, vsync, sound_vol, music_vol, screen_h, screen_w, text_save, text_back, pointer, settings};
int *settings_opt[] = {&cfg->fullscreen, &cfg->fullscreen_custom, &cfg->vsync, &cfg->sound_volume, &cfg->music_volume, &cfg->screen_h, &cfg->screen_w};
size_t settings_size = sizeof(settings_msgs) / sizeof(TEXT);
if (TextArray(settings_msgs, settings_size, TEXT_ARRAY_SCAN))
run = -1;
/* Game loop */
while (run > 0) {
if (value == NULL)
value = settings_opt[1];
/* Input and navigation */
int status = Input(app, &run, fonts, mode);
switch (status) {
case SETTINGS:
mode = IN_SETTINGS;
break;
case KEY_DOWN:
if (index > 0) {
index--;
value = settings_opt[index];
}
break;
case KEY_UP:
if (index < settings_size - 5) {
index++;
value = settings_opt[index];
}
break;
case KEY_LEFT:
changed = 1;
if ((value == &cfg->screen_w && *value > 639) || (value == &cfg->screen_h && *value > 479))
(*value)--;
else if (*value > 0)
(*value)--;
break;
case KEY_RIGHT:
changed = 1;
if (value == &cfg->fullscreen || value == &cfg->fullscreen_custom || value == &cfg->vsync)
*value = 1;
else if ((value == &cfg->music_volume && *value < 100) || (value == &cfg->sound_volume && *value < 100))
(*value) += 2;
else if (value == &cfg->screen_w || value == &cfg->screen_h)
(*value) += 5;
break;
case SAVE:
changed = 0;
if (WriteCfg(*cfg))
run = -1;
break;
case NEW_GAME:
cfg->saved_scene = 0;
/* fallthrough */
case CONTINUE:
char game_run = 1;
while (game_run > 0) {
game_run = lpt_parser(app, fonts, cfg);
if (game_run == ERROR) {
run = -1;
break;
}
else if (game_run == MENU)
break;
else if (game_run == LPTP_NORMAL)
cfg->saved_scene++;
else if (game_run == ENDGAME) {
cfg->saved_scene = 0;
break;
}
}
break;
default:
break;
}
/* Background */
SDL_SetRenderDrawColor(app->render, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_RenderClear(app->render);
/* Create GUI */
if (mode == IN_MENU) {
/* Logo */
SDL_Rect rect = {
.x = APPROX2ORIG(app->scr_width, -0.5),
.y = APPROX2ORIG(app->scr_height, -0.5),
.w = 128 * 2,
.h = 64 * 2
};
SDL_RenderCopy(app->render, logo, NULL, &rect);
/* Menu */
size_t i;
for (i = 0; i < menu_size - 1; i++)
AddPseudoButton(app, menu_msgs[i], i);
if (cfg->saved_scene > 0)
AddPseudoButton(app, menu_msgs[i], i);
}
else if (mode == IN_SETTINGS) {
/* Menu */
AddOption(fonts->font1, app, fullscreen, 0, cfg->fullscreen, 1);
AddOption(fonts->font1, app, fullscreen_custom, 1, cfg->fullscreen_custom, 1);
AddOption(fonts->font1, app, vsync, 2, cfg->vsync, 1);
AddOption(fonts->font1, app, sound_vol, 3, cfg->sound_volume, 2);
AddOption(fonts->font1, app, music_vol, 4, cfg->music_volume, 2);
AddOption(fonts->font1, app, screen_h, 5, cfg->screen_h, 0);
AddOption(fonts->font1, app, screen_w, 6, cfg->screen_w, 0);
AddPseudoButton(app, text_back, 7);
if (changed)
AddPseudoButton(app, text_save, 8);
/* Pointer */
SDL_Rect rect = {
.x = 0,
.y = APPROX2ORIG(app->scr_height, (0.75 - (0.2 * index))),
.w = pointer.w,
.h = pointer.h
};
SDL_RenderCopy(app->render, pointer.texture, NULL, &rect);
/* Title */
rect.y = 0;
rect.w = settings.w;
rect.h = settings.h;
SDL_RenderCopy(app->render, settings.texture, NULL, &rect);
}
if (run == 0 && mode == IN_SETTINGS) {
mode = IN_MENU;
run = 1;
}
/* Render */
SDL_RenderPresent(app->render);
SDL_Delay(20);
}
/* Free structures */
if (logo)
SDL_DestroyTexture(logo);
TextArray(menu_msgs, menu_size, TEXT_ARRAY_FREE);
TextArray(settings_msgs, menu_size, TEXT_ARRAY_FREE);
return (run == -1) ? 1 : 0;
}
int main(void) {
/* Config */
CONFIG cfg = {
.fullscreen = 1,
.fullscreen_custom = 0,
.screen_w = 480,
.screen_h = 640,
.sound_volume = 100,
.music_volume = 20,
.vsync = 0,
};
if (access(CONFIG_FILE, R_OK) < 0) {
fprintf(stderr, "Failed to open %s: %s. Creating...\n", CONFIG_FILE, strerror(errno));
WriteCfg(cfg);
}
else
ReadCfg(&cfg);
/* Init SDL */
APP app;
if (Init_SDL(cfg, &app))
return 1;
int ret = 0;
FONTS fonts;
if (InitFonts(&fonts, app))
ret = 1;
/* Game loop*/
if (!ret)
ret = main_menu(&app, &fonts, &cfg);
/* Clean up */
CloseFonts(&fonts);
CleanUp_SDL(&app);
if (ret)
fputs("Game returned 1: interal error\n", stderr);
fputs("Thanks for playing\n", stderr);
return ret;
}

86
src/parser.c Normal file
View file

@ -0,0 +1,86 @@
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "parser.h"
#define YesOrNo() ((strcmp(val, "yes") > 0) ? 1 : 0)
FILE *OpenFile(const char *file, const char *mode) {
FILE *fp = fopen(file, mode);
if (fp == NULL)
fprintf(stderr, "Can not open %s with %s mode\n", file, mode);
return fp;
}
int ReadCfg(CONFIG *cfg) {
memset(cfg, 0, sizeof(CONFIG));
FILE *fp = OpenFile(CONFIG_FILE, "r");
if (fp == NULL)
return 1;
char *str = NULL;
ssize_t bytes = 0;
size_t n = 0;
while ((bytes = getline(&str, &n, fp)) > 0) {
char *tok_rest = str;
char *var = strtok_r(tok_rest, " ", &tok_rest);
char *val = strtok_r(tok_rest, " ", &tok_rest);
if (var == NULL || val == NULL)
continue;
if (var[0] == '#')
continue;
else if (!strcmp(var, "fullscreen"))
cfg->fullscreen = YesOrNo();
else if (!strcmp(var, "fullscreen_custom"))
cfg->fullscreen_custom = YesOrNo();
else if (!strcmp(var, "vsync"))
cfg->vsync = YesOrNo();
else if (!strcmp(var, "sound_volume"))
cfg->sound_volume = atoi(val);
else if (!strcmp(var, "music_volume"))
cfg->music_volume = atoi(val);
else if (!strcmp(var, "screen_h"))
cfg->screen_h = atoi(val);
else if (!strcmp(var, "screen_w"))
cfg->screen_w = atoi(val);
else if (!strcmp(var, "saved_scene"))
cfg->saved_scene = atoi(val);
}
if (str)
free(str);
fclose(fp);
return 0;
}
int WriteCfg(const CONFIG cfg) {
FILE *fp = OpenFile(CONFIG_FILE, "w");
if (fp == NULL)
return 1;
fprintf(fp, "fullscreen %s\n", (cfg.fullscreen) ? "yes" : "no");
fprintf(fp, "fullscreen_custom %s\n", (cfg.fullscreen_custom) ? "yes" : "no");
fprintf(fp, "vsync %s\n", (cfg.vsync) ? "yes" : "no");
fprintf(fp, "sound_volume %d\n", cfg.sound_volume);
fprintf(fp, "music_volume %d\n", cfg.music_volume);
fprintf(fp, "screen_h %d\n", cfg.screen_h);
fprintf(fp, "screen_w %d\n", cfg.screen_w);
fprintf(fp, "saved_scene %d\n", cfg.saved_scene);
fclose(fp);
return 0;
}

25
src/parser.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef _PARSER_H
#define _PARSER_H
#include <stdio.h>
#define CONFIG_FILE "settings"
typedef struct {
int fullscreen;
int fullscreen_custom;
int vsync;
int screen_w;
int screen_h;
int sound_volume;
int music_volume;
int saved_scene;
} CONFIG;
FILE *OpenFile(const char *file, const char *mode);
int ReadCfg(CONFIG *cfg);
int WriteCfg(const CONFIG cfg);
#endif

95
src/sdl.c Normal file
View file

@ -0,0 +1,95 @@
#include <stdio.h>
#include <stdlib.h>
#include "sdl.h"
#include "parser.h"
void CleanUp_SDL(APP *app) {
if (app->window)
SDL_DestroyWindow(app->window);
if (app->render)
SDL_DestroyRenderer(app->render);
SDL_Quit();
IMG_Quit();
TTF_Quit();
}
int Init_SDL(const CONFIG cfg, APP *app) {
memset(app, 0, sizeof(APP));
int status = 0;
if (SDL_Init(SDL_INIT_VIDEO) < 0)
goto Init_SDL_CLOSE;
/* Get display mode if fullscreen enabled in config */
app->scr_width = cfg.screen_w;
app->scr_height = cfg.screen_h;
int fullscreen = 0;
if (cfg.fullscreen) {
if (!cfg.fullscreen_custom) {
SDL_DisplayMode dm;
if (SDL_GetDesktopDisplayMode(0, &dm) < 0)
goto Init_SDL_CLOSE;
app->scr_width = dm.w;
app->scr_height = dm.h;
}
fullscreen = SDL_WINDOW_FULLSCREEN;
}
/* Creating window */
app->window = SDL_CreateWindow("LPT", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, app->scr_width, app->scr_height, SDL_WINDOW_RESIZABLE | fullscreen);
if (!app->window)
goto Init_SDL_CLOSE;
SDL_SetWindowMinimumSize(app->window, 640, 480);
/* Renderer */
app->render = SDL_CreateRenderer(app->window, -1, SDL_RENDERER_ACCELERATED | (cfg.vsync) ? SDL_RENDERER_PRESENTVSYNC : 0);
if (!app->render)
goto Init_SDL_CLOSE;
/* Image subsystem */
status = 1;
int flags = IMG_INIT_PNG;
if (!(IMG_Init(flags) & flags))
goto Init_SDL_CLOSE;
/* Font subsystem */
status = 2;
if (TTF_Init() < 0)
goto Init_SDL_CLOSE;
return 0;
Init_SDL_CLOSE:
if (status == 1)
fprintf(stderr, "Could not initialize SDL_IMG: %s\n", IMG_GetError());
if (status == 2)
fprintf(stderr, "Could not initialize SDL_TTF: %s\n", TTF_GetError());
else
fprintf(stderr, "Could not initialize SDL: %s\n", SDL_GetError());
CleanUp_SDL(app);
return 1;
}
SDL_Texture *LoadIMG_SDL(const char *path, APP *app) {
if (path == NULL) {
fputs("LoadIMG: path is NULL. Maybe incorrect scene syntax\n", stderr);
return NULL;
}
SDL_Texture *text = IMG_LoadTexture(app->render, path);
if (text == NULL) {
fprintf(stderr, "LoadIMG: %s\n", SDL_GetError());
return NULL;
}
return text;
}

24
src/sdl.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef _LPT_SDL
#define _LPT_SDL
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_keycode.h>
#include "parser.h"
#define APPROX2ORIG(res, appr) ((res * appr + res) / 2)
#define ORIG2APPROX(res, pix) (pix / res * 2 - 1)
typedef struct {
SDL_Window *window;
SDL_Renderer *render;
int scr_height;
int scr_width;
} APP;
void CleanUp_SDL(APP *app);
int Init_SDL(const CONFIG cfg, APP *app);
SDL_Texture *LoadIMG_SDL(const char *path, APP *app);
#endif