EXIF-TSO is a proposal for storing details of tactical information for The Settlers Online adventures within the graphical files used for laying out tactics.
Tactical maps are usually provided in the form of JPEG images.
The JPEG file format allows to store EXIF information, which is ususally used by cameras to store information about used lens and more, but has custom fields.
JSON should be used to represent the data because it is a lightweight both human and machine readable format.
The following is my proposal for a first version of JSON data to represent the tactical information within a file, stored in the Comments EXIF field. Supported fields should be:
- TSOMetaDataVersion
- number (mandatory): currently 1.
- TMUID
- string (mandatory): GUID of map, a unique identifier, see example, but do not reuse the example one!
- ReplacesTMUID
- string (optional): GUID of map this map is intended to replace.
- AdventureID
- number (mandatory): one of these:
- 55: Opening Match / (26+)
- 56: Opening Match / (36+)
- 2: Old Friends / Alte Bekannte (32+)
- 26: Old Ruins / Alte Ruinen (26+)
- 37: / Arktische Explosion (0+)
- 3: Stealing from the Rich / Beutelschneider (26+)
- 32: The Betrayed Little Tailor / Das betrogene Schneiderlein (42+)
- 4: The End Of The World / Das Ende der Welt (26+)
- 34: The Heroic Little Tailor / Das heldenhafte Schneiderlein (42+)
- 33: The Clever Little Tailor / Das kluge Schneiderlein (42+)
- 5: Bandit Nest / Das Räubernest (36+)
- 31: The Valiant Little Tailor / Das tapfere Schneiderlein (42+)
- 75: / Der gestohlene Schlitten (0+)
- 25: The Shaman / Der Schamane (26+)
- 29: The Sleeping Vulcano / Der schlafende Vulkan (26+)
- 6: / Der verschollene Schädel (26+)
- 77: The Siege / Die Belagerung (26+)
- 60: Match Three / Die dritte Begegnung (36+)
- 59: Match Three / Die dritte Begegnung (26+)
- 8: The Dark Brotherhood / Die dunkle Bruderschaft (42+)
- 49: Easter Eggs Hunt Begins / Die Eierjagd beginnt (17+)
- 52: Continued Easter Eggs Hunt / Die Eierjagd geht weiter (17+)
- 65: Final Match / Die finale Begegnung (26+)
- 66: Final Match / Die finale Begegnung (36+)
- 78: The Buccaneer Roundup / Die Freibeuter-Razzia (26+)
- 63: Match Five / Die fünfte Begegnung (26+)
- 64: Match Five / Die fünfte Begegnung (36+)
- 38: / Die Gefängnisinsel (0+)
- 14: The Island of the Pirates / Die Insel der Freibeuter (26+)
- 48: Cousins Island / Die Insel des Cousins (17+)
- 36: The Invasion of the Nords / Die Invasion der Nordmänner (36+)
- 45: Jester Island / Die Karte des Narren (1+)
- 71: / Die letzte Insel (0+)
- 9: / Die Nordmänner (36+)
- 10: The Dark Priests / Die schwarzen Priester (42+)
- 11: The Black Knights / Die schwarzen Ritter (42+)
- 35: The Sons Of The Little Tailor / Die Söhne des Schneiderleins (42+)
- 70: / Die Suche dauert (0+)
- 61: Match Four / Die vierte Begegnung (26+)
- 62: Match Four / Die vierte Begegnung (36+)
- 12: / Die wilde Waltraut (36+)
- 58: Match Two / Die zweite Begegnung (36+)
- 57: Match Two / Die zweite Begegnung (26+)
- 50: Hunting For Easter Eggs / Eierjagd (17+)
- 51: New Easter Eggs Hunt / Eine neue Eierjagd (17+)
- 43: New Experience / Eine neue Erfahrung (1+)
- 13: Secluded Experiments / Einsame Experimente (42+)
- 54: Picasso / Fast ein Picasso (17+)
- 84: / Garrun der Fallensteller (0+)
- 79: Tomb Raiders / Grabräuber (36+)
- 73: / Heimatinsel (0+)
- 46: Hero needed immediately / Held gesucht (1+)
- 81: Raiding The Raiders / Jagd auf die Jäger (36+)
- 15: Bounty Hunter / Kopfgeldjäger (26+)
- 74: / Mehr einsame Experimente (0+)
- 16: Mother Love / Mutterliebe (36+)
- 42: Nothing Special / Nichts Besonderes (1+)
- 72: / Ostereier in den Bergen (0+)
- 28: Pirate Life / Piratenleben (26+)
- 17: Roaring Bull / Rasender Bulle (42+)
- 18: Outlaws / Räuberbande (36+)
- 44: Retro Style / Retro Stil (1+)
- 41: Save The Christmas Feat / Rettet das Weihnachtsfest (36+)
- 40: Save The Christmas Feat / Rettet das Weihnachtsfest (26+)
- 76: Save The Christmas Feat / Rettet das Weihnachtsfest (0+)
- 83: / Rückkehr ins Räubernest (26+)
- 47: Sabotage / Sabotage (17+)
- 1: Horseback / Sattelfest (26+)
- 24: Gunpowder / Schießpulver (36+)
- 30: Sleepy Reef / Schlummerndes Riff (26+)
- 67: / Schnitzeljagd - Erste Stufe (0+)
- 69: / Schnitzeljagd - Letzte Stufe (0+)
- 68: / Schnitzeljagd - Zweite Stufe (0+)
- 80: Lakeside Treasure / Seeufer-Schatz (36+)
- 20: Sons of the Veld / Söhne der Steppe (36+)
- 39: / Steinmetz, Fischer, Jäger, Späher (0+)
- 19: Witch of the Swamp / Sumpfhexe (26+)
- 21: Tropical Sun / Tropensonne (26+)
- 7: Suprise Attack / Überraschungsangriff (36+)
- 53: Lost Easter Eggs / Verlorene Ostereier (17+)
- 22: Traitors / Verräter (26+)
- 23: Victor the Vicious / Viktor der Verschlagene (32+)
- 82: Whirlwind / Wirbelwind (46+)
- AuthorName
- string (mandatory)
- AuthorGuild
- string (optional)
- AuthorWorld
- string (optional): one of these:
- de-01: Grünland
- de-02: Windfeuertal
- de-03: Bernsteingarten
- de-04: Steppenwald
- de-05: Goldenau
- de-06: Mittsommerstadt
- de-07: Apfehain
- de-08: Funkelberg
- de-09: Schneefeuer
- de-10: Tuxingen
- de-11: Morgentau
- de-12: Andosia
- de-13: Wildblumental
- de-14: Glitzerstadt
- AuthorContact
- string (optional): url where author can be contacted (e.g. official forum profile address)
- CreationDate
- number (mandatory): Windows integer file time (NOT Linux file time) formatted date.
- Strategies
- array (optional): an array of objects.
- Strategies[].Difficulty
- number (optional): describes, on a scale from 0 to 10, with 10 being most difficult, how difficult the author rates this strategy.
- Strategies[].RequiredGenerals270
- number (optional): Number of required "Generalmajor" (carrying 270 troups) generals; if not specified, 0 is assumed.
- Strategies[].RequiredGenerals250
- number (optional): Number of required "Veteran" (carrying 250 troups) generals; if not specified, 0 is assumed.
- Strategies[].RequiredGenerals220
- number (optional): Number of required "???" (carrying 220 troups) generals; if not specified, 0 is assumed.
- Strategies[].RequiredGeneralsMdK
- number (optional): Number of required "Meister der Kampfkunst" (carrying 220 troups) generals; if not specified, 0 is assumed.
- Strategies[].RequiredGenerals200
- number (optional): Number of required "Tavernengeneral" (carrying 200 troups) generals; if not specified, 0 is assumed.
- Strategies[].RequiredGenerals200Fast
- number (optional): Number of required "Schneller General" (carrying 200 troups) generals; if not specified, 0 is assumed.
- Strategies[].Recruits
- Strategies[].Bows
- Strategies[].Militia
- Strategies[].Cavalry
- Strategies[].Longbows
- Strategies[].Soldiers
- Strategies[].Crossbows
- Strategies[].EliteSoldiers
- Strategies[].Canoniers
- array (mandatory): specifies required units and expected losses.
- Strategies[].(unit).Required
- number (mandatory): Number of units of given type that are needed.
- Strategies[].(unit).MinLosses
- number (optional): Minimum number of units lost following this strategy.
- Strategies[].(unit).AvgLosses
- number (optional): Average number of units lost following this strategy.
- Strategies[].(unit).MaxLosses
- number (optional): Maximum Number of units lost following this strategy.
- Strategies[].Blocks
- bool (mandatory): whether blocks are needed with this strategy.
- Strategies[].Locks
- bool (mandatory): whether locks are needed with this strategy.
{
"TSOMetaDataVersion":1,
"TMUID": "{413B2D52-2228-4961-901E-80AA4B9F096F}",
"AdventureID":84,
"AuthorName":"Schmat",
"AuthorGuild":"",
"AuthorWorld":"de-12",
"AuthorContact":"http://forum.diesiedleronline.de/members/1124269-Schmat",
"CreationDate":1147356755,
"Strategies":[
{
"Difficulty":8,
"RequiredGenerals270":0,
"RequiredGenerals250":1,
"RequiredGenerals220":0,
"RequiredGenerals200":4,
"RequiredGenerals200Fast":0,
"Recruits":{
"Required":452,
"MinLosses":0,
"AvgLosses":0,
"MaxLosses":452
},
"Bows":{
"Required":0,
"MinLosses":0,
"AvgLosses":0,
"MaxLosses":0
},
"Militia":{
"Required":0,
"MinLosses":0,
"AvgLosses":0,
"MaxLosses":0
},
"Cavalry":{
"Required":0,
"MinLosses":0,
"AvgLosses":0,
"MaxLosses":0
},
"Longbows":{
"Required":0,
"MinLosses":0,
"AvgLosses":0,
"MaxLosses":0
},
"Soldiers":{
"Required":0,
"MinLosses":0,
"AvgLosses":0,
"MaxLosses":0
},
"Crossbows":{
"Required":0,
"MinLosses":0,
"AvgLosses":0,
"MaxLosses":0
},
"EliteSoldiers":{
"Required":0,
"MinLosses":0,
"AvgLosses":0,
"MaxLosses":0
},
"Canoniers":{
"Required":0,
"MinLosses":0,
"AvgLosses":0,
"MaxLosses":0
},
"Blocks":false,
"Locks":false
}
]
}
I do provide a sample implementation in form of a Windows executable TSOEXIFEditor.exe that opens images, offers forms for defining the meta data, and saving again.
For server-sided processing of meta data in tactical maps, I provide EXIFTSOReader.php, or you could simply use this snippet:
function read_exif_tso_information($AFilename)
{
if (!file_exists($AFilename))
{
throw new Exception('Missing file!');
}
$aEXIF = exif_read_data($AFilename, 'ANY_TAG', true);
$sComments = $aEXIF['IFD0']['Comments'];
$sComments = trim(mb_convert_encoding($sComments, 'UTF-8', 'UTF-16LE'));
$jsonMetaData = json_decode($sComments, true);
return $jsonMetaData;
}