1 /** 2 * Copyright © Novelate 2020 3 * License: MIT (https://github.com/Novelate/NovelateEngine/blob/master/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 * Website: https://novelate.com/ 6 * ------ 7 * Novelate is a free and open-source visual novel engine and framework written in the D programming language. 8 * It can be used freely for both personal and commercial projects. 9 * ------ 10 * Module Description: 11 * This module handles parsing of the visual novel scripting files. 12 */ 13 module novelate.parser; 14 15 import novelate.config; 16 import novelate.media; 17 import novelate.character; 18 import novelate.music; 19 import novelate.scene; 20 import novelate.files; 21 22 /** 23 * Parses a Novelate file. 24 * Params: 25 * file = The file to parse. 26 */ 27 void parseFile(string file) 28 { 29 import std.path : absolutePath, dirName; 30 31 parse(cast(string)readFileText(file)); 32 } 33 34 private 35 { 36 /// The parsing types. 37 enum ParsingType 38 { 39 none, 40 config, 41 media, 42 character, 43 music, 44 scene 45 } 46 } 47 48 /** 49 * Parses content using the Novelate file format. 50 * Params: 51 * content = The content to parse. 52 */ 53 void parse(string content) 54 { 55 import std.stdio : writeln, writefln; 56 57 import std.array : array, replace, split; 58 import std.string : strip, stripLeft, stripRight, format; 59 import std.algorithm : map, filter, startsWith, endsWith, countUntil; 60 61 auto lines = content.replace("\r", "").split("\n").map!(l => l.strip).filter!(l => l.length).array; 62 63 ParsingType parsingType; 64 string currentName; 65 NovelateScene currentScene; 66 67 foreach (l; lines) 68 { 69 auto line = l.split("//")[0].stripRight; 70 71 if (line.startsWith("#")) 72 { 73 auto fileName = line[1 .. $]; 74 75 parse(readFileText(fileName ~ ".txt")); 76 } 77 78 if (line.startsWith("<") && line.endsWith(">")) 79 { 80 parsingType = ParsingType.none; 81 currentScene = null; 82 83 auto entryData = line[1 .. $-1]; 84 85 if (entryData == "__Config__") 86 { 87 parsingType = ParsingType.config; 88 } 89 else 90 { 91 auto entries = entryData.split(":"); 92 auto name = entries[0]; 93 auto type = entries[1]; 94 95 currentName = name; 96 97 switch (type) 98 { 99 case "Media": parsingType = ParsingType.media; break; 100 case "Character": 101 { 102 parsingType = ParsingType.character; 103 createCharacterBase(name); 104 break; 105 } 106 case "Music": parsingType = ParsingType.music; break; 107 case "Scene": 108 { 109 parsingType = ParsingType.scene; 110 currentScene = createSceneBase(name); 111 break; 112 } 113 default: parsingType = ParsingType.none; 114 } 115 } 116 } 117 else 118 { 119 switch (parsingType) 120 { 121 case ParsingType.config: 122 { 123 auto valueIndex = line.countUntil("="); 124 125 if (valueIndex < 1) 126 { 127 break; 128 } 129 130 auto name = line[0 .. valueIndex]; 131 auto value = line[valueIndex + 1 .. $]; 132 133 config.setConfig(name, value); 134 break; 135 } 136 137 case ParsingType.media: 138 { 139 addMediaFile(currentName, line); 140 break; 141 } 142 143 case ParsingType.character: 144 { 145 auto valueIndex = line.countUntil("="); 146 147 if (valueIndex < 1) 148 { 149 break; 150 } 151 152 auto name = line[0 .. valueIndex]; 153 auto value = line[valueIndex + 1 .. $]; 154 155 updateCharacter(currentName, name, value); 156 break; 157 } 158 159 case ParsingType.music: 160 { 161 addMusicFile(currentName, line); 162 break; 163 } 164 165 case ParsingType.scene: 166 { 167 if (currentScene) 168 { 169 currentScene.updateScene(line); 170 } 171 break; 172 } 173 174 default: break; 175 } 176 } 177 } 178 }