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