/// <summary> /// Assume we're running a command and have save game data. /// </summary> /// <param name="gameFile"></param> /// <param name="saveFile"></param> /// <param name="command"></param> public EngineWrapper(byte[] gameFile, byte[] saveFile, string command) { if (gameFile == null) throw new Exception("Missing game data."); if (saveFile == null) throw new Exception("Missing required save file."); if (command == "") throw new Exception("Missing command."); MemoryStream gameData = new MemoryStream(gameFile); saveFileData = new MemoryStream(saveFile); vm = new Engine(gameData, saveFileData); wrapperState = VMWrapperState.LoadGame; if (command == "restore.current.game") { requestType = VMRequestType.NoCommand; } else { requestType = VMRequestType.ExecuteCommand; } // save the user's command for later saveCommand = command; Run(); }
public async Task LoadGame(Page page) { this.page = page; StorageFile gameFile = await Package.Current.InstalledLocation.GetFileAsync(@"Game\shadow-w8.ulx"); byte[] gameFileData = await ReadFromFile(gameFile); Stream gameStream = new MemoryStream(gameFileData); vm = new Engine(gameStream); Run(); }
/// <summary> /// Load the game and return data. /// </summary> /// <param name="gameFile"></param> public EngineWrapper(byte[] gameFile) { if (gameFile == null) throw new Exception("Missing game file."); MemoryStream gameData = new MemoryStream(gameFile); vm = new Engine(gameData); requestType = VMRequestType.StartGame; wrapperState = VMWrapperState.LoadGame; Run(); }
/// <summary> /// Intercepts a routine call if its address has previously been registered. /// </summary> /// <param name="e">The <see cref="Engine"/> attempting to call the routine.</param> /// <param name="address">The address of the routine.</param> /// <param name="args">The routine's arguments.</param> /// <param name="result">The routine's return value.</param> /// <returns><see langword="true"/> if the call was intercepted.</returns> /// <exception cref="IndexOutOfRangeException"> /// <paramref name="address"/> matches a registered veneer routine, but /// <paramref name="args"/> is too short for that routine. /// </exception> public bool InterceptCall(Engine e, uint address, uint[] args, out uint result) { if (address != 0) { if (address == zregion_fn) { result = Z__Region(e, args[0]); return true; } if (address == cp_tab_fn) { result = CP__Tab(e, args[0], args[1]); return true; } if (address == oc_cl_fn) { result = OC__Cl(e, args[0], args[1]); return true; } if (address == ra_pr_fn) { result = RA__Pr(e, args[0], args[1]); return true; } if (address == rt_chldw_fn) { result = RT__ChLDW(e, args[0], args[1]); return true; } if (address == unsigned_compare_fn) { result = (uint)args[0].CompareTo(args[1]); return true; } if (address == rl_pr_fn) { result = RL__Pr(e, args[0], args[1]); return true; } if (address == rv_pr_fn) { result = RV__Pr(e, args[0], args[1]); return true; } if (address == op_pr_fn) { result = OP__Pr(e, args[0], args[1]); return true; } if (address == rt_chstw_fn) { result = RT__ChSTW(e, args[0], args[1], args[2]); return true; } if (address == rt_chldb_fn) { result = RT__ChLDB(e, args[0], args[1]); return true; } if (address == meta_class_fn) { result = Meta__class(e, args[0]); return true; } } result = 0; return false; }
protected void EmitChar(Engine e, uint ch) { if (e.outputSystem == IOSystem.Filter) { e.PerformCall(e.filterAddress, new uint[] { ch }, GLULX_STUB_RESUME_HUFFSTR, e.printingDigit, e.pc); } else { e.SendCharToOutput(ch); } }
/// <summary> /// Returns the non-branch node that will handle the next string action. /// </summary> /// <param name="e">The <see cref="Engine"/> that is printing.</param> /// <returns>A non-branch string node.</returns> /// <remarks>When called on a branch node, this will consume one or /// more compressed string bits.</remarks> public virtual StrNode GetHandlingNode(Engine e) { return this; }
public override void HandleNextChar(Engine e) { e.PrintIndirect( dblIndirect ? e.image.ReadInt32(address) : address, argCount, argsAt); }
public override void HandleNextChar(Engine e) { if (e.NextCompressedStringBit() == true) right.HandleNextChar(e); else left.HandleNextChar(e); }
// finds the location of an object ("parent()" function) private uint Parent(Engine e, uint obj) { return e.image.ReadInt32(obj + 1 + num_attr_bytes + 12); }
// determines whether an object provides a given property ("provides" operator) private uint OP__Pr(Engine e, uint obj, uint id) { switch (Z__Region(e, obj)) { case 3: if (id == indiv_prop_start + PRINT_PROP || id == indiv_prop_start + PRINT_TO_ARRAY_PROP) return 1; else return 0; case 2: if (id == indiv_prop_start + CALL_PROP) return 1; else return 0; case 1: if (id >= indiv_prop_start && id < indiv_prop_start + 8) if (Parent(e, obj) == class_mc) return 1; if (RA__Pr(e, obj, id) != 0) return 1; else return 0; default: return 0; } }
// determines whether an object is a member of a given class ("ofclass" operator) private uint OC__Cl(Engine e, uint obj, uint cla) { switch (Z__Region(e, obj)) { case 3: return (uint)(cla == string_mc ? 1 : 0); case 2: return (uint)(cla == routine_mc ? 1 : 0); case 1: if (cla == class_mc) { if (Parent(e, obj) == class_mc) return 1; if (obj == class_mc || obj == string_mc || obj == routine_mc || obj == object_mc) return 1; return 0; } if (cla == object_mc) { if (Parent(e, obj) == class_mc) return 0; if (obj == class_mc || obj == string_mc || obj == routine_mc || obj == object_mc) return 0; return 1; } if (cla == string_mc || cla == routine_mc) return 0; if (Parent(e, cla) != class_mc) { e.NestedCall(rt_err_fn, ofclass_err, cla, 0xFFFFFFFF); return 0; } uint inlist = RA__Pr(e, obj, 2); if (inlist == 0) return 0; uint inlistlen = RL__Pr(e, obj, 2) / 4; for (uint jx = 0; jx < inlistlen; jx++) if (e.image.ReadInt32(inlist + jx * 4) == cla) return 1; return 0; default: return 0; } }
// determines the metaclass of a routine, string, or object ("metaclass()" function) private uint Meta__class(Engine e, uint obj) { switch (Z__Region(e, obj)) { case 2: return routine_mc; case 3: return string_mc; case 1: if (Parent(e, obj) == class_mc) return class_mc; if (obj == class_mc || obj == string_mc || obj == routine_mc || obj == object_mc) return class_mc; return object_mc; default: return 0; } }
// finds an object's common property table private uint CP__Tab(Engine e, uint obj, uint id) { if (Z__Region(e, obj) != 1) { e.NestedCall(rt_err_fn, 23, obj); return 0; } uint otab = e.image.ReadInt32(obj + 16); if (otab == 0) return 0; uint max = e.image.ReadInt32(otab); otab += 4; return e.PerformBinarySearch(id, 2, otab, 10, max, 0, SearchOptions.None); }
/// <summary> /// Registers a routine address or constant value, using the acceleration /// codes defined in the Glulx specification. /// </summary> /// <param name="e">The <see cref="Engine"/> for which the value is being set.</param> /// <param name="isParam"><see langword="true"/> to set a constant value; /// <b>false</b> to set a routine address.</param> /// <param name="slot">The routine or constant index to set.</param> /// <param name="value">The address of the routine or value of the constant.</param> /// <returns><see langword="true"/> if registration was successful.</returns> public bool SetSlotGlulx(Engine e, bool isParam, uint slot, uint value) { if (isParam && slot == 6) { if (value != e.image.RamStart + SELF_OFFSET) throw new ArgumentException("Unexpected value for acceleration parameter 6"); return true; } Dictionary<uint, VeneerSlot> dict = isParam ? paramSlotMap : funcSlotMap; VeneerSlot fyreSlot; if (dict.TryGetValue(slot, out fyreSlot)) return SetSlotFyre((uint)fyreSlot, value); else return false; }
public GlkMemoryUniStream(uint id, Engine engine, uint address, uint size) : base(id) { this.engine = engine; this.address = address; if (address != 0 && size != 0) buffer = new uint[size]; position = written = read = 0; }
public GlkWindowStream(uint id, Engine engine) : base(id) { this.engine = engine; }
// finds the length of an object's property (".#" operator) private uint RL__Pr(Engine e, uint obj, uint id) { uint cla = 0; if ((id & 0xFFFF0000) != 0) { cla = e.image.ReadInt32(classes_table + 4 * (id & 0xFFFF)); if (OC__Cl(e, obj, cla) == 0) return 0; id >>= 16; obj = cla; } uint prop = CP__Tab(e, obj, id); if (prop == 0) return 0; if (Parent(e, obj) == class_mc && cla == 0) if (id < indiv_prop_start || id >= indiv_prop_start + 8) return 0; if (e.image.ReadInt32(e.image.RamStart + SELF_OFFSET) != obj) { int ix = (e.image.ReadByte(prop + 9) & 1); if (ix != 0) return 0; } return (uint)(4 * e.image.ReadInt16(prop + 2)); }
public override StrNode GetHandlingNode(Engine e) { if (e.NextCompressedStringBit() == true) return right.GetHandlingNode(e); else return left.GetHandlingNode(e); }
// performs bounds checking when reading from a byte array ("->" operator) private uint RT__ChLDB(Engine e, uint array, uint offset) { uint address = array + offset; if (address >= e.image.EndMem) return e.NestedCall(rt_err_fn, 24); return e.image.ReadByte(address); }
public override void HandleNextChar(Engine e) { e.DonePrinting(); }
// performs bounds checking when reading from a word array ("-->" operator) private uint RT__ChLDW(Engine e, uint array, uint offset) { uint address = array + 4 * offset; if (address >= e.image.EndMem) { return e.NestedCall(rt_err_fn, 25); } return e.image.ReadInt32(address); }
public override void HandleNextChar(Engine e) { if (e.outputSystem == IOSystem.Filter) { e.PushCallStub( new CallStub(GLULX_STUB_RESUME_HUFFSTR, e.printingDigit, e.pc, e.fp)); e.pc = address; e.execMode = mode; } else { e.SendStringToOutput(str); } }
// performs bounds checking when writing to a word array ("-->" operator) private uint RT__ChSTW(Engine e, uint array, uint offset, uint val) { uint address = array + 4 * offset; if (address >= e.image.EndMem || address < e.image.RamStart) { return e.NestedCall(rt_err_fn, 27); } else { e.image.WriteInt32(address, val); return 0; } }
/// <summary> /// Performs the action associated with this string node: printing /// a character or string, terminating output, or reading a bit and /// delegating to another node. /// </summary> /// <param name="e">The <see cref="Engine"/> that is printing.</param> /// <remarks>When called on a branch node, this will consume one or /// more compressed string bits.</remarks> public abstract void HandleNextChar(Engine e);
// reads the value of an object's property ("." operator) private uint RV__Pr(Engine e, uint obj, uint id) { uint addr = RA__Pr(e, obj, id); if (addr == 0) { if (id > 0 && id < indiv_prop_start) return e.image.ReadInt32(cpv_start + 4 * id); e.NestedCall(rt_err_fn, readprop_err, obj, id); return 0; } return e.image.ReadInt32(addr); }
public override void HandleNextChar(Engine e) { EmitChar(e, ch); }
// distinguishes between strings, routines, and objects private uint Z__Region(Engine e, uint address) { if (address < 36 || address >= e.image.EndMem) return 0; byte type = e.image.ReadByte(address); if (type >= 0xE0) return 3; if (type >= 0xC0) return 2; if (type >= 0x70 && type <= 0x7F && address >= e.image.RamStart) return 1; return 0; }