/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/* Based on Glulx intrepreter version 0.5.4 */

#ifndef GLK_GLULXE
#define GLK_GLULXE

#include "common/scummsys.h"
#include "common/random.h"
#include "glk/glk_api.h"
#include "glk/glulx/glulx_types.h"

namespace Glk {
namespace Glulx {

class Glulx;
typedef void (Glulx::*CharHandler)(unsigned char);
typedef void (Glulx::*UnicharHandler)(uint32);

/**
 * Glulx game interpreter
 */
class Glulx : public GlkAPI {
private:
	/**
	 * \defgroup vm fields
	 * @{
	 */

	bool vm_exited_cleanly;
	uint gamefile_start, gamefile_len;
	UnicharHandler glkio_unichar_han_ptr;
	char *init_err, *init_err2;
	CharHandler stream_char_handler;

	byte *memmap;
	byte *stack;
	UnicharHandler stream_unichar_handler;

	uint ramstart;
	uint endgamefile;
	uint origendmem;
	uint stacksize;
	uint startfuncaddr;
	uint checksum;
	uint stackptr;
	uint frameptr;
	uint pc;
	uint origstringtable;
	uint stringtable;
	uint valstackbase;
	uint localsbase;
	uint endmem;
	uint protectstart, protectend;
	uint prevpc;

	/**@}*/

	/**
	 * \defgroup main fields
	 * @{
	 */

	/**
	 * The library_autorestore_hook is called right after the VM's initial setup. This is an appropriate time
	 * to autorestore an initial game state, if the library has that capability. (Currently, only iosglk does.)
	 */
	void(*library_autorestore_hook)(void);

	/**@}*/

	/**
	 * \defgroup accel fields
	 * @{
	 */

	uint classes_table;     ///< class object array
	uint indiv_prop_start;  ///< first individual prop ID
	uint class_metaclass;   ///< "Class" class object
	uint object_metaclass;  ///< "Object" class object
	uint routine_metaclass; ///< "Routine" class object
	uint string_metaclass;  ///< "String" class object
	uint self;              ///< address of global "self"
	uint num_attr_bytes;    ///< number of attributes / 8
	uint cpv__start;        ///< array of common prop defaults
	accelentry_t **accelentries;

	/**@}*/

	/**
	 * \defgroup heap fields
	 * @{
	 */

	uint heap_start;    ///< zero for inactive heap
	int alloc_count;

	/* The heap_head/heap_tail is a doubly-linked list of blocks, both
	   free and allocated. It is kept in address order. It should be
	   complete -- that is, the first block starts at heap_start, and each
	   block ends at the beginning of the next block, until the last one,
	   which ends at endmem.

	   (Heap_start is never the same as end_mem; if there is no heap space,
	   then the heap is inactive and heap_start is zero.)

	   Adjacent free blocks may be merged at heap_alloc() time.

	   ### To make alloc more efficient, we could keep a separate
	   free-list. To make free more efficient, we could keep a hash
	   table of allocations.
	 */
	heapblock_t *heap_head;
	heapblock_t *heap_tail;

	/**@}*/

	/**
	 * \defgroup operand fields
	 * @{
	 */

	/**
	 * This is a handy array in which to look up operandlists quickly. It stores the operandlists
	 * for the first 128 opcodes, which are the ones used most frequently.
	 */
	const operandlist_t *fast_operandlist[0x80];

	/**@}*/

	/**
	 * \defgroup serial fields
	 * @{
	 */

	/**
	 * This can be adjusted before startup by platform-specific startup code -- that is, preference code.
	 */
	int max_undo_level;

	int undo_chain_size;
	int undo_chain_num;
	byte **undo_chain;

	/**
	 * This will contain a copy of RAM (ramstate to endmem) as it exists in the game file.
	 */
	byte *ramcache;

	/**@}*/

	/**
	 * \defgroup string fields
	 * @{
	 */

	uint iosys_mode;
	uint iosys_rock;

	/**
	 * The current string-decoding tables, broken out into a fast and easy-to-use form.
	 */
	bool tablecache_valid;
	cacheblock_t tablecache;

	/* This misbehaves if a Glk function has more than one S argument. */
#define STATIC_TEMP_BUFSIZE (127)
	char temp_buf[STATIC_TEMP_BUFSIZE + 1];

	/**@}*/

	Common::String _savegameDescription;
protected:
	/**
	 * \defgroup glkop fields
	 * @{
	 */

	/**
	 * The library_select_hook is called every time the VM blocks for input.
	 * The app might take this opportunity to autosave, for example.
	 */
	void (*library_select_hook)(uint);

	arrayref_t *arrays;

	/**
	 * The list of hash tables, for the classes.
	 */
	int num_classes;
	classtable_t **classes;

	/**@}*/

	/**
	 * \defgroup accel support methods
	 * @{
	 */

	void accel_error(const char *msg);
	uint func_1_z__region(uint argc, uint *argv);

	/**
	 * The old set of accel functions (2 through 7) are deprecated; they behave badly if the Inform 6
	 * NUM_ATTR_BYTES option (parameter 7) is changed from its default value (7). They will not be removed,
	 * but new games should use functions 8 through 13 instead
	 */
	uint func_2_cp__tab(uint argc, uint *argv);
	uint func_3_ra__pr(uint argc, uint *argv);
	uint func_4_rl__pr(uint argc, uint *argv);
	uint func_5_oc__cl(uint argc, uint *argv);
	uint func_6_rv__pr(uint argc, uint *argv);
	uint func_7_op__pr(uint argc, uint *argv);

	/**
	 * Here are the newer functions, which support changing NUM_ATTR_BYTES.
	   These call get_prop_new() instead of get_prop()
	 */
	uint func_8_cp__tab(uint argc, uint *argv);
	uint func_9_ra__pr(uint argc, uint *argv);
	uint func_10_rl__pr(uint argc, uint *argv);
	uint func_11_oc__cl(uint argc, uint *argv);
	uint func_12_rv__pr(uint argc, uint *argv);
	uint func_13_op__pr(uint argc, uint *argv);
	int obj_in_class(uint obj);

	/**
	 * Look up a property entry.
	 */
	uint get_prop(uint obj, uint id);

	/**
	 * Look up a property entry. This is part of the newer set of accel functions (8 through 13),
	 * which support increasing NUM_ATTR_BYTES. It is identical to get_prop() except that it calls
	 * the new versions of func_5 and func_2
	 */
	uint get_prop_new(uint obj, uint id);

	/**@}*/

	/**
	 * \defgroup glkop support methods
	 * @{
	 */

	/**
	 * Build a hash table to hold a set of Glk objects.
	 */
	classtable_t *new_classtable(uint firstid);

	/**
	 * Find a Glk object in the appropriate hash table.
	 */
	void *classes_get(int classid, uint objid);

	/**
	 * Put a Glk object in the appropriate hash table. If origid is zero, invent a new
	 * unique ID for it.
	 */
	classref_t *classes_put(int classid, void *obj, uint origid);

	/**
	 * Delete a Glk object from the appropriate hash table.
	 */
	void classes_remove(int classid, void *obj);

	long glulxe_array_locate(void *array, uint len, char *typecode, gidispatch_rock_t objrock, int *elemsizeref);
	gidispatch_rock_t glulxe_array_restore(long bufkey, uint len, char *typecode, void **arrayref);

	char *grab_temp_c_array(uint addr, uint len, int passin);
	void release_temp_c_array(char *arr, uint addr, uint len, int passout);
	uint *grab_temp_i_array(uint addr, uint len, int passin);
	void release_temp_i_array(uint *arr, uint addr, uint len, int passout);
	void **grab_temp_ptr_array(uint addr, uint len, int objclass, int passin);
	void release_temp_ptr_array(void **arr, uint addr, uint len, int objclass, int passout);

	/**
	 * This reads through the prototype string, and pulls Floo objects off the stack. It also works out the maximal number
	 * of gluniversal_t objects which could be used by the Glk call in question. It then allocates space for them.
	 */
	void prepare_glk_args(const char *proto, dispatch_splot_t *splot);

	/**
	 * This long and unpleasant function translates a set of Floo objects into a gluniversal_t array. It's recursive, too,
	 * to deal with structures.
	 */
	void parse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, int *argnumptr, uint subaddress, int subpassin);

	/**
	 * This is about the reverse of parse_glk_args().
	 */
	void unparse_glk_args(dispatch_splot_t *splot, const char **proto, int depth,
	                      int *argnumptr, uint subaddress, int subpassout);

	/**
	 * Create a string identifying this game. We use the first 64 bytes of the memory map, encoded as hex,
	 */
	char *get_game_id();

	uint ReadMemory(uint addr);
	void WriteMemory(uint addr, uint val);
	char *CaptureCArray(uint addr, uint len, int passin);
	void ReleaseCArray(char *ptr, uint addr, uint len, int passout);
	uint *CaptureIArray(uint addr, uint len, int passin);
	void ReleaseIArray(uint *ptr, uint addr, uint len, int passout);
	void **CapturePtrArray(uint addr, uint len, int objclass, int passin);
	void ReleasePtrArray(void **ptr, uint addr, uint len, int objclass, int passout);
	uint ReadStructField(uint addr, uint fieldnum);
	void WriteStructField(uint addr, uint fieldnum, uint val);
	char *DecodeVMString(uint addr);
	void ReleaseVMString(char *ptr);
	uint32 *DecodeVMUstring(uint addr);
	void ReleaseVMUstring(uint32 *ptr);

	/**@}*/

	/**
	 * \defgroup search support methods
	 * @{
	 */

	/**
	 * This massages the key into a form that's easier to handle. When it returns, the key will
	 * be stored in keybuf if keysize <= 4; otherwise, it will be in memory.
	 */
	void fetchkey(unsigned char *keybuf, uint key, uint keysize, uint options);

	/**@}*/

	/**
	 * \defgroup serial support methods
	 * @{
	 */

	uint write_memstate(dest_t *dest);
	uint write_heapstate(dest_t *dest, int portable);
	uint write_stackstate(dest_t *dest, int portable);
	uint read_memstate(dest_t *dest, uint chunklen);
	uint read_heapstate(dest_t *dest, uint chunklen, int portable, uint *sumlen, uint **summary);
	uint read_stackstate(dest_t *dest, uint chunklen, int portable);
	uint write_heapstate_sub(uint sumlen, uint *sumarray, dest_t *dest, int portable);
	static int sort_heap_summary(const void *p1, const void *p2);

	int read_byte(dest_t *dest, byte *val);
	int read_short(dest_t *dest, uint16 *val);
	int read_long(dest_t *dest, uint *val);

	int write_byte(dest_t *dest, byte val);
	int write_short(dest_t *dest, uint16 val);
	int write_long(dest_t *dest, uint val);

	int read_buffer(dest_t *dest, byte *ptr, uint len);
	int reposition_write(dest_t *dest, uint pos);
	int write_buffer(dest_t *dest, const byte *ptr, uint len);

	/**@}*/

	/**
	 * \defgroup string support methods
	 * @{
	 */

	void stream_setup_unichar();

	void nopio_char_han(unsigned char ch);
	void filio_char_han(unsigned char ch);
	void nopio_unichar_han(uint32 ch);
	void filio_unichar_han(uint32 ch);
	void glkio_unichar_nouni_han(uint32 val);

	void dropcache(cacheblock_t *cablist);
	void buildcache(cacheblock_t *cablist, uint nodeaddr, int depth, int mask);
	void dumpcache(cacheblock_t *cablist, int count, int indent);

	/**@}*/
public:
	/**
	 * Constructor
	 */
	Glulx(OSystem *syst, const GlkGameDescription &gameDesc);

	/**
	 * Run the game
	 */
	void runGame() override;

	/**
	 * Returns the running interpreter type
	 */
	InterpreterType getInterpreterType() const override {
		return INTERPRETER_GLULX;
	}

	/**
	 * Loads Quetzal chunks from the passed savegame
	 */
	Common::Error loadGameChunks(QuetzalReader &quetzal) override;

	/**
	 * Writes out the Quetzal chunks within a savegame
	 */
	Common::Error saveGameChunks(QuetzalWriter &quetzal) override;

	/**
	 * Load a savegame from the passed Quetzal file chunk stream
	 */
	Common::Error readSaveData(Common::SeekableReadStream *rs) override;

	/**
	 * Save the game. The passed write stream represents access to the UMem chunk
	 * in the Quetzal save file that will be created
	 */
	Common::Error writeGameData(Common::WriteStream *ws) override;

	/**
	 * \defgroup Main access methods
	 * @{
	 */

	/**
	 * Display an error in the error window, and then exit.
	 */
	void NORETURN_PRE fatal_error_handler(const char *str, const char *arg, bool useVal, int val);

	/**
	 * Display a warning in the error window, and then continue.
	 */
	void nonfatal_warning_handler(const char *str, const char *arg, bool useVal, int val);

	/**
	 * \defgroup Files access methods
	 * @{
	 */

	/**
	 * Validates the game file, and if it's invalid, displays an error dialog
	 */
	bool is_gamefile_valid();

	/**@}*/

	/**
	 * \defgroup Vm access methods
	 * @{
	 */

	/**
	 * Read in the game file and build the machine, allocating all the memory necessary.
	*/
	void setup_vm();

	/**
	 * Deallocate all the memory and shut down the machine.
	 */
	void finalize_vm();

	/**
	 * Put the VM into a state where it's ready to begin executing the game. This is called
	 * both at startup time, and when the machine performs a "restart" opcode.
	 */
	void vm_restart();

	/**
	 * Change the size of the memory map. This may not be available at all; #define FIXED_MEMSIZE
	 * if you want the interpreter to unconditionally refuse. The internal flag should be true only
	 * when the heap-allocation system is calling. Returns 0 for success; otherwise, the operation failed.
	 */
	uint change_memsize(uint newlen, bool internal);

	/**
	 * If addr is 0, pop N arguments off the stack, and put them in an array. If non-0, take N arguments
	 * from that main memory address instead. This has to dynamically allocate if there are more than
	 * 32 arguments, but that shouldn't be a problem.
	 */
	uint *pop_arguments(uint count, uint addr);

	/**
	 * Make sure that count bytes beginning with addr all fall within the current memory map.
	 * This is called at every memory (read) access if VERIFY_MEMORY_ACCESS is defined in the header file.
	*/
	void verify_address(uint addr, uint count);

	/**
	 * Make sure that count bytes beginning with addr all fall within RAM. This is called at every memory
	 * write if VERIFY_MEMORY_ACCESS is defined in the header file.
	*/
	void verify_address_write(uint addr, uint count);

	/**
	 * Make sure that an array of count elements (size bytes each), starting at addr, does not fall
	 * outside the memory map. This goes to some trouble that verify_address() does not, because we need
	 * to be wary of lengths near -- or beyond -- 0x7FFFFFFF.
	 */
	void verify_array_addresses(uint addr, uint count, uint size);

	/**@}*/

	/**
	 * \defgroup Exec access methods
	 * @{
	 */

	/**
	 * The main interpreter loop. This repeats until the program is done
	 */
	void execute_loop();

	/**@}*/

	/**
	 * \defgroup Operand access methods
	 * @{
	 */

	/**
	 * Set up the fast-lookup array of operandlists. This is called just once, when the terp starts up.
	 */
	void init_operands();

	/**
	 * Return the operandlist for a given opcode. For opcodes in the range 00..7F, it's faster
	 * to use the array fast_operandlist[].
	*/
	const operandlist_t *lookup_operandlist(uint opcode);

	/**
	 * Read the list of operands of an instruction, and put the values in args. This assumes
	 * that the PC is at the beginning of the operand mode list (right after an opcode number.)
	 * Upon return, the PC will be at the beginning of the next instruction.
	 *
	 * This also assumes that args points at an allocated array of MAX_OPERANDS oparg_t structures.
	*/
	void parse_operands(oparg_t *opargs, const operandlist_t *oplist);

	/**
	 * Store a result value, according to the desttype and destaddress given. This is usually used to store
	 * the result of an opcode, but it's also used by any code that pulls a call-stub off the stack.
	 */
	void store_operand(uint desttype, uint destaddr, uint storeval);

	void store_operand_s(uint desttype, uint destaddr, uint storeval);
	void store_operand_b(uint desttype, uint destaddr, uint storeval);

	/**@}*/

	/**
	 * \defgroup Func access methods
	 * @{
	 */

	/**
	 * This writes a new call frame onto the stack, at stackptr. It leaves frameptr pointing
	 * to the frame (ie, the original stackptr value.) argc and argv are an array of arguments.
	 * Note that if argc is zero, argv may be nullptr.
	 */
	void enter_function(uint addr, uint argc, uint *argv);

	/**
	 * Pop the current call frame off the stack. This is very simple.
	*/
	void leave_function();

	/**
	 * Push the magic four values on the stack: result destination, PC, and frameptr.
	 */
	void push_callstub(uint desttype, uint destaddr);

	/**
	 * Remove the magic four values from the stack, and use them. The returnvalue, whatever it is,
	 * is put at the result destination; the PC and frameptr registers are set.
	*/
	void pop_callstub(uint returnvalue);

	/**
	 * Remove the magic four values, but interpret them as a string restart state.
	 * Returns zero if it's a termination stub, or returns the restart address. The bitnum is extra.
	 */
	uint pop_callstub_string(int *bitnum);

	/**@}*/

	/**
	 * \defgroup Heap access methods
	 * @{
	 */

	/**
	 * Set the heap state to inactive, and free the block lists. This is called when the game
	 * starts or restarts.
	 */
	void heap_clear();

	/**
	 * Returns whether the heap is active.
	 */
	int heap_is_active() const;

	/**
	 * Returns the start address of the heap, or 0 if the heap is not active.
	 */
	uint heap_get_start() const;

	/**
	 * Allocate a block. If necessary, activate the heap and/or extend memory. This may not be
	 * available at all; #define FIXED_MEMSIZE if you want the interpreter to unconditionally refuse.
	 * Returns the memory address of the block, or 0 if the operation failed.
	 */
	uint heap_alloc(uint len);

	/**
	 * Free a heap block. If necessary, deactivate the heap.
	 */
	void heap_free(uint addr);

	/**
	 * Create an array of words, in the VM serialization format:
	 *
	 *   heap_start
	 *   alloc_count
	 *   addr of first block
	 *   len of first block
	 *   ...
	 *
	 * (Note that these are uint values -- native byte ordering. Also, the blocks will be in address order,
	 * which is a stricter guarantee than the VM specifies; that'll help in heap_apply_summary().)
	 *
	 * If the heap is inactive, store nullptr. Return 0 for success; otherwise, the operation failed.
	 *
	 * The array returned in summary must be freed with glulx_free() after the caller uses it.
	 */
	int heap_get_summary(uint *valcount, uint **summary);

	/**
	 * Given an array of words in the above format, set up the heap to contain it. As noted above,
	 * the caller must ensure that the blocks are in address order. When this is called, the heap
	 * must be inactive.
	 *
	 * Return 0 for success. Otherwise the operation failed (and, most likely, caused a fatal error).
	*/
	int heap_apply_summary(uint valcount, uint *summary);

	/**@}*/

	/**
	 * \defgroup Serial access methods
	 * @{
	 */

	/**@}*/

	/**
	 * \defgroup Search access methods
	 * @{
	 */


	/**
	 * An array of data structures is stored in memory, beginning at start, each structure being structsize bytes.
	 * Within each struct, there is a key value keysize bytes long, starting at position keyoffset (from
	 * the start of the structure.) Search through these in order. If one is found whose key matches, return it.
	 * If numstructs are searched with no result, return nullptr.
	 *
	 * numstructs may be -1 (0xFFFFFFFF) to indicate no upper limit to the number of structures to search.
	 * The search will continue until a match is found, or (if ZeroKeyTerminates is set) a zero key.
	 *
	 * The KeyIndirect, ZeroKeyTerminates, and ReturnIndex options may be used.
	 */
	uint linear_search(uint key, uint keysize, uint start, uint structsize, uint numstructs,
	                   uint keyoffset, uint options);

	/**
	 * An array of data structures is in memory, as above. However, the structs must be stored in forward
	 * order of their keys (taking each key to be a multibyte unsigned integer.) There can be no duplicate keys.
	 * numstructs must indicate the exact length of the array; it cannot be -1.
	 *
	 * The KeyIndirect and ReturnIndex options may be used.
	 */
	uint binary_search(uint key, uint keysize, uint start, uint structsize, uint numstructs,
	                   uint keyoffset, uint options);

	/**
	 * The structures may be anywhere in memory, in any order. They are linked by a four-byte address field,
	 * which is found in each struct at position nextoffset. If this field contains zero, it indicates
	 * the end of the linked list.
	 *
	 * The KeyIndirect and ZeroKeyTerminates options may be used.
	 */
	uint linked_search(uint key, uint keysize, uint start, uint keyoffset, uint nextoffset, uint options);

	/**@}*/

	/**
	 * \defgroup Osdepend access methods
	 * @{
	 */

	inline void *glulx_malloc(uint len) {
		return malloc(len);
	}
	inline void *glulx_realloc(void *ptr, uint len) {
		return realloc(ptr, len);
	}
	inline void glulx_free(void *ptr) {
		free(ptr);
	}
	inline void glulx_setrandom(uint32 seed) {
		_random.setSeed(seed);
	}
	inline uint glulx_random() {
		return _random.getRandomNumber(0xfffffff);
	}

	void glulx_sort(void *addr, int count, int size, int(*comparefunc)(const void *p1, const void *p2));

	/**@}*/

	/**
	 * \defgroup Gestalt access methods
	 * @{
	 */

	uint do_gestalt(uint val, uint val2);

	/**@}*/

	/**
	 * \defgroup Glkop access methods
	 * @{
	 */

	/**
	 * glkop section initialization
	 */
	void glkopInit();

	void set_library_select_hook(void(*func)(uint));

	/**
	 * Set up the class hash tables and other startup-time stuff.
	 */
	bool init_dispatch();

	/**
	 * The object registration/unregistration callbacks that the library calls
	 * to keep the hash tables up to date.
	 */
	gidispatch_rock_t glulxe_classtable_register(void *obj, uint objclass);

	gidispatch_rock_t glulxe_classtable_register_existing(void *obj, uint objclass, uint dispid);

	void glulxe_classtable_unregister(void *obj, uint objclass, gidispatch_rock_t objrock);

	gidispatch_rock_t glulxe_retained_register(void *array, uint len, const char *typecode);
	void glulxe_retained_unregister(void *array, uint len, const char *typecode, gidispatch_rock_t objrock);

	/**
	 * Turn a list of Glulx arguments into a list of Glk arguments, dispatch the function call, and return the result.
	 */
	uint perform_glk(uint funcnum, uint numargs, uint *arglist);

	/**
	 * Read the prefixes of an argument string -- the "<>&+:#!" chars.
	 */
	const char *read_prefix(const char *cx, int *isref, int *isarray, int *passin, int *passout,
	                        int *nullok, int *isretained, int *isreturn);

	/**
	 * This is used by some interpreter code which has to, well, find a Glk stream given its ID.
	 */
	strid_t find_stream_by_id(uint objid);

	/**
	 * Return the ID of a given Glk window.
	 */
	uint find_id_for_window(winid_t win);

	/**
	 * Return the ID of a given Glk stream.
	 */
	uint find_id_for_stream(strid_t str);

	/**
	 * Return the ID of a given Glk fileref.
	 */
	uint find_id_for_fileref(frefid_t fref);

	/**
	 * Return the ID of a given Glk schannel.
	 */
	uint find_id_for_schannel(schanid_t schan);

	/**@}*/

	/**
	 * \defgroup Profile access methods
	 * @{
	 */

	void setup_profile(strid_t stream, char *filename);
	int init_profile();
	void profile_set_call_counts(int flag);

#ifdef VM_PROFILING
	uint profile_opcount;
	#define profile_tick() (profile_opcount++)
	int profile_profiling_active();
	void profile_in(uint addr, uint stackuse, int accel);
	void profile_out(uint stackuse);
	void profile_fail(const char *reason);
	void profile_quit();
#else /* VM_PROFILING */
	void profile_tick() {}
	void profile_profiling_active() {}
	void profile_in(uint addr, uint stackuse, int accel) {}
	void profile_out(uint stackuse)  {}
	void profile_fail(const char *reason) {}
	void profile_quit() {}
#endif /* VM_PROFILING */

#ifdef VM_DEBUGGER
	unsigned long debugger_opcount;
	void debugger_tick() { debugger_opcount++ }
	int debugger_load_info_stream(strid_t stream);
	int debugger_load_info_chunk(strid_t stream, uint pos, uint len);
	void debugger_track_cpu(int flag);
	void debugger_set_start_trap(int flag);
	void debugger_set_quit_trap(int flag);
	void debugger_set_crash_trap(int flag);
	void debugger_check_story_file();
	void debugger_setup_start_state();
	int debugger_ever_invoked();
	int debugger_cmd_handler(char *cmd);
	void debugger_cycle_handler(int cycle);
	void debugger_check_func_breakpoint(uint addr);
	void debugger_block_and_debug(char *msg);
	void debugger_handle_crash(char *msg);
	void debugger_handle_quit();
#else /* VM_DEBUGGER */
	void debugger_tick() {}
	void debugger_check_story_file() {}
	void debugger_setup_start_state() {}
	void debugger_check_func_breakpoint(uint addr) {}
	void debugger_handle_crash(const char *msg) {}
#endif /* VM_DEBUGGER */

	/**@}*/

	/**
	 * \defgroup Accel access methods
	 * @{
	 */

	acceleration_func accel_find_func(uint index);
	acceleration_func accel_get_func(uint addr);
	void accel_set_func(uint index, uint addr);
	void accel_set_param(uint index, uint val);

	uint accel_get_param_count() const;
	uint accel_get_param(uint index) const;

	/**
	 * Iterate the entire acceleration table, calling the callback for each (non-nullptr) entry.
	 * This is used only for autosave.
	 */
	void accel_iterate_funcs(void(*func)(uint index, uint addr));

	/**@}*/

	/**
	 * \defgroup Float access methods
	 * @{
	 */
#ifdef FLOAT_SUPPORT

	/* Uncomment this definition if your gfloat32 type is not a standard
	   IEEE-754 single-precision (32-bit) format. Normally, Glulx assumes
	   that it can reinterpret-cast IEEE-754 int values into gfloat32
	   values. If you uncomment this, Glulx switches to lengthier
	   (but safer) encoding and decoding functions. */
	/* #define FLOAT_NOT_NATIVE (1) */

	int init_float() {
		return true;
	}

	/**
	 * Encode floats by a lot of annoying bit manipulation.
	 * The function is adapted from code in Python  (Objects/floatobject.c)
	 */
	static uint encode_float(gfloat32 val);

	/**
	 * Decode floats by a lot of annoying bit manipulation.
	 * The function is adapted from code in Python  (Objects/floatobject.c)
	 */
	static gfloat32 decode_float(uint val);

	/* Uncomment this definition if your powf() function does not support
	   all the corner cases specified by C99. If you uncomment this,
	   osdepend.c will provide a safer implementation of glulx_powf(). */
	/* #define FLOAT_COMPILE_SAFER_POWF (1) */

	inline gfloat32 glulx_powf(gfloat32 val1, gfloat32 val2) const {
		return powf(val1, val2);
	}

#endif /* FLOAT_SUPPORT */
	/**@}*/

	/**
	 * \defgroup serial access methods
	 * @{
	 */

	/**
	 * Set up the undo chain and anything else that needs to be set up.
	 */
	bool init_serial();

	/**
	 * Clean up memory when the VM shuts down.
	 */
	void final_serial();

	/**
	 * Add a state pointer to the undo chain. This returns 0 on success, 1 on failure.
	 */
	uint perform_saveundo();

	/**
	 * Pull a state pointer from the undo chain. This returns 0 on success, 1 on failure.
	 * Note that if it succeeds, the frameptr, localsbase, and valstackbase registers are invalid;
	 * they must be rebuilt from the stack.
	 */
	uint perform_restoreundo();

	uint perform_verify();

	/**@}*/

	/**
	 * \defgroup Strings access methods
	 * @{
	 */

	/**
	 * Write a signed integer to the current output stream.
	 */
	void stream_num(int val, int inmiddle, int charnum);

	/**
	 * Write a Glulx string object to the current output stream. inmiddle is zero if we are beginning
	 * a new string, or nonzero if restarting one (E0/E1/E2, as appropriate for the string type).
	 */
	void stream_string(uint addr, int inmiddle, int bitnum);

	/**
	 * Get the current table address.
	 */
	uint stream_get_table();

	/**
	 * Set the current table address, and rebuild decoding cache.
	 */
	void stream_set_table(uint addr);

	void stream_get_iosys(uint *mode, uint *rock);
	void stream_set_iosys(uint mode, uint rock);
	char *make_temp_string(uint addr);
	uint32 *make_temp_ustring(uint addr);
	void free_temp_string(char *str);
	void free_temp_ustring(uint32 *str);

	/**@}*/
};

extern Glulx *g_vm;

#define fatal_error(s)  (fatal_error_handler((s), nullptr, false, 0))
#define fatal_error_2(s1, s2)  (fatal_error_handler((s1), (s2), false, 0))
#define fatal_error_i(s, v)  (fatal_error_handler((s), nullptr, true, (v)))
#define nonfatal_warning(s) (nonfatal_warning_handler((s), nullptr, false, 0))
#define nonfatal_warning_2(s1, s2) (nonfatal_warning_handler((s1), (s2), false, 0))
#define nonfatal_warning_i(s, v) (nonfatal_warning_handler((s), nullptr, true, (v)))

} // End of namespace Glulx
} // End of namespace Glk

#endif
