/// <summary> /// Load DAT file and output with multiline comments /// </summary> /// <param name="pFile"></param> public static void SaveAsCommentedDat(string pFile) { string outputFileName = Path.GetFileNameWithoutExtension(pFile) + "_commented" + Path.GetExtension(pFile); using (StreamWriter sw = new StreamWriter(outputFileName)) { DATToChunks.Load(pFile); var header = new GameHeader(DATToChunks.GetTokensAsInt(12)); sw.WriteLine("{0} /*Unknown*/", header.Unknown); sw.WriteLine("{0} /*Number of items*/", header.NumItems - 1); sw.WriteLine("{0} /*Number of actions*/", header.NumActions - 1); sw.WriteLine("{0} /*Number of Noun Verbs*/", header.NumNounVerbs - 1); sw.WriteLine("{0} /*Number of Rooms*/", header.NumRooms - 1); sw.WriteLine("{0} /*Maximum carry*/", header.MaxCarry); sw.WriteLine("{0} /*Start room*/", header.StartRoom); sw.WriteLine("{0} /*Total treasures*/", header.TotalTreasures); sw.WriteLine("{0} /*Word length*/", header.WordLength); sw.WriteLine("{0} /*Light duration*/", header.LightDuration); sw.WriteLine("{0} /*Number of messages*/", header.NumMessages - 1); sw.WriteLine("{0} /*Treasure room*/", header.TreasureRoom); //produces an array of arrays var labels = new string[] { "/*NounVerb*/", "/*Condition1*/", "/*Condition2*/", "/*Condition3*/", "/*Condition4*/", "/*Condition5*/", "/*Actions 1 and 2*/", "/*Actions 3 and 4*/" }; int ctr = 1; foreach (var action in Enumerable.Range(0, header.NumActions).Select(n => DATToChunks.getTokens(8).ToArray()).ToArray()) { sw.WriteLine("{0}\t\t\t/*Action index {1} - NounVerb*/", action.First(), ctr++); for (int a = 1; a < 8; a++) { sw.WriteLine("{0}\t\t\t{1}", action[a], labels[a]); } } int vb = 0, nn = 0, j = 0; foreach (var w in DATToChunks.getTokens(header.NumNounVerbs * 2)) { if (j == 0) //verb { if (!w.StartsWith("*")) { vb++; } sw.WriteLine("\"{0}\"\t\t{1}" , w , !w.StartsWith("*") ? string.Format("/*Verb index {0}*/", vb) : string.Format("/*synonym of verb index {0}*/", vb) ); } else if (j == 1) //noun { if (!w.StartsWith("*")) { nn++; } sw.WriteLine("\"{0}\"\t\t{1}" , w , !w.StartsWith("*") ? string.Format("/*Noun index {0}*/", nn) : string.Format("/*synonym of noun index {0}*/", nn) ); } j++; if (j > 1) { j = 0; } } ctr = 0; string[] dire = { "/*North ", "/*South ", "/*East ", "/*West ", "/*Up ", "/*Down " }; foreach (var room in Enumerable.Range(0, header.NumRooms).Select(n => DATToChunks.getTokens(7).ToArray())) { for (int q = 0; q < 6; q++) { sw.WriteLine("{0}\t{1}{2}*/", room[q], dire[q], room[q] == "0" ? " - not used" : " - links to room " + room[q]); } sw.WriteLine("\"{0}\" /*Room {1} Description*/", room.Last(), ctr++); } ctr = 0; foreach (var message in DATToChunks.getTokens(header.NumMessages)) { sw.WriteLine("\"{0}\" /*Message {1}*/", message, ctr++); } ctr = 0; foreach (var item in Enumerable.Range(0, header.NumItems).Select(n => DATToChunks.getTokens(2).ToArray())) { sw.WriteLine("\"{0}\" /*Item {1} Description*/ {2} /*Location*/", item.First(), ctr++, item.Last()); } ctr = 0; foreach (var actionMessage in DATToChunks.getTokens(header.NumActions)) { sw.WriteLine("\"{0}\" /*Action {1} description*/", actionMessage, ctr++); } var footer = new GameFooter(DATToChunks.GetTokensAsInt(3)); sw.WriteLine("{0} /*Version number*/", footer.Version); sw.WriteLine("{0} /*Adventure number*/", footer.AdventureNumber); sw.WriteLine("{0} /*Unknown*/", footer.Unknown); } }
/// <summary> /// Load the adventure game from the provided dat file /// </summary> /// <param name="pFile"></param> /// <returns>Game data class</returns> public static GameData Load(string pFile) { string[] directionsLong = { "North", "South", "East", "West", "Up", "Down" }; int VERB_TAKE = 10; int VERB_DROP = 18; GameData gd = new GameData(); DATToChunks.Load(pFile); int[] header = DATToChunks.GetTokensAsInt(12); gd.Header = new GameHeader(header); gd.Verbs = new string[gd.Header.NumNounVerbs]; gd.Nouns = new string[gd.Header.NumNounVerbs]; gd.Rooms = new Room[gd.Header.NumRooms]; gd.Messages = new string[gd.Header.NumMessages]; gd.Items = new Item[gd.Header.NumItems]; gd.GameName = pFile; gd.CurrentRoom = gd.Header.StartRoom; gd.LampLife = gd.Header.LightDuration; int ctr = 0; List <Action> Actions = new List <Action>(); #region Actions for (ctr = 0; ctr < gd.Header.NumActions; ctr++) { Actions.Add(new Action(DATToChunks.GetTokensAsInt(8))); } #endregion #region Words /* * An interleaved list of verb/noun that begins * with the entries "AUT" and "ANY" that we skip * * An entry beginning with a star is a synonym of the first * preceeding word that doesn't begin with a star */ int v = 0; int n = 0; string[] word = DATToChunks.getTokens(gd.Header.NumNounVerbs * 2); for (ctr = 0 /*SKIP*/; ctr < word.Count(); ctr++) { if (ctr % 2 == 0) { gd.Verbs[v] = word[ctr]; if (gd.Verbs[v].StartsWith("*") & gd.Verbs[v].Length > (gd.Header.WordLength + 1)) { gd.Verbs[v] = gd.Verbs[v].Substring(0, gd.Header.WordLength + 1); } else if (!gd.Verbs[v].StartsWith("*") & gd.Verbs[v].StartsWith("*") && word[ctr].Length > gd.Header.WordLength) { gd.Verbs[v] = gd.Verbs[v].Substring(0, gd.Header.WordLength); } v++; } else { gd.Nouns[n] = word[ctr]; if (gd.Nouns[n].StartsWith("*") & gd.Nouns[n].Length > (gd.Header.WordLength + 1)) { gd.Nouns[n] = gd.Nouns[n].Substring(0, gd.Header.WordLength + 1); } else if (!gd.Nouns[n].StartsWith("*") & word[ctr].Length > gd.Header.WordLength) { gd.Nouns[n] = gd.Nouns[n].Substring(0, gd.Header.WordLength); } n++; } } #endregion #region Rooms for (ctr = 0; ctr < gd.Rooms.Length; ctr++) { gd.Rooms[ctr] = new Room(DATToChunks.GetTokensAsInt(6), DATToChunks.getTokens(1).First()); gd.Rooms[ctr].Description += Environment.NewLine + Environment.NewLine + _ObviousExits; if (gd.Rooms[ctr].Exits.Count(e => e > 0) > 0) { gd.Rooms[ctr].Description += gd.Rooms[ctr].Exits .Select((val, ind) => new { val, ind }) .Where(i => i.val > 0) .Select(i => directionsLong[i.ind]) .Aggregate((current, next) => current + ", " + next); } else { gd.Rooms[ctr].Description += _None; //none } } #endregion #region Build Game Messages gd.Messages = DATToChunks.getTokens(gd.Messages.Length); #endregion #region Items for (ctr = 0; ctr < gd.Items.Length; ctr++) { gd.Items[ctr] = new Item(DATToChunks.getTokens(1).First(), DATToChunks.GetTokensAsInt(1).First()); } #endregion #region Add any comments to actions for (ctr = 0; ctr < gd.Header.NumActions; ctr++) { Actions[ctr].Comment = DATToChunks.getTokens(1).First(); } #endregion #region Generate get/drop actions for items that can be carried for (int itemCtr = 0; itemCtr < gd.Items.Count(); itemCtr++) { if (gd.Items[itemCtr].Word != null) { Actions.Add ( new Action() { Comment = "Autotake for " + gd.Items[itemCtr].Description , Verb = VERB_TAKE , Noun = gd.Nouns.TakeWhile(nn => nn != gd.Items[itemCtr].Word).Count() , Conditions = new int[][] { new int[] { 2, itemCtr } } , Effects = new int[][] { new int[] { 52, itemCtr, 0 } } } ); Actions.Add ( new Action() { Comment = "Autodrop for " + gd.Items[itemCtr].Description , Verb = VERB_DROP , Noun = gd.Nouns.TakeWhile(nn => nn != gd.Items[itemCtr].Word).Count() , Conditions = new int[][] { new int[] { 1, itemCtr } } , Effects = new int[][] { new int[] { 53, itemCtr, 0 } } } ); } } #endregion // Child action processing // All actions that follow an action with a component 73 are noun == 0 && verb == 0 // and are the children of that action. This method moves them into a array of // their parent // e.g 157 in Adv01.dat List <Action> childs = null; for (int i = Actions.Count() - 1; i >= 0; i--) { if (Actions[i].Effects.Count(act => act[0] == 73) > 0) { int j = i + 1; childs = new List <Action>(); while (Actions[j].Verb == 0 && Actions[j].Noun == 0) { childs.Add(Actions[j]); j++; } Actions[i].Children = childs.ToArray(); Actions.RemoveRange(i + 1, childs.Count()); } } /* * Claymorgue castle fix * * Action 148 has no conditions and a noun and a verb of 0, * and follows a user tiggered action. This particular set of * conditions only occurs in Claymorgue and none of the other 13 * adventures. I think that when this set of conditions occurs, * the latter action should be treated as a child of the former, * and would require some special treatment when the DAT file is loaded. */ Action ac; for (var i = 1; i < Actions.Count(); i++) { ac = Actions[i]; if ( Actions[i].Conditions.Length == 0 && Actions[i].Effects.Length > 0 && Actions[i].Verb == 0 && Actions[i].Noun == 0 && Actions[i - 1].Verb > 0 ) { Actions[i - 1].Children = new Action[] { Actions[i] }; Actions[i] = null; } } //If words aren't present for SAV GAM add them //for adv06, 07, 09, 10, 11, 12, 13, 14a #region Add save game if (!gd.Nouns.Contains("GAM") && !gd.Verbs.Contains("SAV")) { Array.Resize(ref gd.Nouns, gd.Nouns.Length + 1); gd.Nouns[gd.Nouns.Length - 1] = "GAM"; Array.Resize(ref gd.Verbs, gd.Verbs.Length + 1); gd.Verbs[gd.Verbs.Length - 1] = "SAV"; Actions.Add( new Action( new int[] { (gd.Verbs.Length - 1) * 150 //verb + (gd.Nouns.Length - 1) //noun , 0 //condition 1 , 0 //condition 2 , 0 //condition 3 , 0 //condition 4 , 0 //condition 5 , 71 * 150 , 0 } ) ); } #endregion gd.Actions = Actions.Where(a => a != null).ToArray(); gd.Footer = new GameFooter(DATToChunks.GetTokensAsInt(3)); return(gd); }