2
0

5 Commits 26959e7cfa ... 09bcf38e24

Autor SHA1 Nachricht Datum
  Evgeniy Parfenyuk 09bcf38e24 Date update vor 1 Monat
  Evgeniy Parfenyuk 6cb4aa57fb Add gitignore vor 1 Monat
  Evgeniy Parfenyuk 6f769ad8fd Remade main() to show file lines on screen vor 1 Monat
  Evgeniy Parfenyuk 767797c704 Fix parser mess, add different macro for engine/parser vor 2 Monaten
  Evgeniy Parfenyuk af8ddd19e2 Add size vars in the textbox vor 2 Monaten

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+vinora
+.obj
+*.a

+ 3 - 1
Makefile

@@ -1,7 +1,7 @@
 # Compiler and flags
 CC = gcc
 CFLAGS = -Wall -Wextra -Wpedantic -Werror -std=c99 \
-	     -O2 -Iexternal/raylib/src
+	     -O2 -Iexternal/raylib/src -MMD -MP
 LDFLAGS = -Lexternal/raylib/ -lraylib -lm -lpthread
 
 # Directories
@@ -75,3 +75,5 @@ clean:
 
 # Phony targets
 .PHONY: all clean run raylib
+
+-include $(OBJECTS:.o=.d)

+ 93 - 0
assets/balsamiq_sans/LICENSE.txt

@@ -0,0 +1,93 @@
+Copyright 2011 The Balsamiq Sans Project Authors (https://github.com/balsamiq/balsamiqsans)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.

BIN
assets/balsamiq_sans/bold.ttf


BIN
assets/balsamiq_sans/bold_italic.ttf


BIN
assets/balsamiq_sans/italic.ttf


BIN
assets/balsamiq_sans/regular.ttf


+ 0 - 0
src/test.vnrs → assets/test.vnrs


+ 61 - 27
src/main.c

@@ -1,7 +1,7 @@
 /*
 Vinora Engine -- simple and portable visual novels engine.
 
-Copyright (c) 2025 Evgeniy "Parthen" Parfenyuk <parthen [at] riseup.net>
+Copyright (c) 2025-2026 Evgeniy "Parthen" Parfenyuk <parthen [at] riseup.net>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -19,48 +19,61 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include <raylib.h>
 #include <string.h>
+#include <stdio.h>
 
-typedef struct {
-    Texture2D img;
-    Vector2 pos; // In percents
-} Layer;
+#include "vnrs_parser.h"
+#include "text_utils.h"
 
-typedef struct {
-    Layer layers[10];
-    int layers_count;
-
-    char *name;
-    Color name_color;
-    char *text;
-    Color text_color;
-} GameState;
+#define MAIN_ENGINE 1
+#define MAIN_PARSER 2
+#define MAIN MAIN_ENGINE
 
+void get_cur_state(GameState *cur_state)
+{
+    cur_state->name = "Sumi";
+    cur_state->name_color = RAYWHITE;
+    cur_state->name_size = 40;
+
+    cur_state->text = strdup("Hello, World!");
+    cur_state->text_color = RAYWHITE;
+    cur_state->text_size = 30;
+
+    /*
+    cur_state->layers[0].img = LoadTexture("assets/spiral_atlas/bedroom.jpg");
+    cur_state->layers[0].pos = (Vector2){0.5, 0.5};
+    cur_state->layers[1].img = LoadTexture("assets/sumi/smile.png");
+    cur_state->layers[1].pos = (Vector2){0.5, 0.5}; */
+    cur_state->layers_count = 0;
+}
 
 int main(void)
 {
+#if MAIN == MAIN_ENGINE
     const int screenWidth = 1920;
     const int screenHeight = 1080;
 
     SetConfigFlags(FLAG_FULLSCREEN_MODE);
+    SetTraceLogLevel(LOG_ERROR);
     InitWindow(screenWidth, screenHeight,
                "Vinora Engine");
 
 
     GameState cur_state = {0};
-
-    cur_state.name = "Sumi";
-    cur_state.name_color = RAYWHITE;
-
-    cur_state.layers[0].img = LoadTexture("assets/spiral_atlas/bedroom.jpg");
-    cur_state.layers[0].pos = (Vector2){0.5, 0.5};
-    cur_state.layers[1].img = LoadTexture("assets/sumi/smile.png");
-    cur_state.layers[1].pos = (Vector2){0.5, 0.5};
-    cur_state.layers_count += 2;
-
+    get_cur_state(&cur_state);
+    FILE *f;
+    f = fopen("assets/test.vnrs", "r");
+    if (!f) {
+        perror("assets/test.vnrs");
+        return 2;
+    }
+    
     while (!WindowShouldClose())
     {
+        if (IsKeyPressed(KEY_SPACE))
+            get_next_state(f, &cur_state);
+
         BeginDrawing();
-            ClearBackground(RAYWHITE);
+            ClearBackground(BLACK);
 
             for (int i = 0; i < cur_state.layers_count; i++) {
                 DrawTexture(cur_state.layers[i].img,
@@ -70,9 +83,17 @@ int main(void)
                                     cur_state.layers[i].img.height / 2,
                             RAYWHITE);
             }
+            DrawRectangle(screenWidth/10, screenHeight/5 * 4 - 10,
+                          screenWidth * 4 / 5, screenHeight / 5,
+                          (Color){0, 0, 0, 150});
             DrawText(cur_state.name, screenWidth/2  -
-            (strlen(cur_state.name)*40/2)/2, screenHeight/5*4 - 40, 40,
-            cur_state.name_color);
+                (strlen(cur_state.name)*cur_state.name_size/2)/2,
+                screenHeight/5*4,
+                cur_state.name_size, cur_state.name_color);
+            DrawText(cur_state.text, screenWidth/2  -
+                (strlen(cur_state.text)*cur_state.text_size/2)/2,
+                screenHeight/5*4.5,
+                cur_state.text_size, cur_state.text_color);
             DrawFPS(5, 5);
 
         EndDrawing();
@@ -80,5 +101,18 @@ int main(void)
 
     CloseWindow();
 
+#elif MAIN == MAIN_PARSER
+    FILE *f;
+    printf("Parser...\n");
+    f = fopen("assets/test.vnrs", "r");
+    if (!f) {
+        perror("assets/test.vnrs");
+        return 2; 
+    }
+
+    parse_vnrs(f, stdout);
+
+    fclose(f);
+#endif
     return 0;
 }

+ 152 - 0
src/raytext.h

@@ -0,0 +1,152 @@
+/*
+raytext.h -- advanced text functions for raylib.
+
+Copyright (c) 2025-2026 Evgeniy "Parthen" Parfenyuk <parthen [at] riseup.net>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef RAYTEXT_H
+#define RAYTEXT_H
+
+// From raylib examples, TODO: re-write 
+static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint)
+{
+    int length = TextLength(text);  // Total length in bytes of the text, scanned by codepoints in loop
+
+    float textOffsetY = 0;          // Offset between lines (on line break '\n')
+    float textOffsetX = 0.0f;       // Offset X to next character to draw
+
+    float scaleFactor = fontSize/(float)font.baseSize;     // Character rectangle scaling factor
+
+    // Word/character wrapping mechanism variables
+    enum { MEASURE_STATE = 0, DRAW_STATE = 1 };
+    int state = wordWrap? MEASURE_STATE : DRAW_STATE;
+
+    int startLine = -1;         // Index where to begin drawing (where a line begins)
+    int endLine = -1;           // Index where to stop drawing (where a line ends)
+    int lastk = -1;             // Holds last value of the character position
+
+    for (int i = 0, k = 0; i < length; i++, k++)
+    {
+        // Get next codepoint from byte string and glyph index in font
+        int codepointByteCount = 0;
+        int codepoint = GetCodepoint(&text[i], &codepointByteCount);
+        int index = GetGlyphIndex(font, codepoint);
+
+        // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
+        // but we need to draw all of the bad bytes using the '?' symbol moving one byte
+        if (codepoint == 0x3f) codepointByteCount = 1;
+        i += (codepointByteCount - 1);
+
+        float glyphWidth = 0;
+        if (codepoint != '\n')
+        {
+            glyphWidth = (font.glyphs[index].advanceX == 0) ? font.recs[index].width*scaleFactor : font.glyphs[index].advanceX*scaleFactor;
+
+            if (i + 1 < length) glyphWidth = glyphWidth + spacing;
+        }
+
+        // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container
+        // We store this info in startLine and endLine, then we change states, draw the text between those two variables
+        // and change states again and again recursively until the end of the text (or until we get outside of the container)
+        // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately
+        // and begin drawing on the next line before we can get outside the container
+        if (state == MEASURE_STATE)
+        {
+            // TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
+            // Ref: http://jkorpela.fi/chars/spaces.html
+            if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) endLine = i;
+
+            if ((textOffsetX + glyphWidth) > rec.width)
+            {
+                endLine = (endLine < 1)? i : endLine;
+                if (i == endLine) endLine -= codepointByteCount;
+                if ((startLine + codepointByteCount) == endLine) endLine = (i - codepointByteCount);
+
+                state = !state;
+            }
+            else if ((i + 1) == length)
+            {
+                endLine = i;
+                state = !state;
+            }
+            else if (codepoint == '\n') state = !state;
+
+            if (state == DRAW_STATE)
+            {
+                textOffsetX = 0;
+                i = startLine;
+                glyphWidth = 0;
+
+                // Save character position when we switch states
+                int tmp = lastk;
+                lastk = k - 1;
+                k = tmp;
+            }
+        }
+        else
+        {
+            if (codepoint == '\n')
+            {
+                if (!wordWrap)
+                {
+                    textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
+                    textOffsetX = 0;
+                }
+            }
+            else
+            {
+                if (!wordWrap && ((textOffsetX + glyphWidth) > rec.width))
+                {
+                    textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
+                    textOffsetX = 0;
+                }
+
+                // When text overflows rectangle height limit, just stop drawing
+                if ((textOffsetY + font.baseSize*scaleFactor) > rec.height) break;
+
+                // Draw selection background
+                bool isGlyphSelected = false;
+                if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
+                {
+                    DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, glyphWidth, (float)font.baseSize*scaleFactor }, selectBackTint);
+                    isGlyphSelected = true;
+                }
+
+                // Draw current character glyph
+                if ((codepoint != ' ') && (codepoint != '\t'))
+                {
+                    DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint);
+                }
+            }
+
+            if (wordWrap && (i == endLine))
+            {
+                textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
+                textOffsetX = 0;
+                startLine = endLine;
+                endLine = -1;
+                glyphWidth = 0;
+                selectStart += lastk - k;
+                k = lastk;
+
+                state = !state;
+            }
+        }
+
+        if ((textOffsetX != 0) || (codepoint != ' ')) textOffsetX += glyphWidth;  // avoid leading spaces
+    }
+}
+#endif

+ 23 - 1
src/text_utils.h

@@ -1,7 +1,7 @@
 /*
 text_utils.h -- useful functions for working with text.
 
-Copyright (c) 2025 Evgeniy "Parthen" Parfenyuk <parthen [at] riseup.net>
+Copyright (c) 2025-2026 Evgeniy "Parthen" Parfenyuk <parthen [at] riseup.net>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 #define TEXT_UTILS_H
 
 #include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
 
 inline bool is_whitespace(int ch)
 {
@@ -29,5 +31,25 @@ inline bool is_whitespace(int ch)
             ch == '\r' || ch == '\v' || ch == '\f');
 }
 
+inline bool is_emptyline(char *line)
+{
+    while (*line)
+    {
+        if (!is_whitespace((unsigned char)*line))
+            return 0;
+        line++;
+    }
+    return 1;
+}
+
+
+inline char *strdup(const char *s)
+{
+    size_t len = strlen(s) + 1;
+    char *p = malloc(len);
+    if (p != NULL)
+        memcpy(p, s, len);
+    return p;
+}
 
 #endif

+ 23 - 24
src/vnrs_parser.c

@@ -1,7 +1,7 @@
 /*
 vnrs_parser.c -- parse Vinora Screenplay files.
 
-Copyright (c) 2025 Evgeniy "Parthen" Parfenyuk <parthen [at] riseup.net>
+Copyright (c) 2025-2026 Evgeniy "Parthen" Parfenyuk <parthen [at] riseup.net>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include "chunks.h"
 #include "text_utils.h"
-
+#include "vnrs_parser.h"
 
 
 static void normalise(char *s)
@@ -52,13 +52,22 @@ static void normalise(char *s)
 static char *get_line(FILE *input)
 {
     static char buffer[MAX_LINE_LENGTH];
-
     if (!input) return NULL;
 
-    if (fgets(buffer, MAX_LINE_LENGTH, input) == NULL)
-        return NULL;
-    normalise(buffer); 
-    return buffer;
+    while (fgets(buffer, sizeof buffer, input)) {
+        normalise(buffer);
+        if (is_emptyline(buffer))
+            continue;
+        size_t len = strlen(buffer);
+        char *result = malloc(len + 1);
+        if (!result)
+            return NULL;
+
+        memcpy(result, buffer, len + 1);
+        return result;
+    }
+
+    return NULL;
 }
 
 
@@ -78,24 +87,14 @@ void parse_vnrs(FILE *input, FILE *output)
     }
 }
 
-/*
-int main(int argc, char *argv[])
+void get_next_state(FILE *input, GameState *state)
 {
-    if (argc < 2) {
-        fprintf(stderr, "Usage: ./parser <filename>.vnrs\n");
-        return 1;
-    }
-
-    FILE *f;
-    f = fopen(argv[1], "r");
-    if (!f) {
-        perror(argv[1]);
-        return 2; 
-    }
+    if (!input) return;
 
-    parse_vnrs(f, stdout);
+    char *s = get_line(input);
+    if (!s) return;
 
-    fclose(f);
-    return 0;
+    free(state->text);
+    state->text = s;
 }
-*/
+

+ 27 - 0
src/vnrs_parser.h

@@ -0,0 +1,27 @@
+#ifndef VNRS_PARSER_H
+#define VNRS_PARSER_H
+
+#include <raylib.h>
+
+typedef struct {
+    Texture2D img;
+    Vector2 pos; // In percents
+} Layer;
+
+typedef struct {
+    Layer layers[10];
+    int layers_count;
+
+    char *name;
+    Color name_color;
+    int name_size;
+
+    char *text;
+    Color text_color;
+    int text_size;
+} GameState;
+
+void parse_vnrs(FILE *input, FILE *output);
+void get_next_state(FILE *input, GameState *state);
+
+#endif // VNRS_PARSER_H