Changeset 14:0047b364b6d9
- Timestamp:
- 03/07/08 12:51:02
(10 months ago)
- Author:
- Diggory Hardy <diggory.hardy@gmail.com>
- branch:
- default
- convert_revision:
- d82c68b347390eb6bb28020500056e0060b36de6
- Message:
Changed much of the mergetag structure and some functionality. First tests on windows.
Changes to mergetag Reader methods. New functionality allowing a dataSecCreator to cause sections to be skipped.
Moved several of the mergetag modules and some of their contents around. Moved all interfaces to separate modules in iface/ .
IReader & IWriter interfaces exist; MTTReader, MTBReader, MTTWriter, MTBWriter & DualWriter? all now exist and implement IReader/IWriter (although the MTB variants are dummy classes); makeReader & makeWriter should both be fully functional.
Tested building on windows with partial success (works but window won't open).
Included a temporary hack from windows to get supported resolutions information.
committer: Diggory Hardy <diggory.hardy@gmail.com>
-
Files:
-
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
| r10 |
r14 |
|
| 1 | 1 | {MT01} |
|---|
| 2 | | <char[][]|Configs=["Std"]> |
|---|
| 3 | | <char[]|Test="1"> |
|---|
| | 2 | !<char[][]|Configs=["Default"]> |
|---|
| 4 | 3 | {Default} |
|---|
| 5 | 4 | <uint[][uint]|B=[ 0x8800001B : [[0x1000, 0x0]] ]> |
|---|
| r12 |
r14 |
|
| 1 | 1 | {MT01} |
|---|
| 2 | 2 | {Default} |
|---|
| 3 | | <char[]|greeting="Hi â options now work!"> |
|---|
| 4 | 3 | <char[]|L10n="en-GB"> |
|---|
| 5 | 4 | |
|---|
| r12 |
r14 |
|
| 2 | 2 | |
|---|
| 3 | 3 | To do: |
|---|
| 4 | | * Submit new version of parseTo |
|---|
| 5 | | * Why doesn't input.config filtering via headers "Configs" work? |
|---|
| | 4 | * Control logging level/output via options. |
|---|
| | 5 | * Read config from system and user paths. |
|---|
| | 6 | * Windows building/compatibility (currently partial) |
|---|
| | 7 | * gdc building/compatibility (wait for tango 0.99.5 release?) |
|---|
| | 8 | * Config loaded from correct places |
|---|
| 6 | 9 | * OutOfMemoryException is not currently checked for â it should be at least in critical places (use high-level catching of all errors?). |
|---|
| 7 | 10 | * Sensitivity adjustments. From es_a_out: |
|---|
| … | … | |
| 34 | 37 | |
|---|
| 35 | 38 | Done (for git log message): |
|---|
| 36 | | * Implemented i18n.I18nTranslation class to load strings and descriptions from files (with unittest). |
|---|
| 37 | | * MTUnknownTypeException removed: its pointless since it's always ignored without even any message. |
|---|
| 38 | | * A few fixes to mde.mergetag.read.Reader regarding partial reading. |
|---|
| | 39 | Changes to mergetag Reader methods. New functionality allowing a dataSecCreator to cause sections to be skipped. |
|---|
| | 40 | Moved several of the mergetag modules and some of their contents around. Moved all interfaces to separate modules in iface/ . |
|---|
| | 41 | Mergetag: IReader & IWriter interfaces exist; MTTReader, MTBReader, MTTWriter, MTBWriter & DualWriter all now exist and implement IReader/IWriter (although the MTB variants are dummy classes); makeReader & makeWriter should both be fully functional. |
|---|
| | 42 | Tested building on windows with partial success (works but window won't open). |
|---|
| | 43 | Included a temporary hack from windows to get supported resolutions information. |
|---|
| r11 |
r14 |
|
| 1 | | defaulttargets = mde/mde.d |
|---|
| | 1 | version (Posix) { |
|---|
| | 2 | defaulttargets = mde/mde.d |
|---|
| | 3 | } else version (Win32) { |
|---|
| | 4 | defaulttargets = mde\mde.d |
|---|
| | 5 | } |
|---|
| 2 | 6 | |
|---|
| 3 | 7 | [*] |
|---|
| 4 | 8 | version (Posix) { |
|---|
| 5 | 9 | buildflags=-L-ldl |
|---|
| 6 | | } else { |
|---|
| 7 | | warn Only posix builds have been tested; elsewhere other libraries will probably need to be linked. |
|---|
| 8 | 10 | } |
|---|
| 9 | 11 | |
|---|
| 10 | 12 | [mde/mde.d] |
|---|
| 11 | 13 | target=bin/mde |
|---|
| | 14 | |
|---|
| | 15 | #[mde\mde.d] |
|---|
| | 16 | #target=bin\mde |
|---|
| 12 | 17 | |
|---|
| 13 | 18 | [test/mdeTest.d] |
|---|
| … | … | |
| 17 | 22 | version (Posix) { |
|---|
| 18 | 23 | buildflags+=-L-ldl |
|---|
| 19 | | } else { |
|---|
| 20 | | warn Only posix builds have been tested; elsewhere other libraries will probably need to be linked. |
|---|
| 21 | 24 | } |
|---|
| r12 |
r14 |
|
| 27 | 27 | import mde.exception; |
|---|
| 28 | 28 | |
|---|
| 29 | | import mde.mergetag.dataset; |
|---|
| 30 | | import mde.mergetag.read; |
|---|
| | 29 | import mde.mergetag.DataSet; |
|---|
| | 30 | import mde.mergetag.Reader; |
|---|
| 31 | 31 | import mde.mergetag.exception; |
|---|
| 32 | 32 | |
|---|
| … | … | |
| 40 | 40 | * Encoding used is UTF-8. |
|---|
| 41 | 41 | */ |
|---|
| 42 | | class I18nTranslation : DataSection |
|---|
| | 42 | class I18nTranslation : IDataSection |
|---|
| 43 | 43 | { |
|---|
| 44 | 44 | final char[] name; /// The module/package/... which the instance is for |
|---|
| … | … | |
| 85 | 85 | */ |
|---|
| 86 | 86 | FilePath filePath = new FilePath ("conf/L10n/"~name~".mtt"); |
|---|
| 87 | | // If it's not a file or an empty file stop. |
|---|
| 88 | | if (!filePath.exists || filePath.fileSize == 0u) { |
|---|
| | 87 | // If it doesn't exist, stop. |
|---|
| | 88 | // Don't bother checking it's not a folder, because it could still be a block or something. |
|---|
| | 89 | if (!filePath.exists) { |
|---|
| 89 | 90 | throw new L10nLoadException ("No database file conf/L10n/"~name~".mtt exists!"); |
|---|
| 90 | 91 | } |
|---|
| 91 | 92 | |
|---|
| 92 | | Reader reader; |
|---|
| | 93 | IReader reader; |
|---|
| 93 | 94 | try { |
|---|
| 94 | | reader = new Reader(filePath); |
|---|
| | 95 | reader = new MTTReader(filePath); |
|---|
| 95 | 96 | /* Note: we don't want to load every translation section depended on to its own class |
|---|
| 96 | 97 | * instance, since we want to merge them. So make every mergetag section use the same |
|---|
| 97 | 98 | * instance. */ |
|---|
| 98 | | reader.dataSecCreator = delegate DataSection(ID) { |
|---|
| | 99 | reader.dataSecCreator = delegate IDataSection(ID) { |
|---|
| 99 | 100 | return transl; |
|---|
| 100 | 101 | }; |
|---|
| r12 |
r14 |
|
| 19 | 19 | import tango.util.log.Log : Log, Logger; |
|---|
| 20 | 20 | import tango.util.log.ConsoleAppender : ConsoleAppender; |
|---|
| 21 | | import tango.stdc.stringz : fromUtf8z; |
|---|
| | 21 | import tango.stdc.stringz : fromStringz; |
|---|
| 22 | 22 | |
|---|
| 23 | 23 | import derelict.sdl.sdl; |
|---|
| … | … | |
| 36 | 36 | // For now, just log to the console: |
|---|
| 37 | 37 | Logger root = Log.getRootLogger(); |
|---|
| 38 | | root.setLevel(root.Level.Trace); |
|---|
| | 38 | debug root.setLevel(root.Level.Trace); |
|---|
| | 39 | else root.setLevel(root.Level.Info); |
|---|
| 39 | 40 | root.addAppender(new ConsoleAppender); |
|---|
| 40 | 41 | } |
|---|
| … | … | |
| 93 | 94 | return; |
|---|
| 94 | 95 | } |
|---|
| 95 | | logger.info ("Derelict: loaded SDL"); |
|---|
| | 96 | logger.trace ("Derelict: loaded SDL"); |
|---|
| 96 | 97 | |
|---|
| 97 | 98 | if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_JOYSTICK /+| SDL_INIT_EVENTTHREAD+/)) { |
|---|
| 98 | 99 | logger.fatal ("SDL initialisation failed:"); |
|---|
| 99 | 100 | char* msg = SDL_GetError (); |
|---|
| 100 | | logger.fatal (msg ? fromUtf8z(msg) : "no reason available"); |
|---|
| | 101 | logger.fatal (msg ? fromStringz(msg) : "no reason available"); |
|---|
| 101 | 102 | |
|---|
| 102 | 103 | setFailure (); // abort |
|---|
| … | … | |
| 104 | 105 | } |
|---|
| 105 | 106 | |
|---|
| 106 | | SDL_SetVideoMode (800, 600, 0, 0); |
|---|
| 107 | | |
|---|
| 108 | 107 | addCleanupFct (&cleanupSDL); |
|---|
| 109 | | logger.info ("SDL initialised"); |
|---|
| | 108 | logger.trace ("SDL initialised"); |
|---|
| | 109 | |
|---|
| | 110 | // Print a load of info: |
|---|
| | 111 | logger.info ("Available video modes:"); |
|---|
| | 112 | char[128] tmp; |
|---|
| | 113 | SDL_Rect** modes = SDL_ListModes (null, SDL_FULLSCREEN); |
|---|
| | 114 | if (modes is null) logger.info ("None!"); |
|---|
| | 115 | else if (modes is cast(SDL_Rect**) -1) logger.info ("All modes are available"); |
|---|
| | 116 | else { |
|---|
| | 117 | for (uint i = 0; modes[i] !is null; ++i) { |
|---|
| | 118 | logger.info (logger.format (tmp, "\t{}x{}", modes[i].w, modes[i].h)); |
|---|
| | 119 | } |
|---|
| | 120 | } |
|---|
| | 121 | |
|---|
| | 122 | SDL_VideoInfo* vi = SDL_GetVideoInfo (); |
|---|
| | 123 | if (vi !is null) { |
|---|
| | 124 | logger.info ("Video info:"); |
|---|
| | 125 | logger.info ("Hardware surface support: "~ (vi.flags & SDL_HWSURFACE ? "yes" : "no")); |
|---|
| | 126 | logger.info (logger.format (tmp, "Video memory: {}", vi.video_mem)); |
|---|
| | 127 | |
|---|
| | 128 | if (vi.vfmt !is null) { |
|---|
| | 129 | logger.info ("Best video mode:"); |
|---|
| | 130 | logger.info (logger.format (tmp, "Bits per pixel: {}", vi.vfmt.BitsPerPixel)); |
|---|
| | 131 | } |
|---|
| | 132 | } |
|---|
| | 133 | |
|---|
| | 134 | // FIXME: make this non-fatal and provide a way to re-set video mode |
|---|
| | 135 | if (SDL_SetVideoMode (800, 600, 0, 0) is null) { // Can't open in windows!! |
|---|
| | 136 | logger.fatal ("Unable to set video mode:"); |
|---|
| | 137 | char* msg = SDL_GetError (); |
|---|
| | 138 | logger.fatal (msg ? fromStringz(msg) : "no reason available"); |
|---|
| | 139 | |
|---|
| | 140 | setFailure (); |
|---|
| | 141 | return; |
|---|
| | 142 | } |
|---|
| 110 | 143 | |
|---|
| 111 | 144 | openJoysticks (); // after SDL init |
|---|
| … | … | |
| 164 | 197 | try { |
|---|
| 165 | 198 | t.join (true); |
|---|
| 166 | | /+ Will only catch thread exceptions; but even so something still went badly wrong so we want the same functionality. |
|---|
| | 199 | /+ We might as well catch and report any exception, rather than just ThreadExceptions: |
|---|
| 167 | 200 | } catch (ThreadException e) { // Any threading exception |
|---|
| 168 | 201 | +/ |
|---|
| … | … | |
| 195 | 228 | { |
|---|
| 196 | 229 | if (!initFailure) { |
|---|
| 197 | | logger.info ("Cleaning up..."); |
|---|
| | 230 | logger.trace ("Cleaning up..."); |
|---|
| 198 | 231 | runCleanupFcts(); // if threading, note not all functions can be called simultaeneously |
|---|
| 199 | | logger.info ("Done!"); |
|---|
| | 232 | logger.trace ("Done!"); |
|---|
| 200 | 233 | } |
|---|
| 201 | 234 | } |
|---|
| r12 |
r14 |
|
| 6 | 6 | import mde.input.exception; |
|---|
| 7 | 7 | |
|---|
| 8 | | import MT = mde.mergetag.read; |
|---|
| | 8 | import MT = mde.mergetag.Reader; |
|---|
| 9 | 9 | import tango.scrapple.text.convert.parseTo : parseTo; |
|---|
| 10 | 10 | |
|---|
| … | … | |
| 17 | 17 | * Class extends DataSection so that it can be loaded by mergetag easily. |
|---|
| 18 | 18 | */ |
|---|
| 19 | | class Config : MT.DataSection |
|---|
| | 19 | class Config : MT.IDataSection |
|---|
| 20 | 20 | { |
|---|
| 21 | 21 | alias uint[] outQueue; // This is the type for the out queue config data. |
|---|
| … | … | |
| 118 | 118 | loadedFiles.add (filename); |
|---|
| 119 | 119 | |
|---|
| 120 | | MT.Reader file; |
|---|
| | 120 | MT.IReader file; |
|---|
| 121 | 121 | |
|---|
| 122 | 122 | try { |
|---|
| 123 | | file = new MT.Reader(filename, null, true); // open and read header |
|---|
| | 123 | file = new MT.MTTReader(filename, null, true); // open and read header |
|---|
| 124 | 124 | // TODO: also load user-config file |
|---|
| 125 | 125 | |
|---|
| 126 | 126 | file.dataSecCreator = |
|---|
| 127 | | delegate MT.DataSection (MT.ID) { return new Config; }; |
|---|
| | 127 | delegate MT.IDataSection (MT.ID) { return new Config; }; |
|---|
| 128 | 128 | |
|---|
| 129 | 129 | // D2.0: enum MT.ID CONFIGS = "Configs"; |
|---|
| 130 | 130 | const MT.ID CONFIGS = cast(MT.ID)"Configs"; |
|---|
| 131 | | MT.ID[] file_configs; // active config sections (may not exist) |
|---|
| 132 | | MT.ID[]* file_configs_p = cast(MT.ID[]*) (CONFIGS in file.dataset.header._charAA); |
|---|
| 133 | | debug foreach (i,d; file.dataset.header._charAA) logger.trace ("ID: "~cast(char[])i); |
|---|
| 134 | | debug foreach (i,d; file.dataset.header._charA) logger.trace ("ID: "~cast(char[])i); |
|---|
| 135 | | |
|---|
| 136 | | if (file_configs_p) file.read(*file_configs_p); // restrict to this set IF a restriction was given |
|---|
| | 131 | // Restrict config sections if this tag exists: |
|---|
| | 132 | auto file_configs_p = CONFIGS in file.dataset.header._charAA; |
|---|
| | 133 | MT.ID[] file_configs = null; |
|---|
| | 134 | if (file_configs_p) { |
|---|
| | 135 | file_configs = cast(MT.ID[]) *file_configs_p; |
|---|
| | 136 | } |
|---|
| | 137 | |
|---|
| | 138 | if (file_configs) file.read(file_configs); // restrict to this set IF a restriction was given |
|---|
| 137 | 139 | else file.read(); // otherwise read all |
|---|
| 138 | 140 | } |
|---|
| r12 |
r14 |
|
| 123 | 123 | /** Feed an SDL_Event struct (only uses if it's a key, mouse or joystick event). |
|---|
| 124 | 124 | * |
|---|
| 125 | | * Other types of event functions may be added. Returns true if the event was used, false if not. |
|---|
| | 125 | * Other types of event functions may be added. Returns true if the event was used, false if not |
|---|
| | 126 | * or no config was available. Hmm... doesn't seem very useful, but has practically no cost. |
|---|
| 126 | 127 | * |
|---|
| 127 | 128 | * May throw InputClassExceptions (on configuration errors). Catching the exception and continuing should |
|---|
| … | … | |
| 129 | 130 | */ |
|---|
| 130 | 131 | bool opCall (ref SDL_Event event) { |
|---|
| | 132 | /* Non-config events. |
|---|
| | 133 | * |
|---|
| | 134 | * Mouse events don't need config for the GUI. Handle them first so that if no config exists |
|---|
| | 135 | * some functionality at least is retained. |
|---|
| | 136 | */ |
|---|
| | 137 | switch (event.type) { |
|---|
| | 138 | case SDL_MOUSEBUTTONDOWN: |
|---|
| | 139 | case SDL_MOUSEBUTTONUP: |
|---|
| | 140 | foreach (dg; mouseClickCallbacks) |
|---|
| | 141 | dg (event.button.x, event.button.y, event.button.button, event.button.state == SDL_PRESSED); |
|---|
| | 142 | break; |
|---|
| | 143 | |
|---|
| | 144 | case SDL_MOUSEMOTION: |
|---|
| | 145 | mouse_x = event.motion.x; |
|---|
| | 146 | mouse_y = event.motion.y; |
|---|
| | 147 | break; |
|---|
| | 148 | |
|---|
| | 149 | default: |
|---|
| | 150 | } |
|---|
| | 151 | |
|---|
| | 152 | /* No config available, so don't try to access it and segfault. |
|---|
| | 153 | * Don't log a message because this function is called per-event (i.e. frequently). |
|---|
| | 154 | * A message should already have been logged by loadConfig anyway. |
|---|
| | 155 | */ |
|---|
| | 156 | if (!config) return false; |
|---|
| | 157 | |
|---|
| 131 | 158 | switch (event.type) { |
|---|
| 132 | 159 | // Keyboard events: |
|---|
| … | … | |
| 142 | 169 | case SDL_MOUSEBUTTONDOWN: |
|---|
| 143 | 170 | case SDL_MOUSEBUTTONUP: |
|---|
| 144 | | // Mouse clicks: |
|---|
| 145 | | foreach (dg; mouseClickCallbacks) |
|---|
| 146 | | dg (event.button.x, event.button.y, event.button.button, event.button.state == SDL_PRESSED); |
|---|
| 147 | | |
|---|
| 148 | 171 | // Button events: |
|---|
| 149 | 172 | outQueue[]* p = (Config.B.MOUSE | event.button.button) in config.button; |
|---|
| … | … | |
| 154 | 177 | |
|---|
| 155 | 178 | case SDL_MOUSEMOTION: |
|---|
| 156 | | // Screen coordinates (set here since relMotion is also used for joystick balls): |
|---|
| 157 | | mouse_x = event.motion.x; |
|---|
| 158 | | mouse_y = event.motion.y; |
|---|
| 159 | | |
|---|
| 160 | 179 | // Relative motion: |
|---|
| 161 | 180 | outQueue[]* p = (Config.M.WMMOUSE) in config.relMotion; |
|---|
| … | … | |
| 268 | 287 | return false; |
|---|
| 269 | 288 | } |
|---|
| 270 | | debug logger.warn ("Config \""~profile~"\" not found."); |
|---|
| | 289 | logger.warn ("Config profile \""~profile~"\" not found: input won't work unless a valid profile is loaded!"); |
|---|
| 271 | 290 | return true; |
|---|
| 272 | 291 | } |
|---|
| r0 |
r14 |
|
| 4 | 4 | BOM --- a Byte Order Mark should be used to determin endianness (MT01 (or other version) in bytes, but converted to two ushorts to detect endianness?) |
|---|
| 5 | 5 | |
|---|
| | 6 | |
|---|
| | 7 | File should then consist of sections: |
|---|
| | 8 | |
|---|
| | 9 | Header data including an address for the header section data if included. |
|---|
| | 10 | |
|---|
| | 11 | Sections list. Include a list of sections with identifiers and addresses, sorted by identifier and in a suitible format to easily be converted to a D hash-map. Addresses for each section should consist of both a start and an end address; the end address should be checked upon reading the section. In addition the start address must be checked against the end of file to avoid security vulnerabilities with reading other memory blocks. |
|---|
| r12 |
r14 |
|
| 45 | 45 | } |
|---|
| 46 | 46 | |
|---|
| 47 | | /** Thrown by addTag (in classes implementing dataset.DataSection) when a data parsing error occurs |
|---|
| | 47 | /** Thrown by addTag (in classes implementing IDataSection) when a data parsing error occurs |
|---|
| 48 | 48 | * (really just to make whoever called addTag to log a warning saying where the error occured). */ |
|---|
| 49 | 49 | class MTaddTagParseException : MTException { |
|---|
| … | … | |
| 66 | 66 | } |
|---|
| 67 | 67 | } |
|---|
| | 68 | |
|---|
| | 69 | /// Thrown when attempting to use an unimplemented part of the package |
|---|
| | 70 | /// Really, just until MTB stuff is implemented |
|---|
| | 71 | class MTNotImplementedException : MTException { |
|---|
| | 72 | this () { |
|---|
| | 73 | super ("Functionality not implemented!"); |
|---|
| | 74 | } |
|---|
| | 75 | } |
|---|
| r10 |
r14 |
|
| 2 | 2 | module mde.mergetag.mtunittest; |
|---|
| 3 | 3 | |
|---|
| 4 | | import mde.mergetag.read; |
|---|
| 5 | | import mde.mergetag.write; |
|---|
| 6 | | import mde.mergetag.defaultdata; |
|---|
| | 4 | import mde.mergetag.Reader; |
|---|
| | 5 | import mde.mergetag.Writer; |
|---|
| | 6 | import mde.mergetag.DataSet; |
|---|
| | 7 | import mde.mergetag.DefaultData; |
|---|
| 7 | 8 | |
|---|
| 8 | 9 | import tango.scrapple.text.convert.parseTo : parseTo; |
|---|
| … | … | |
| 14 | 15 | private Logger logger; |
|---|
| 15 | 16 | static this() { |
|---|
| 16 | | logger = Log.getLogger ("mde.mergetag.unittest"); |
|---|
| | 17 | logger = Log.getLogger ("mde.mergetag.mtunittest"); |
|---|
| 17 | 18 | } |
|---|
| 18 | 19 | |
|---|
| … | … | |
| 21 | 22 | * Thus it provides some basic testing for the whole mergetag package. */ |
|---|
| 22 | 23 | |
|---|
| 23 | | const file = "unittest.mtt"; |
|---|
| | 24 | const file = "unittest"; |
|---|
| 24 | 25 | const ID UT_ID = cast (ID) "mdeUT"; |
|---|
| 25 | 26 | const headInfo = "mde Unit Test"; |
|---|
| … | … | |
| 42 | 43 | mixin (genUTCode()); // Add an entry to dd for each type |
|---|
| 43 | 44 | |
|---|
| 44 | | // FIXME: if/when binary writing is available, write in both formats |
|---|
| 45 | | IWriter w = makeWriter (file, dsW); |
|---|
| | 45 | IWriter w = makeWriter (file, dsW, WriterMethod.Both); |
|---|
| 46 | 46 | w.write(); |
|---|
| 47 | 47 | |
|---|
| 48 | | Reader r = new Reader (file, null, true); |
|---|
| | 48 | // FIXME: when binary writing is supported, read both formats and check |
|---|
| | 49 | IReader r = makeReader (file~".mtt", null, true); |
|---|
| 49 | 50 | r.read(); |
|---|
| 50 | 51 | |
|---|
| … | … | |
| 57 | 58 | assert (*p == headInfo); |
|---|
| 58 | 59 | |
|---|
| 59 | | DataSection* sec_p = (UT_ID in dsR.sec); |
|---|
| | 60 | IDataSection* sec_p = (UT_ID in dsR.sec); |
|---|
| 60 | 61 | assert (sec_p); |
|---|
| 61 | 62 | DefaultData secR = cast(DefaultData) *sec_p; |
|---|
| r12 |
r14 |
|
| 8 | 8 | import mde.exception; |
|---|
| 9 | 9 | |
|---|
| 10 | | import mde.mergetag.read; |
|---|
| 11 | | import mde.mergetag.write; |
|---|
| 12 | | import mde.mergetag.dataset; |
|---|
| | 10 | import mde.mergetag.Reader; |
|---|
| | 11 | import mde.mergetag.Writer; |
|---|
| | 12 | import mde.mergetag.DataSet; |
|---|
| 13 | 13 | import mde.mergetag.exception; |
|---|
| 14 | 14 | |
|---|
| … | … | |
| 31 | 31 | * All options and handling should be non-static to allow possibility of profiles later. |
|---|
| 32 | 32 | */ |
|---|
| 33 | | class Options : DataSection |
|---|
| | 33 | class Options : IDataSection |
|---|
| 34 | 34 | { |
|---|
| 35 | 35 | /* The options. |
|---|
| … | … | |
| 39 | 39 | * They can be read and set directly by other code (so not thread-safe for writing currently). |
|---|
| 40 | 40 | */ |
|---|
| 41 | | char[] greeting; // just a testing message |
|---|
| 42 | 41 | char[] L10n; // locale, e.g. en-GB |
|---|
| | 42 | |
|---|
| | 43 | /* Have any options been changed? Only bother writing if true. |
|---|
| | 44 | * |
|---|
| | 45 | * Maybe update this in the future to save if the last file edit was not made by mde. */ |
|---|
| | 46 | bool changed = false; |
|---|
| 43 | 47 | |
|---|
| 44 | 48 | /* The code to load/save the values. |
|---|
| … | … | |
| 48 | 52 | void addTag (char[] tp, ID id, char[] dt) { |
|---|
| 49 | 53 | if (tp == "char[]") { |
|---|
| 50 | | if (id == cast(ID)"greeting") greeting = parseTo!(char[]) (dt); |
|---|
| 51 | | else if (id == cast(ID)"L10n") L10n = parseTo!(char[]) (dt); |
|---|
| | 54 | if (id == cast(ID)"L10n") L10n = parseTo!(char[]) (dt); |
|---|
| 52 | 55 | } |
|---|
| 53 | 56 | } |
|---|
| 54 | 57 | void writeAll (ItemDelg dlg) { |
|---|
| 55 | | dlg ("char[]", cast(ID)"greeting", parseFrom!(char[]) (greeting)); |
|---|
| 56 | 58 | dlg ("char[]", cast(ID)"L10n", parseFrom!(char[]) (L10n)); |
|---|
| 57 | 59 | } |
|---|
| … | … | |
| 66 | 68 | static void load () { |
|---|
| 67 | 69 | FilePath filePath = new FilePath (fileName); |
|---|
| 68 | | // If it's not a file or an empty file stop. It should still be created on exit (assuming |
|---|
| 69 | | // it's not a folder). |
|---|
| 70 | | if (!filePath.exists || filePath.fileSize == 0u) return; |
|---|
| 71 | 70 | |
|---|
| 72 | | Reader reader; |
|---|
| | 71 | // Check it exists (if not it should still be created on exit). |
|---|
| | 72 | // Don't bother checking it's not a folder, because it could still be a block or something. |
|---|
| | 73 | if (!filePath.exists) return; |
|---|
| | 74 | |
|---|
| | 75 | IReader reader; |
|---|
| 73 | 76 | try { |
|---|
| 74 | | reader = new Reader(filePath); |
|---|
| 75 | | reader.dataSecCreator = delegate DataSection(ID) { |
|---|
| | 77 | reader = new MTTReader(filePath); |
|---|
| | 78 | reader.dataSecCreator = delegate IDataSection(ID) { |
|---|
| 76 | 79 | return new Options; |
|---|
| 77 | 80 | }; |
|---|
| … | … | |
| 83 | 86 | } |
|---|
| 84 | 87 | |
|---|
| 85 | | DataSection* secP = secName in reader.dataset.sec; |
|---|
| | 88 | IDataSection* secP = secName in reader.dataset.sec; |
|---|
| 86 | 89 | options = cast(Options) *secP; |
|---|
| 87 | 90 | if (options is null) { |
|---|
| … | … | |
| 91 | 94 | } |
|---|
| 92 | 95 | static void save () { |
|---|
| | 96 | if (!options.changed) return; // skip |
|---|
| | 97 | |
|---|
| 93 | 98 | DataSet ds = new DataSet(); |
|---|
| 94 | 99 | ds.sec[secName] = options; |
|---|
| r4 |
r14 |
|
| 9 | 9 | // FIXME: support delegates or not? |
|---|
| 10 | 10 | /// This class can run scheduled functions per frame or every t seconds (sim-time). |
|---|
| 11 | | class Scheduler |
|---|
| | 11 | abstract class Scheduler |
|---|
| 12 | 12 | { |
|---|
| 13 | 13 | /** The type of function pointer to be passed to the scheduler. |
|---|
| r12 |
r14 |
|
| 5 | 5 | // This module should import all mde modules containing unittests: |
|---|
| 6 | 6 | import mde.input.input; |
|---|
| 7 | | import mde.mergetag.dataset; |
|---|
| | 7 | import mde.mergetag.DataSet; |
|---|
| 8 | 8 | import mde.mergetag.mtunittest; |
|---|
| 9 | 9 | import mde.exception; |
|---|