public Word[] GetContentsOfNthEntry(int n, GameMemory zorkFile) { int entrySize = GetEntrySizeInBytes(n, zorkFile); // I have been assuming that the above call will return 4, since that // is how many bytes are in a word entry. But it think this size // allows there to be arbitrary entries after all the words, and they might // be of up to this size. // Two questions: // 1. How do I know that we are in the size-regulated, has words-in-order // part of the dictionary? // 2. How do I know the size of the entries that are after the words, // in the arbitrary-sized part of the table? int entrySizeInWords = entrySize / 2; Word[] retval = new Word[entrySizeInWords]; WordAddress ptrStartOfEntry = GetOffSetOfNthEntry(n, zorkFile); for(int i = 0; i < entrySizeInWords; i++) { WordAddress here = new WordAddress(ptrStartOfEntry.Value + (i * 2)); retval[i] = zorkFile.ReadWord(here); } return retval; }
public int CountOfEntries(GameMemory zorkFile) { ByteAddress startOfDictionary = ptrToDictionary(zorkFile); int countSepCharacters = CountOfSeparatorCharacters(zorkFile); WordAddress ptrToEntryCount = new WordAddress(startOfDictionary.Value + 1 + countSepCharacters + 2); return zorkFile.ReadWord(ptrToEntryCount).Value; }
public static WordAddress AddressOfAbbreviationByNumber(AbbreviationNumber number, GameMemory memory) { AbbreviationTableBase basePtr = new AbbreviationTableBase(memory); WordAddress addressOfPtrToAbbrTable = new WordAddress(24); WordAddress ptrToAbbrevTable = new WordAddress(memory.ReadWord(addressOfPtrToAbbrTable).Value); WordAddress ptrToChosenAbbrv = ptrToAbbrevTable + (number.Value); WordAddress decompressedAbbrvPtr = new WordAddress(memory.ReadWord(ptrToChosenAbbrv).Value * 2); return decompressedAbbrvPtr; }
public int GetEntrySizeInBytes(int n, GameMemory zorkFile) { if (IsNthEntryADictionaryWord(n)) { return 4; } else { return ReadCustomSizeFromFile(zorkFile); } }
public static List<Zchar> ReadStringFromAddress(WordAddress address, GameMemory memory) { throw new System.NotImplementedException(); List<Zchar> retval = new List<Zchar>(); Word word = memory.ReadWord(address); while(!word.IsTerminal()) { //Zchar char1 = new Zchar(new AbbreviationNumber(Bits.FetchBits(BitNumber.Bit14, BitSize.Size5, word))); //Zchar char2 = new Zchar(new AbbreviationNumber(Bits.FetchBits(BitNumber.Bit9, BitSize.Size5, word))); //Zchar char3 = new Zchar(new AbbreviationNumber(Bits.FetchBits(BitNumber.Bit4, BitSize.Size5, word))); //retval.Add(char1); //retval.Add(char2); //retval.Add(char3); address = address + 1; word = memory.ReadWord(address); } return retval; }
public int CountOfSeparatorCharacters(GameMemory zorkFile) { return zorkFile.ReadByte(ptrToDictionary(zorkFile)); }
private int ReadCustomSizeFromFile(GameMemory zorkFile) { ByteAddress startOfDictionary = ptrToDictionary(zorkFile); int numberOfSeps = CountOfSeparatorCharacters(zorkFile); ByteAddress ptrSizeEntry = new ByteAddress(startOfDictionary.Value + numberOfSeps + 1); int sizeOfEntry = zorkFile.ReadByte(ptrSizeEntry); return sizeOfEntry; }
public ISet<char> Separators(GameMemory zorkFile) { //TODO read separators from dictionary. int countSeparators = CountOfSeparatorCharacters(zorkFile); ByteAddress startOfDictionary = ptrToDictionary(zorkFile); return new HashSet<char>(new char[] { ' ' }); }
public DictionaryEntry ReadNthEntry(int n, GameMemory zorkFile) { WordAddress offset = GetOffSetOfNthEntry(n, zorkFile); Word[] entryContents = GetContentsOfNthEntry(n, zorkFile); List <Zchar> entryZchars = new List<Zchar>(); foreach(Word contents in entryContents) { var zchars = Zchar.ReadWordAsZchars(contents); entryZchars.AddRange(zchars); } var chars = Zchar.DecodeFromZString(entryZchars, zorkFile); return new DictionaryEntry( print: new string(chars.ToArray()), ix: n, offset: offset); }
private static void TestReadingAndWritingGameState() { string storyState = "Listen to a story 'bout a guy named Al"; string interpreterState = "Who lived in the sewer with his hamster pal"; int expectedImmutableSize = Encoding.UTF8.GetByteCount(storyState); byte[] immutablePart = Encoding.UTF8.GetBytes(storyState); int expectedDynamicSize = Encoding.UTF8.GetByteCount(interpreterState); byte[] dynamicPart = Encoding.UTF8.GetBytes(interpreterState); ImmutableByteWrapper changingPart = new ImmutableByteWrapper(dynamicPart); GameMemory gameState = new GameMemory(immutablePart, changingPart); WordAddress zeroPointer = new WordAddress(0); List<WordAddress> firstFivePointers = Enumerable.Range(0, 5) .Select(el => zeroPointer + el) .ToList(); IEnumerable<ByteAddress> firstTenByteAddress = Enumerable.Range(0, 10) .Select(el => new ByteAddress(el)); byte[] firstTenBytes = firstTenByteAddress .Select(p => gameState.ReadByte(p)) .ToArray(); byte[] firstFiveWords = firstFivePointers .Select(p => gameState.ReadWord(p)) .SelectMany(word => new byte[] { (byte)(word.Value & 0xFF), (byte)((word.Value >> 8) & 0xFF) }) .ToArray(); string firstFive = new string(Encoding.UTF8.GetChars(firstFiveWords)); byte[] firstFiveTransposed = firstFivePointers .Select(p => gameState.ReadWord(p)) .SelectMany(word => new byte[] { (byte)((word.Value >> 8) & 0xFF), (byte)(word.Value & 0xFF) }) .ToArray(); string firstFiveTransposedRead = new string(Encoding.UTF8.GetChars(firstFiveTransposed)); string whatIRead = new String(Encoding.UTF8.GetChars(firstTenBytes)); //test writing to game state: //replace "Who lived " with // "zzCheese2 " byte[] toWrite = Encoding.UTF8.GetBytes("zzCheese2 "); System.Diagnostics.Debug.Assert(toWrite.Length == firstTenBytes.Length); Enumerable.Range(0, 10) .Select(el => new { address = new ByteAddress(el), val = toWrite[el] }) .ToList() .ForEach(item => gameState.WriteByte(item.address, item.val)); byte[] firstTenBytesAgain = firstTenByteAddress .Select(p => gameState.ReadByte(p)) .ToArray(); string mutated = new string(Encoding.UTF8.GetChars(firstTenBytesAgain)); //test writing Words instead of bytes to game state: byte[] toWriteWordWise = Encoding.UTF8.GetBytes("aaZZeess33"); WordAddress zero = new WordAddress(0); for(int i = 0; i < toWriteWordWise.Length - 1; i += 2) { int val = (toWriteWordWise[i] * 256 + toWriteWordWise[i + 1]); WordAddress address = zero + i / 2; gameState.WriteWord(address, new Word(val)); } byte[] firstTenBytesAgain2 = firstTenByteAddress .Select(p => gameState.ReadByte(p)) .ToArray(); string mutated2 = new string(Encoding.UTF8.GetChars(firstTenBytesAgain2)); Console.WriteLine($"{whatIRead} became {mutated} and then {mutated2}"); }
public AbbreviationTableBase(GameMemory memory) { WordAddress addrOfTableBasePtr = new WordAddress(24); WordAddress addressOfTableBase = new WordAddress(memory.ReadWord(addrOfTableBasePtr).Value); this.value = addressOfTableBase.Value; }
public static IEnumerable<char> YieldAbbreviationFromAbbreviationNumber(AbbreviationNumber number, GameMemory memory) { WordAddress addressOfPtrToAbbrTable = new WordAddress(24); WordAddress ptrToAbbrevTable = new WordAddress(memory.ReadWord(addressOfPtrToAbbrTable).Value); WordAddress address = AbbreviationTableBase.AddressOfAbbreviationByNumber(number, memory); Word word = memory.ReadWord(address); while (true) { char[] firstThree = Zchar.PrintWordAsZchars(word).ToCharArray(); foreach (char ch in firstThree) { yield return ch; } if (word.IsTerminal()) { yield break; } address = address + 1; word = memory.ReadWord(address); } }
public static IEnumerable<Zchar> ReadWordsTillBreak(WordAddress address, GameMemory memory, ISet<char> breakers = null) { while (true) { Word word = memory.ReadWord(address); Zchar low = new Zchar(Bits.FetchBits(BitNumber.Bit14, BitSize.Size5, word)); Zchar mid = new Zchar(Bits.FetchBits(BitNumber.Bit9, BitSize.Size5, word)); Zchar high = new Zchar(Bits.FetchBits(BitNumber.Bit4, BitSize.Size5, word)); yield return low; yield return mid; yield return high; if (word.IsTerminal(breakers)) { yield break; } address = address + 1; } }
public static IEnumerable<Zchar> ReadAbbrevTillBreak(AbbreviationNumber num, GameMemory memory) { WordAddress addressOfPtrToAbbrTable = new WordAddress(24); WordAddress ptrToAbbrevTable = new WordAddress(memory.ReadWord(addressOfPtrToAbbrTable).Value); WordAddress ptrChosenAbbrev = AbbreviationTableBase.AddressOfAbbreviationByNumber(num, memory); return ReadWordsTillBreak(ptrChosenAbbrev, memory); }
public static IEnumerable<char> DecodeFromZString(IEnumerable<Zchar> chars, GameMemory memory, bool permitRecurse = true) { //THIS IS the least functional-style code in here. it's so stateful. (well, it's a state machine) //another way would be to write a function that //takes a state and a zchar and decides what to do. const int ZCHAR_PER_ABBREV = 6; const int lowEntry = 6; Zchar? previous = null; state current = state.lower; int ixCounter = 0; //TEMP DELME int fivesToSkip = 0; foreach (Zchar ch in chars) { ixCounter++; bool isAbbrev = current == state.abbr || current == state.abbr32 || current == state.abbr64; bool isAscii = current == state.ascii || current == state.ascii2; if ((ch.Value >= lowEntry || ch.Value == 0)|| (isAbbrev) || (isAscii)) { switch (current) { case state.abbr: case state.abbr32: case state.abbr64: AbbreviationNumber num = GetFromZcharAndState(current, ch); IEnumerable<Zchar> abbrevs = ReadAbbrevTillBreak(num, memory).ToList(); List<char> inner = DecodeFromZString(abbrevs, memory, false).ToList(); fivesToSkip = ZCHAR_PER_ABBREV - inner.Count - 1; foreach (char innerChar in inner) { yield return innerChar; } if(fivesToSkip > 0) { current = state.lower; fivesToSkip--; continue; } current = state.lower; break; case state.lower: //Console.Write(Lower[ch.Value]); yield return Lower[ch.Value]; break; case state.upper: current = state.lower; //Console.Write(Upper[ch.Value]); yield return Upper[ch.Value]; break; case state.symbol: if (ch.Value == 6) { current = state.ascii; continue; } else { current = state.lower; //Console.Write(Symbol[ch.Value]); yield return Symbol[ch.Value]; } break; case state.ascii: previous = ch; current = state.ascii2; continue; case state.ascii2: //throw new System.NotImplementedException(); //TODO the previous char and the current char // are combined to make an ascii char // not in the tables. break; } } else { switch (ch.Value) { case 1: current = state.abbr; if (!permitRecurse) { //yield break; } break; case 2: current = state.abbr32; if (!permitRecurse) { //yield break; } break; case 3: current = state.abbr64; if (!permitRecurse) { //yield break; } break; case 4: current = state.upper; break; case 5: current = state.symbol; break; case 0: break; default: throw new ArgumentException("Invalid state."); } continue; } } }
private static void ReadDictionaryTillBreak(GameMemory zorkFile) { DictionaryHelper dictionary = new DictionaryHelper(); WordAddress startOfEntries = dictionary.GetOffSetOfNthEntry(0, zorkFile); WordAddress ptrFirstEntry = new WordAddress(startOfEntries.Value); var test = Zchar.ReadWordsTillBreak(ptrFirstEntry, zorkFile); char[] result = Zchar.DecodeFromZString(test, zorkFile, permitRecurse: false) .ToArray(); Console.Write(new string(result)); }
/* Structure of Dictionary Header: [0] => n := byte containing number of word separator characters [1:n] => word separator characters, I believe in ASCII [n+1] => size := size of each entry [n+2:n+3] => as word, numOfEntries := number of entries [n+4:n+(numOfEntires * 2 * size)] => the actual entries NOTE that we care about both "entry number" (index into this structure) AND about the entry address (offset into file of a given entry) */ public WordAddress GetOffSetOfNthEntry(int n, GameMemory zorkFile) { ByteAddress startOfDictionary = ptrToDictionary(zorkFile); int dictionaryBaseOffset = startOfDictionary.Value; int sizeOfSepCountEntry = 1; int numberOfSeps = CountOfSeparatorCharacters(zorkFile); int sizeOfSizeEntry = 1; int sizeOfEntryCountEntry = 2; int offsetFirstEntry = dictionaryBaseOffset + sizeOfSepCountEntry + numberOfSeps + sizeOfSizeEntry + sizeOfEntryCountEntry; ByteAddress ptrSizeEntry = new ByteAddress(startOfDictionary.Value + numberOfSeps + sizeOfSepCountEntry); int sizeOfEntry = zorkFile.ReadByte(ptrSizeEntry); return new WordAddress(offsetFirstEntry + (n * sizeOfEntry)); }
public ByteAddress ptrToDictionary(GameMemory zorkFile) { return new ByteAddress(zorkFile.ReadWord(ptrDictionaryPtr).Value); }