Changeset 81:d8fccaa45d5f
- Timestamp:
- 08/29/08 06:59:43
(4 months ago)
- Author:
- Diggory Hardy <diggory.hardy@gmail.com>
- branch:
- default
- Message:
Moved file IO code from mde/mergetag to mde/file/mergetag and changed how some errors are caught.
-
Files:
-
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
| r79 |
r81 |
|
| | 1 | /* LICENSE BLOCK |
|---|
| | 2 | Part of mde: a Modular D game-oriented Engine |
|---|
| | 3 | Copyright © 2007-2008 Diggory Hardy |
|---|
| | 4 | |
|---|
| | 5 | This program is free software: you can redistribute it and/or modify it under the terms |
|---|
| | 6 | of the GNU General Public License as published by the Free Software Foundation, either |
|---|
| | 7 | version 2 of the License, or (at your option) any later version. |
|---|
| | 8 | |
|---|
| | 9 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
|---|
| | 10 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|---|
| | 11 | See the GNU General Public License for more details. |
|---|
| | 12 | |
|---|
| | 13 | You should have received a copy of the GNU General Public License |
|---|
| | 14 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
|---|
| | 15 | |
|---|
| 1 | 16 | /************************************************************************************************** |
|---|
| 2 | 17 | * Generic deserialization templated function. |
|---|
| 3 | 18 | * |
|---|
| 4 | | * copyright: Copyright (c) 2007-2008 Diggory Hardy. |
|---|
| 5 | | * |
|---|
| 6 | | * author: Diggory Hardy, diggory.hardy@gmail.com |
|---|
| 7 | | * |
|---|
| 8 | 19 | * Supports: |
|---|
| 9 | 20 | * Associative arrays, arrays (inc. strings), structs, char types, bool, int types, float types. |
|---|
| 10 | 21 | * |
|---|
| 11 | 22 | * There are also some public utility functions with their own documentation. |
|---|
| 12 | | * |
|---|
| 13 | | * Throws: |
|---|
| 14 | | * On errors, a ParseException or a UnicodeException (both extend TextException) is thrown with a |
|---|
| 15 | | * suitable message. No other exceptions should be thrown. |
|---|
| 16 | 23 | * |
|---|
| 17 | 24 | * Examples: |
|---|
| … | … | |
| 37 | 44 | * ------------------------------------------------------------------------------------------------ |
|---|
| 38 | 45 | * |
|---|
| | 46 | * Throws: |
|---|
| | 47 | * May throw a ParseException or a UnicodeException (which both extend TextException). |
|---|
| | 48 | * |
|---|
| 39 | 49 | * TODO: Optimize memory allocation (if possible?). Test best sizes for initial allocations |
|---|
| 40 | 50 | * instead of merely guessing? |
|---|
| … | … | |
| 42 | 52 | //NOTE: in case of multiple formats, make this a dummy module importing both serialize modules, |
|---|
| 43 | 53 | // or put all the code here. |
|---|
| 44 | | module mde.mergetag.deserialize; |
|---|
| | 54 | module mde.file.deserialize; |
|---|
| 45 | 55 | |
|---|
| 46 | 56 | // tango imports |
|---|
| r79 |
r81 |
|
| 16 | 16 | /** This module contains the mergetag DataSet class, used for all reading and writing operations. |
|---|
| 17 | 17 | */ |
|---|
| 18 | | module mde.mergetag.DataSet; |
|---|
| | 18 | module mde.file.mergetag.DataSet; |
|---|
| 19 | 19 | |
|---|
| 20 | 20 | // package imports |
|---|
| 21 | | public import mde.mergetag.iface.IDataSection; |
|---|
| 22 | | import mde.mergetag.DefaultData; |
|---|
| 23 | | import mde.mergetag.exception; |
|---|
| | 21 | public import mde.file.mergetag.iface.IDataSection; |
|---|
| | 22 | import mde.file.mergetag.DefaultData; |
|---|
| 24 | 23 | |
|---|
| 25 | 24 | |
|---|
| r79 |
r81 |
|
| 17 | 17 | * other types of DataSection. |
|---|
| 18 | 18 | */ |
|---|
| 19 | | module mde.mergetag.DefaultData; |
|---|
| | 19 | module mde.file.mergetag.DefaultData; |
|---|
| 20 | 20 | |
|---|
| 21 | | public import mde.mergetag.iface.IDataSection; |
|---|
| 22 | | import mde.mergetag.exception; |
|---|
| 23 | | |
|---|
| 24 | | import mde.mergetag.serialize; |
|---|
| | 21 | public import mde.file.mergetag.iface.IDataSection; |
|---|
| | 22 | import mde.file.serialize; |
|---|
| 25 | 23 | |
|---|
| 26 | 24 | |
|---|
| r80 |
r81 |
|
| 17 | 17 | * This module contains all reading functions, for both binary and text MergeTag files. |
|---|
| 18 | 18 | *************************************************************************************************/ |
|---|
| 19 | | module mde.mergetag.Reader; |
|---|
| | 19 | module mde.file.mergetag.Reader; |
|---|
| 20 | 20 | |
|---|
| 21 | 21 | // package imports |
|---|
| 22 | | public import mde.mergetag.iface.IReader; |
|---|
| 23 | | import mde.mergetag.DataSet; |
|---|
| 24 | | import mde.mergetag.DefaultData; |
|---|
| 25 | | import mde.mergetag.exception; |
|---|
| 26 | | import mde.mergetag.internal; |
|---|
| | 22 | public import mde.file.mergetag.iface.IReader; |
|---|
| | 23 | import mde.file.mergetag.DataSet; |
|---|
| | 24 | import mde.file.mergetag.DefaultData; |
|---|
| | 25 | import mde.file.mergetag.exception; |
|---|
| | 26 | import mde.file.mergetag.internal; |
|---|
| 27 | 27 | |
|---|
| 28 | 28 | import tango.core.Exception; |
|---|
| r67 |
r81 |
|
| 27 | 27 | * instance of the appropriate class. |
|---|
| 28 | 28 | *************************************************************************************************/ |
|---|
| 29 | | module mde.mergetag.Writer; |
|---|
| | 29 | module mde.file.mergetag.Writer; |
|---|
| 30 | 30 | |
|---|
| 31 | 31 | // package imports |
|---|
| 32 | | public import mde.mergetag.iface.IWriter; |
|---|
| 33 | | import mde.mergetag.DataSet; |
|---|
| 34 | | import mde.mergetag.internal; |
|---|
| 35 | | import mde.mergetag.exception; |
|---|
| | 32 | public import mde.file.mergetag.iface.IWriter; |
|---|
| | 33 | import mde.file.mergetag.DataSet; |
|---|
| | 34 | import mde.file.mergetag.internal; |
|---|
| | 35 | import mde.file.mergetag.exception; |
|---|
| 36 | 36 | |
|---|
| 37 | 37 | // tango imports |
|---|
| r26 |
r81 |
|
| 19 | 19 | * Publically imports mde.exception. |
|---|
| 20 | 20 | ******************************************/ |
|---|
| 21 | | module mde.mergetag.exception; |
|---|
| | 21 | module mde.file.mergetag.exception; |
|---|
| 22 | 22 | |
|---|
| 23 | 23 | public import mde.exception; |
|---|
| r70 |
r81 |
|
| 21 | 21 | * Also some base mergetag symbols have been moved here. |
|---|
| 22 | 22 | */ |
|---|
| 23 | | module mde.mergetag.iface.IDataSection; |
|---|
| | 23 | module mde.file.mergetag.iface.IDataSection; |
|---|
| 24 | 24 | |
|---|
| 25 | 25 | /** Typedef for data & section indexes. |
|---|
| r26 |
r81 |
|
| 17 | 17 | * Interface for readers. |
|---|
| 18 | 18 | */ |
|---|
| 19 | | module mde.mergetag.iface.IReader; |
|---|
| | 19 | module mde.file.mergetag.iface.IReader; |
|---|
| 20 | 20 | |
|---|
| 21 | | import mde.mergetag.DataSet; |
|---|
| | 21 | import mde.file.mergetag.DataSet; |
|---|
| 22 | 22 | |
|---|
| 23 | 23 | import tango.util.collection.model.View : View; |
|---|
| r26 |
r81 |
|
| 17 | 17 | * Interface for writers. |
|---|
| 18 | 18 | */ |
|---|
| 19 | | module mde.mergetag.iface.IWriter; |
|---|
| | 19 | module mde.file.mergetag.iface.IWriter; |
|---|
| 20 | 20 | |
|---|
| 21 | | import mde.mergetag.DataSet; |
|---|
| | 21 | import mde.file.mergetag.DataSet; |
|---|
| 22 | 22 | |
|---|
| 23 | 23 | |
|---|
| r26 |
r81 |
|
| 15 | 15 | |
|---|
| 16 | 16 | /// Contains functions/data structures used internally by mergetag. |
|---|
| 17 | | module mde.mergetag.internal; |
|---|
| | 17 | module mde.file.mergetag.internal; |
|---|
| 18 | 18 | |
|---|
| 19 | 19 | package abstract class MTFormatVersion { |
|---|
| r70 |
r81 |
|
| 15 | 15 | |
|---|
| 16 | 16 | /// This module provides a unittest for mergetag. |
|---|
| 17 | | module mde.mergetag.mtunittest; |
|---|
| | 17 | module mde.file.mergetag.mdeUT; |
|---|
| 18 | 18 | |
|---|
| 19 | 19 | debug (mdeUnitTest) { |
|---|
| 20 | | import mde.mergetag.Reader; |
|---|
| 21 | | import mde.mergetag.Writer; |
|---|
| 22 | | import mde.mergetag.DataSet; |
|---|
| 23 | | import mde.mergetag.DefaultData; |
|---|
| 24 | | import mde.mergetag.parse.parseTo : parseTo; |
|---|
| 25 | | import mde.mergetag.parse.parseFrom : parseFrom; |
|---|
| | 20 | import mde.file.mergetag.Reader; |
|---|
| | 21 | import mde.file.mergetag.Writer; |
|---|
| | 22 | import mde.file.mergetag.DataSet; |
|---|
| | 23 | import mde.file.mergetag.DefaultData; |
|---|
| | 24 | import mde.file.deserialize; |
|---|
| | 25 | import mde.file.serialize; |
|---|
| 26 | 26 | |
|---|
| 27 | 27 | import tango.io.FilePath; |
|---|
| … | … | |
| 30 | 30 | private Logger logger; |
|---|
| 31 | 31 | static this() { |
|---|
| 32 | | logger = Log.getLogger ("mde.mergetag.mtunittest"); |
|---|
| | 32 | logger = Log.getLogger ("mde.mergetag.unittest"); |
|---|
| 33 | 33 | } |
|---|
| 34 | 34 | |
|---|
| … | … | |
| 62 | 62 | |
|---|
| 63 | 63 | // FIXME: when binary writing is supported, read both formats and check |
|---|
| 64 | | IReader r = makeReader (file~".mtt", null, true); |
|---|
| | 64 | IReader r = makeReader (FilePath (file~".mtt"), null, true); |
|---|
| 65 | 65 | r.read(); |
|---|
| 66 | 66 | |
|---|
| r79 |
r81 |
|
| | 1 | /* LICENSE BLOCK |
|---|
| | 2 | Part of mde: a Modular D game-oriented Engine |
|---|
| | 3 | Copyright © 2007-2008 Diggory Hardy |
|---|
| | 4 | |
|---|
| | 5 | This program is free software: you can redistribute it and/or modify it under the terms |
|---|
| | 6 | of the GNU General Public License as published by the Free Software Foundation, either |
|---|
| | 7 | version 2 of the License, or (at your option) any later version. |
|---|
| | 8 | |
|---|
| | 9 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
|---|
| | 10 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|---|
| | 11 | See the GNU General Public License for more details. |
|---|
| | 12 | |
|---|
| | 13 | You should have received a copy of the GNU General Public License |
|---|
| | 14 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
|---|
| | 15 | |
|---|
| 1 | 16 | /************************************************************************************************** |
|---|
| 2 | 17 | * Generic serialization templated function. |
|---|
| 3 | | * |
|---|
| 4 | | * copyright: Copyright (c) 2007-2008 Diggory Hardy. |
|---|
| 5 | | * |
|---|
| 6 | | * author: Diggory Hardy, diggory.hardy@gmail.com |
|---|
| 7 | 18 | * |
|---|
| 8 | 19 | * Supports: |
|---|
| … | … | |
| 33 | 44 | * ------------------------------------------------------------------------------------------------ |
|---|
| 34 | 45 | * |
|---|
| | 46 | * throws: |
|---|
| | 47 | * May throw a UnicodeException or an IllegalArgumentException. |
|---|
| | 48 | * |
|---|
| 35 | 49 | * TODO: Optimize memory allocation (if possible?). Test best sizes for initial allocations |
|---|
| 36 | 50 | * instead of merely guessing? |
|---|
| … | … | |
| 38 | 52 | //NOTE: in case of multiple formats, make this a dummy module importing both serialize modules, |
|---|
| 39 | 53 | // or put all the code here. |
|---|
| 40 | | module mde.mergetag.serialize; |
|---|
| | 54 | module mde.file.serialize; |
|---|
| 41 | 55 | // Since serialize is never used in a module where deserialize is not used, save an import: |
|---|
| 42 | | public import mde.mergetag.deserialize; |
|---|
| | 56 | public import mde.file.deserialize; |
|---|
| 43 | 57 | |
|---|
| 44 | 58 | // tango imports |
|---|
| r79 |
r81 |
|
| 22 | 22 | import mde.font.exception; |
|---|
| 23 | 23 | |
|---|
| 24 | | import mde.mergetag.Reader; |
|---|
| 25 | | import mde.mergetag.DataSet; |
|---|
| 26 | | import mde.mergetag.exception; |
|---|
| | 24 | import mde.file.mergetag.Reader; |
|---|
| | 25 | import mde.file.mergetag.DataSet; |
|---|
| 27 | 26 | import mde.setup.paths; |
|---|
| 28 | 27 | |
|---|
| … | … | |
| 30 | 29 | import derelict.opengl.gl; |
|---|
| 31 | 30 | |
|---|
| 32 | | import mde.mergetag.deserialize; |
|---|
| | 31 | import mde.file.deserialize; |
|---|
| 33 | 32 | import tango.stdc.stringz; |
|---|
| 34 | 33 | import Util = tango.text.Util; |
|---|
| … | … | |
| 60 | 59 | private const fileName = "fonts"; |
|---|
| 61 | 60 | void initialize () { |
|---|
| 62 | | if (!confDir.exists (fileName)) |
|---|
| 63 | | throw new fontException ("No font settings file (fonts.[mtt|mtb])"); |
|---|
| 64 | | |
|---|
| 65 | 61 | if (FT_Init_FreeType (&library)) |
|---|
| 66 | 62 | throw new fontException ("error initialising the FreeType library"); |
|---|
| … | … | |
| 111 | 107 | throw new fontException ("No fallback font style specified"); |
|---|
| 112 | 108 | fallbackName = *p; |
|---|
| 113 | | } |
|---|
| 114 | | catch (MTException e) { |
|---|
| 115 | | throw new fontException ("Mergetag exception: "~e.msg); |
|---|
| | 109 | } catch (NoFileException) { |
|---|
| | 110 | throw new fontException ("No font settings file (fonts.[mtt|mtb])"); |
|---|
| | 111 | } catch (Exception e) { |
|---|
| | 112 | throw new fontException ("Reading font settings failed: "~e.msg); |
|---|
| 116 | 113 | } |
|---|
| 117 | 114 | |
|---|
| r80 |
r81 |
|
| 31 | 31 | |
|---|
| 32 | 32 | // For loading from file: |
|---|
| 33 | | import mt = mde.mergetag.DataSet; |
|---|
| 34 | | import mt = mde.mergetag.DefaultData; |
|---|
| 35 | | import mt = mde.mergetag.exception; |
|---|
| 36 | | import mde.mergetag.serialize; |
|---|
| | 33 | import mt = mde.file.mergetag.DataSet; |
|---|
| | 34 | import mt = mde.file.mergetag.DefaultData; |
|---|
| | 35 | import mde.file.serialize; |
|---|
| 37 | 36 | import tango.util.log.Log : Log, Logger; |
|---|
| 38 | 37 | |
|---|
| r80 |
r81 |
|
| 188 | 188 | import mde.gui.widget.createWidget; |
|---|
| 189 | 189 | |
|---|
| 190 | | import mde.mergetag.Reader; |
|---|
| 191 | | import mde.mergetag.Writer; |
|---|
| | 190 | import mde.file.mergetag.Reader; |
|---|
| | 191 | import mde.file.mergetag.Writer; |
|---|
| 192 | 192 | import mde.setup.paths; |
|---|
| 193 | 193 | |
|---|
| … | … | |
| 221 | 221 | if (allLoaded || (defaultDesign !is null && allDesigns == false)) |
|---|
| 222 | 222 | return; // test if already loaded |
|---|
| 223 | | |
|---|
| 224 | | if (!confDir.exists (fileName)) { |
|---|
| 225 | | logger.error ("Unable to load GUI: no config file!"); |
|---|
| 226 | | return; // not a fatal error (so long as the game can run without a GUI!) |
|---|
| 227 | | } |
|---|
| 228 | 223 | |
|---|
| 229 | 224 | // Set up a reader |
|---|
| … | … | |
| 267 | 262 | } else |
|---|
| 268 | 263 | reader.read([defaultDesign]); |
|---|
| | 264 | } catch (NoFileException) { |
|---|
| | 265 | logger.error ("Unable to load GUI: no config file!"); |
|---|
| | 266 | // just return: not a fatal error (so long as the game can run without a GUI!) |
|---|
| 269 | 267 | } catch (Exception e) { |
|---|
| 270 | 268 | logger.error ("Unable to load GUI: errors parsing config file ("~confDir.getFileName(fileName,PRIORITY.HIGH_LOW)~"):"); |
|---|
| r79 |
r81 |
|
| 19 | 19 | import mde.input.exception; |
|---|
| 20 | 20 | |
|---|
| 21 | | import MT = mde.mergetag.Reader; |
|---|
| | 21 | import MT = mde.file.mergetag.Reader; |
|---|
| 22 | 22 | import mde.setup.paths; |
|---|
| 23 | | import mde.mergetag.deserialize; |
|---|
| 24 | | debug import mde.mergetag.serialize; |
|---|
| | 23 | import mde.file.deserialize; |
|---|
| | 24 | debug import mde.file.serialize; |
|---|
| 25 | 25 | |
|---|
| 26 | 26 | import tango.util.log.Log : Log, Logger; |
|---|
| r80 |
r81 |
|
| 25 | 25 | import mde.exception; |
|---|
| 26 | 26 | |
|---|
| 27 | | import mde.mergetag.Reader; |
|---|
| 28 | | import mde.mergetag.Writer; |
|---|
| 29 | | import mde.mergetag.DataSet; |
|---|
| 30 | | import mde.mergetag.exception; |
|---|
| 31 | | import mde.mergetag.serialize; |
|---|
| | 27 | import mde.file.mergetag.Reader; |
|---|
| | 28 | import mde.file.mergetag.Writer; |
|---|
| | 29 | import mde.file.mergetag.DataSet; |
|---|
| | 30 | import mde.file.serialize; |
|---|
| 32 | 31 | |
|---|
| 33 | 32 | import tango.core.Exception : ArrayBoundsException; |
|---|
| … | … | |
| 135 | 134 | private const fileName = "options"; |
|---|
| 136 | 135 | void load () { |
|---|
| 137 | | // Check it exists (if not it should still be created on exit). |
|---|
| 138 | | // Don't bother checking it's not a folder, because it could still be a block or something. |
|---|
| 139 | | if (!confDir.exists (fileName)) return; |
|---|
| 140 | | |
|---|
| 141 | 136 | try { |
|---|
| 142 | 137 | IReader reader; |
|---|
| … | … | |
| 149 | 144 | }; |
|---|
| 150 | 145 | reader.read; |
|---|
| 151 | | } catch (MTException e) { |
|---|
| 152 | | logger.fatal ("Loading options aborted:"); |
|---|
| 153 | | logger.fatal (e.msg); |
|---|
| 154 | | throw new optionsLoadException ("Mergetag exception (see above message)"); |
|---|
| | 146 | } catch (NoFileException e) { |
|---|
| | 147 | // Just return. Options file will be created on exit. |
|---|
| | 148 | } catch (Exception e) { |
|---|
| | 149 | logger.warn ("Loading options failed: "~e.msg); |
|---|
| | 150 | logger.warn ("If warning persists, delete the offending file."); // FIXME - delete the bad file somehow |
|---|
| 155 | 151 | } |
|---|
| 156 | 152 | } |
|---|
| … | … | |
| 171 | 167 | } catch (NoFileException) { |
|---|
| 172 | 168 | // No user file exists; not an error. |
|---|
| 173 | | } catch (MTException e) { |
|---|
| | 169 | } catch (Exception e) { |
|---|
| 174 | 170 | // Log a message and continue, overwriting the file: |
|---|
| 175 | | logger.error ("Loading options aborted:"); |
|---|
| 176 | | logger.error (e.msg); |
|---|
| | 171 | logger.error ("Loading options aborted: " ~ e.msg); |
|---|
| 177 | 172 | } |
|---|
| 178 | 173 | |
|---|
| … | … | |
| 181 | 176 | writer = confDir.makeMTWriter (fileName, ds); |
|---|
| 182 | 177 | writer.write(); |
|---|
| 183 | | } catch (MTException e) { |
|---|
| 184 | | logger.error ("Saving options aborted! Reason:"); |
|---|
| 185 | | logger.error (e.msg); |
|---|
| | 178 | } catch (Exception e) { |
|---|
| | 179 | logger.error ("Saving options aborted: "~e.msg); |
|---|
| 186 | 180 | } |
|---|
| 187 | 181 | } |
|---|
| r79 |
r81 |
|
| 42 | 42 | import mde.exception; |
|---|
| 43 | 43 | |
|---|
| 44 | | import mde.mergetag.DataSet; |
|---|
| 45 | | import mde.mergetag.Reader; |
|---|
| 46 | | import mde.mergetag.exception; |
|---|
| 47 | | import mde.mergetag.deserialize; |
|---|
| | 44 | import mde.file.mergetag.DataSet; |
|---|
| | 45 | import mde.file.mergetag.Reader; |
|---|
| | 46 | import mde.file.mergetag.exception; |
|---|
| | 47 | import mde.file.deserialize; |
|---|
| 48 | 48 | |
|---|
| 49 | 49 | import tango.util.log.Log : Log, Logger; |
|---|
| r75 |
r81 |
|
| 36 | 36 | import tango.time.Clock; // Clock.now() |
|---|
| 37 | 37 | import tango.util.log.Log : Log, Logger; |
|---|
| | 38 | debug (mdeUnitTest) import mde.file.mergetag.mdeUT; |
|---|
| 38 | 39 | |
|---|
| 39 | 40 | int main(char[][] args) |
|---|
| r78 |
r81 |
|
| 33 | 33 | |
|---|
| 34 | 34 | import mde.exception; |
|---|
| 35 | | import mde.mergetag.Reader; |
|---|
| 36 | | import mde.mergetag.Writer; |
|---|
| 37 | | import mde.mergetag.DataSet; |
|---|
| 38 | | import mde.mergetag.exception; |
|---|
| | 35 | import mde.file.mergetag.Reader; |
|---|
| | 36 | import mde.file.mergetag.Writer; |
|---|
| | 37 | import mde.file.mergetag.DataSet; |
|---|
| | 38 | import mde.file.mergetag.exception; |
|---|
| 39 | 39 | |
|---|
| 40 | 40 | import tango.io.Console; |
|---|
| … | … | |
| 106 | 106 | } |
|---|
| 107 | 107 | |
|---|
| 108 | | /** Check whether the given file exists under any path with either .mtt or .mtb suffix. */ |
|---|
| 109 | | bool exists (char[] file) { |
|---|
| 110 | | for (uint i = 0; i < pathsLen; ++i) { |
|---|
| 111 | | if (FilePath (paths[i]~file~".mtt").exists) return true; |
|---|
| 112 | | if (FilePath (paths[i]~file~".mtb").exists) return true; |
|---|
| 113 | | } |
|---|
| 114 | | return false; |
|---|
| 115 | | } |
|---|
| 116 | | |
|---|
| 117 | 108 | /// Print all paths found. |
|---|
| 118 | 109 | static void printPaths () { |
|---|