1 /* 2 * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license. 3 * 4 * Pixel Perfect Engine, config module 5 */ 6 7 module PixelPerfectEngine.system.config; 8 9 //import std.xml; 10 import std.file; 11 import std.stdio; 12 import std..string; 13 import std.conv; 14 //import std.csv; 15 16 import PixelPerfectEngine.system.inputHandler; 17 import PixelPerfectEngine.system.exc; 18 import PixelPerfectEngine.system.etc; 19 import PixelPerfectEngine.system.dictionary; 20 import PixelPerfectEngine.graphics.outputScreen; 21 22 import bindbc.sdl; 23 24 import sdlang; 25 26 public class ConfigurationProfile{ 27 public static const ushort[string] keymodifierStrings; 28 public static const string[ushort] joymodifierStrings; 29 public static const string[Devicetype] devicetypeStrings; 30 private static Dictionary keyNameDict, joyButtonNameDict, joyAxisNameDict; 31 public int sfxVol, musicVol; 32 public int threads; 33 public string screenMode, resolution, scalingQuality, driver; 34 //public string[string] videoSettings; 35 public KeyBinding[] keyBindingList; 36 public InputDeviceData[] inputDevices; 37 private string path; 38 public AuxillaryElements[] auxillaryParameters; 39 private static string vaultPath; 40 private SDL_DisplayMode[] videoModes; 41 //public AuxillaryElements auxillaryElements[]; 42 public string appName; 43 public string appVers; 44 /// Initializes a basic configuration profile. If [vaultPath] doesn't have any configfiles, restores it from defaults. 45 public this(){ 46 path = vaultPath ~ "config.sdl"; 47 if(exists(path)){ 48 restore(); 49 }else{ 50 std.file.copy("../system/defaultConfig.sdl",path); 51 restore(); 52 } 53 } 54 55 static this(){ 56 keymodifierStrings = 57 ["NONE" : 0x0000, "LSHIFT": 0x0001, "RSHIFT": 0x0002, "LCTRL": 0x0040, "RCTRL": 0x0080, "CTRL": 0x0020, 58 "LALT": 0x0100, "RALT": 0x0200 , "LGUI": 0x0400, "RGUI": 0x0800, "NUM": 0x1000, "CAPS": 0x2000, "MODE": 0x4000, 59 "RESERVED": 0x8000, "ANY": 0xFFFF]; 60 joymodifierStrings = [0x0000: "buttons",0x0004: "dpad",0x0008: "axis"]; 61 devicetypeStrings = [Devicetype.JOYSTICK: "joystick", Devicetype.KEYBOARD: "keyboard", Devicetype.MOUSE: "mouse", 62 Devicetype.TOUCHSCREEN: "touchscreen" ]; 63 keyNameDict = new Dictionary("../system/keycodeNamings.sdl"); 64 joyButtonNameDict = new Dictionary("../system/joyButtonNamings.sdl"); 65 joyAxisNameDict = new Dictionary("../system/joyAxisNamings.sdl"); 66 } 67 ///Restores configuration profile 68 public void restore(){ 69 70 //string s = cast(string)std.file.read(configFile); 71 72 Tag root; 73 74 try{ 75 root = parseFile(path); 76 foreach(Tag t0; root.tags){ 77 if(t0.name == "audio"){ //get values for the audio subsystem 78 sfxVol = t0.getTagValue!int("soundVol", 100); 79 musicVol = t0.getTagValue!int("musicVol", 100); 80 }else if(t0.name == "video"){ //get values for the video subsystem 81 foreach(Tag t1; t0.tags){ 82 switch(t1.name){ 83 case "driver": driver = t1.getValue!string("software"); break; 84 case "scaling": scalingQuality = t1.getValue!string("nearest"); break; 85 case "screenMode": driver = t1.getValue!string("windowed"); break; 86 case "resolution": driver = t1.getValue!string("0"); break; 87 case "threads": threads = t1.getValue!int(-1); break; 88 default: break; 89 } 90 } 91 }else if(t0.name == "input"){ 92 foreach(Tag t1; t0.tags){ 93 switch(t1.name){ 94 case "device": 95 InputDeviceData device; 96 string name = t1.getValue!string(""); 97 int devicenumber = t1.getAttribute!int("devNum"); 98 switch(t1.expectAttribute!string("type")){ 99 case "keyboard": 100 device = InputDeviceData(devicenumber, Devicetype.KEYBOARD, name); 101 foreach(Tag t2; t1.tags){ 102 if(t2.name is null){ 103 uint scanCode = t2.getAttribute!int("keyCode", keyNameDict.decode(t2.getAttribute!string("keyName"))); 104 keyBindingList ~= KeyBinding(stringToKeymod(t2.getAttribute!string("keyMod","NONE;")), scanCode, devicenumber, t2.expectValue!string(), Devicetype.KEYBOARD, stringToKeymod(t2.getAttribute!string("keyMod","ALL;"))); 105 } 106 } 107 break; 108 case "joystick": 109 device = InputDeviceData(devicenumber, Devicetype.JOYSTICK, name); 110 foreach(Tag t2; t1.tags){ 111 if(t2.name is null){ 112 switch(t2.getAttribute!string("keymodifier")){ 113 case "buttons": 114 uint scanCode = t2.getAttribute!int("keyCode", joyButtonNameDict.decode(t2.getAttribute!string("keyName"))); 115 keyBindingList ~= KeyBinding(0, scanCode, devicenumber, t2.expectValue!string(), Devicetype.JOYSTICK); 116 break; 117 case "dpad": 118 uint scanCode = t2.getAttribute!int("keyCode"); 119 keyBindingList ~= KeyBinding(4, scanCode, devicenumber, t2.expectValue!string(), Devicetype.JOYSTICK); 120 break; 121 case "axis": 122 uint scanCode = t2.getAttribute!int("keyCode", joyAxisNameDict.decode(t2.getAttribute!string("keyName"))); 123 keyBindingList ~= KeyBinding(8, scanCode, devicenumber, t2.expectValue!string(), Devicetype.JOYSTICK); 124 break; 125 default: 126 uint scanCode = t2.getAttribute!int("keyCode"); 127 keyBindingList ~= KeyBinding(0, scanCode, devicenumber, t2.expectValue!string(), Devicetype.JOYSTICK); 128 break; 129 } 130 }else if(t2.name == "enableForceFeedback"){ 131 device.enableForceFeedback = t2.getValue!bool(true); 132 }else if(t2.name == "axisDeadzone"){ 133 device.axisDeadZonePlus[t2.getAttribute!int("axisNumber", joyAxisNameDict.decode(t2.getAttribute!string("axisName")))] = t2.expectAttribute!int("plus"); 134 device.axisDeadZoneMinus[t2.getAttribute!int("axisNumber", joyAxisNameDict.decode(t2.getAttribute!string("axisName")))] = t2.expectAttribute!int("minus"); 135 } 136 } 137 break; 138 case "mouse": 139 device = InputDeviceData(devicenumber, Devicetype.MOUSE, name); 140 foreach(Tag t2; t1.tags){ 141 if(t2.name is null){ 142 uint scanCode = t2.getAttribute!int("keyCode"); 143 keyBindingList ~= KeyBinding(0, scanCode, devicenumber, t2.expectValue!string(), Devicetype.MOUSE); 144 } 145 } 146 break; 147 default: 148 device = InputDeviceData(devicenumber, Devicetype.KEYBOARD, name); 149 break; 150 } 151 inputDevices ~= device; 152 break; 153 default: break; 154 } 155 } 156 }else if(t0.name == "etc"){ 157 foreach(Tag t1; t0.tags){ 158 auxillaryParameters ~= AuxillaryElements(t1.name(), t1.getValue!string()); 159 } 160 } 161 } 162 } 163 catch(ParseException e){ 164 writeln(e.msg); 165 } 166 167 168 169 } 170 public void store(){ 171 172 173 Tag root = new Tag(null, null); //, [Value(appName), Value(appVers)] 174 175 Tag t0 = new Tag(root, null, "audio"); 176 Tag t0_0 = new Tag(t0, null, "soundVol", [Value(sfxVol)]); 177 Tag t0_1 = new Tag(t0, null, "musicVolt", [Value(musicVol)]); 178 179 Tag t1 = new Tag(root, null, "video"); 180 Tag t1_0 = new Tag(t1, null, "driver", [Value(driver)]); 181 Tag t1_1 = new Tag(t1, null, "scaling", [Value(scalingQuality)]); 182 Tag t1_2 = new Tag(t1, null, "screenMode", [Value(screenMode)]); 183 Tag t1_3 = new Tag(t1, null, "resolution", [Value(resolution)]); 184 Tag t1_4 = new Tag(t1, null, "threads", [Value(threads)]); 185 186 Tag t2 = new Tag(root, null, "input"); 187 foreach(InputDeviceData idd; inputDevices){ 188 string devType = devicetypeStrings[idd.type]; 189 Tag t2_0 = new Tag(t2, null, "device", null, [new Attribute(null, "name",Value(idd.name)), new Attribute(null, "type", Value(devType)), new Attribute(null, "devNum", Value(idd.deviceNumber))]); 190 if(idd.type == Devicetype.KEYBOARD){ 191 foreach(KeyBinding k; keyBindingList){ 192 if(k.devicetype == idd.type && k.devicenumber == idd.deviceNumber){ 193 Attribute key; 194 string s = keyNameDict.encode(k.scancode); 195 if(s is null){ 196 key = new Attribute(null, "keyCode", Value(to!int(k.scancode))); 197 }else{ 198 key = new Attribute(null, "keyName", Value(s)); 199 } 200 new Tag(t2_0, null, null, [Value(k.ID)], [key, new Attribute(null, "keyMod", Value(keymodToString(k.keymod))), new Attribute(null, "keyModIgnore", Value(keymodToString(k.keymodIgnore)))]); 201 } 202 } 203 }else if(idd.type == Devicetype.JOYSTICK){ 204 foreach(KeyBinding k; keyBindingList){ 205 if(k.devicetype == idd.type && k.devicenumber == idd.deviceNumber){ 206 new Tag(t2_0, null, null, [Value(k.ID)], [new Attribute(null, "keyCode", Value(to!int(k.scancode))), new Attribute(null, "keyMod", Value(joymodifierStrings[k.keymod]))]); 207 208 } 209 } 210 new Tag(t2_0, null, "enableForceFeedback", [Value(idd.enableForceFeedback)]); 211 foreach(int i; idd.axisDeadZonePlus.byKey){ 212 new Tag(t2_0, null, "axisDeadzone", null, [new Attribute(null, "axisNumber", Value(i) ), new Attribute(null, "plus", Value(idd.axisDeadZonePlus[i]) ), new Attribute(null, "minus", Value(idd.axisDeadZoneMinus[i]) )]); 213 } 214 }else if(idd.type == Devicetype.MOUSE){ 215 foreach(KeyBinding k; keyBindingList){ 216 if(k.devicetype == idd.type && k.devicenumber == idd.deviceNumber){ 217 new Tag(t2_0, null, null, [Value(k.ID)], [new Attribute(null, "keyCode", Value(to!int(k.scancode)))]); 218 } 219 } 220 } 221 } 222 Tag t3 = new Tag(root, null, "etc"); 223 foreach(AuxillaryElements ae; auxillaryParameters){ 224 new Tag(t3, null, ae.name, [Value(ae.value)]); 225 } 226 string data = root.toSDLDocument(); 227 std.file.write(path, data); 228 } 229 230 public ushort stringToKeymod(string s){ 231 if(s == "NONE;") return KeyModifier.NONE; 232 if(s == "ANY;") return KeyModifier.ANY; 233 string[] values = csvParser(s, ';'); 234 int result; 235 foreach(t ; values){ 236 result += keymodifierStrings[t]; 237 } 238 return to!ushort(result); 239 } 240 241 public string keymodToString(ushort keymod){ 242 if(keymod == KeyModifier.NONE) 243 return "NONE;"; 244 if(keymod == KeyModifier.ANY) 245 return "ANY;"; 246 string result; 247 if(keymod & KeyModifier.LSHIFT){ 248 result ~= "LSHIFT;"; 249 } 250 if(keymod & KeyModifier.RSHIFT){ 251 result ~= "RSHIFT;"; 252 } 253 if(keymod & KeyModifier.LCTRL){ 254 result ~= "LCTRL;"; 255 } 256 if(keymod & KeyModifier.RCTRL){ 257 result ~= "RCTRL;"; 258 } 259 if(keymod & KeyModifier.LALT){ 260 result ~= "LALT;"; 261 } 262 if(keymod & KeyModifier.RALT){ 263 result ~= "RALT;"; 264 } 265 if(keymod & KeyModifier.LGUI){ 266 result ~= "LGUI;"; 267 } 268 if(keymod & KeyModifier.RGUI){ 269 result ~= "RGUI;"; 270 } 271 if(keymod & KeyModifier.NUM){ 272 result ~= "NUM;"; 273 } 274 if(keymod & KeyModifier.CAPS){ 275 result ~= "CAPS;"; 276 } 277 if(keymod & KeyModifier.MODE){ 278 result ~= "MODE;"; 279 } 280 if(keymod & KeyModifier.RESERVED){ 281 result ~= "RESERVED;"; 282 } 283 return result; 284 } 285 286 public string joymodToString(ushort s){ 287 switch(s){ 288 case JoyModifier.AXIS: return "AXIS"; 289 case JoyModifier.DPAD: return "DPAD"; 290 default: return "BUTTONS"; 291 } 292 } 293 public void useVideoMode(int mode, OutputScreen window){ 294 295 } 296 public void autodetectVideoModes(int display = 0){ 297 int displaymodes = SDL_GetNumDisplayModes(display); 298 //writeln(displaymodes); 299 //writeln(to!string(SDL_GetError())); 300 for(int i ; i <= displaymodes ; i++){ 301 SDL_DisplayMode d = SDL_DisplayMode(); 302 if(SDL_GetDisplayMode(display,i,&d) == 0){ 303 304 videoModes ~= d; 305 306 } 307 } 308 } 309 public size_t getNumOfVideoModes(){ 310 return videoModes.length; 311 } 312 public string videoModeToString(size_t n){ 313 return to!string(videoModes[n].w) ~ "x" ~ to!string(videoModes[n].h) ~ "@" ~ to!string(videoModes[n].refresh_rate) ~ "Hz"; 314 } 315 public static void setVaultPath(const char* developer, const char* application){ 316 vaultPath = to!string(SDL_GetPrefPath(developer, application)); 317 } 318 public static string getVaultPath(){ 319 return vaultPath; 320 } 321 322 } 323 /** 324 * Deprecated, not up to current standards, will be upgraded to take advantage of the SDLang format. 325 */ 326 public struct AuxillaryElements{ 327 public string value, name; 328 public this(string name, string value){ 329 this.name = name; 330 this.value = value; 331 } 332 } 333 334 /** 335 * Stores basic InputDevice info alongside with some additional settings 336 */ 337 public struct InputDeviceData{ 338 public int deviceNumber; 339 public Devicetype type; 340 public string name; 341 public bool enableForceFeedback; 342 public int[int] axisDeadZonePlus, axisDeadZoneMinus; 343 public this(int deviceNumber, Devicetype type, string name){ 344 this.deviceNumber = deviceNumber; 345 this.type = type; 346 this.name = name; 347 } 348 } 349 350 /*public class VideoMode{ 351 public int displayIndex, modeIndex; 352 public SDL_DisplayMode displaymode; 353 public this (){ 354 355 } 356 public override string toString(){ 357 return to!string(displaymode.w) ~ "x" ~ to!string(displaymode.h) ~ "@" ~ to!string(displaymode.refresh_rate) ~ "Hz"; 358 } 359 }*/ 360 /** 361 * Default keywords to look up for common video settings 362 */ 363 public enum VideoConfigDefaults : string{ 364 SCREENMODE = "screenMode", 365 RESOLUTION = "resolution", 366 SCALINGQUALITY = "scalingQuality", 367 DRIVER = "driver", 368 THREADS = "threads", 369 }