Exemple #1
0
inline static void BTC_memory_account_mark(NewGC *gc, mpage *page, void *ptr)
{
  GCDEBUG((DEBUGOUTF, "BTC_memory_account_mark: %p/%p\n", page, ptr));
  if(page->size_class) {
    if(page->size_class > 1) {
      /* big page */
      objhead *info = BIG_PAGE_TO_OBJHEAD(page);
      
      if(info->btc_mark == gc->old_btc_mark) {
        info->btc_mark = gc->new_btc_mark;
        account_memory(gc, gc->current_mark_owner, gcBYTES_TO_WORDS(page->size));
        push_ptr(gc, TAG_AS_BIG_PAGE_PTR(ptr));
      }
    } else {
      /* medium page */
      objhead *info = MED_OBJHEAD(ptr, page->size);

      if(info->btc_mark == gc->old_btc_mark) {
        info->btc_mark = gc->new_btc_mark;
        account_memory(gc, gc->current_mark_owner, info->size);
        ptr = OBJHEAD_TO_OBJPTR(info);
        push_ptr(gc, ptr);
      }
    }
  } else {
    objhead *info = OBJPTR_TO_OBJHEAD(ptr);

    if(info->btc_mark == gc->old_btc_mark) {
      info->btc_mark = gc->new_btc_mark;
      account_memory(gc, gc->current_mark_owner, info->size);
      push_ptr(gc, ptr);
    }
  }
}
Exemple #2
0
inline static void BTC_memory_account_mark(NewGC *gc, mpage *page, void *ptr, int is_a_master_page)
{
  GCDEBUG((DEBUGOUTF, "BTC_memory_account_mark: %p/%p\n", page, ptr));

  /* In the case of is_a_master_page, whether this place is charged is
     a little random: there's no guarantee that the btc_mark values
     are in sync, and there are races among places. Approximations are
     ok for accounting, though, as long as the probably for completely
     wrong accounting is very low.

     At the same time, we need to synchronize enough so that two
     places with different new_btc_mark values don't send each other
     into infinite loops (with the btc_mark value bouncing back and
     forth) or overcounting. We synchronize enough by having a single
     new_btc_mark value for master pages, and we stall if the value
     isn't what this place wants. */

  if (is_a_master_page)
    check_master_btc_mark(gc, page);

  if(page->size_class) {
    if(page->size_class > 1) {
      /* big page */
      objhead *info = BIG_PAGE_TO_OBJHEAD(page);
      
      if(info->btc_mark == gc->old_btc_mark) {
        info->btc_mark = gc->new_btc_mark;
        account_memory(gc, gc->current_mark_owner, gcBYTES_TO_WORDS(page->size), is_a_master_page);
        push_ptr(gc, TAG_AS_BIG_PAGE_PTR(ptr));
      }
    } else {
      /* medium page */
      objhead *info = MED_OBJHEAD(ptr, page->size);

      if(info->btc_mark == gc->old_btc_mark) {
        info->btc_mark = gc->new_btc_mark;
        account_memory(gc, gc->current_mark_owner, info->size, is_a_master_page);
        ptr = OBJHEAD_TO_OBJPTR(info);
        push_ptr(gc, ptr);
      }
    }
  } else {
    objhead *info = OBJPTR_TO_OBJHEAD(ptr);

    if(info->btc_mark == gc->old_btc_mark) {
      info->btc_mark = gc->new_btc_mark;
      account_memory(gc, gc->current_mark_owner, info->size, 0);
      push_ptr(gc, ptr);
    }
  }
}
Exemple #3
0
inline static void BTC_memory_account_mark(NewGC *gc, mpage *page, void *ptr, int is_a_master_page)
{
  GCDEBUG((DEBUGOUTF, "BTC_memory_account_mark: %p/%p\n", page, ptr));

  /* In the case of is_a_master_page, whether this place is charged is
     a little random: there's no guarantee that the btc_mark values are
     in sync, and there are races among places. Approximations are ok for
     accounting, though, as long as the probably for completely wrong
     accounting is very low. */

  if(page->size_class) {
    if(page->size_class > 1) {
      /* big page */
      objhead *info = BIG_PAGE_TO_OBJHEAD(page);
      
      if(info->btc_mark == gc->old_btc_mark) {
        info->btc_mark = gc->new_btc_mark;
        account_memory(gc, gc->current_mark_owner, gcBYTES_TO_WORDS(page->size), is_a_master_page);
        push_ptr(gc, TAG_AS_BIG_PAGE_PTR(ptr));
      }
    } else {
      /* medium page */
      objhead *info = MED_OBJHEAD(ptr, page->size);

      if(info->btc_mark == gc->old_btc_mark) {
        info->btc_mark = gc->new_btc_mark;
        account_memory(gc, gc->current_mark_owner, info->size, is_a_master_page);
        ptr = OBJHEAD_TO_OBJPTR(info);
        push_ptr(gc, ptr);
      }
    }
  } else {
    objhead *info = OBJPTR_TO_OBJHEAD(ptr);

    if(info->btc_mark == gc->old_btc_mark) {
      info->btc_mark = gc->new_btc_mark;
      account_memory(gc, gc->current_mark_owner, info->size, 0);
      push_ptr(gc, ptr);
    }
  }
}
Exemple #4
0
int preload(char *path, size_t *size)
{
	int file = open(path, O_RDONLY);
	struct stat statrec;
	if (file < 0) {
		fprintf(stderr, "Failed to open file \"%s\" (%d)\n", path, errno);
		return 1;
	}
	if (fstat(file, &statrec) != 0) {
		close(file);
		fprintf(stderr, "Failed to stat file \"%s\" (%d)\n", path, errno);
		return 2;
	}
	size_t length = statrec.st_size;
	if (length == 0) {
		close(file);
		fprintf(stderr, "Skipping empty file \"%s\"\n", path);
		return 0;
	}
	void *map = mmap(NULL, length, PROT_READ | PROT_EXEC,
		MAP_SHARED, file, 0);
	if (map == MAP_FAILED) {
		close(file);
		fprintf(stderr, "Failed to map file \"%s\" (%d)\n", path, errno);
		return 3;
	}
	int mlock_error = mlock(map, length);
	if (mlock_error != 0) {
		close(file);
		print_mlock_error(path, errno);
		return 4;
	}
	close(file);
	push_ptr(map, length);
	*size = length;
	return 0;
}
int Logic::runScript2(byte *scriptData, byte *objectData, byte *offsetPtr) {
	// Interestingly, unlike our BASS engine the stack is a local variable.
	// I don't know whether or not this is relevant to the working of the
	// BS2 engine.

	int32 stack[STACK_SIZE];
	int32 stackPtr = 0;

	uint32 offset = READ_LE_UINT32(offsetPtr);

	ResHeader header;

	header.read(scriptData);

	scriptData += ResHeader::size() + ObjectHub::size();

	// The script data format:
	//	int32_TYPE	1		Size of variable space in bytes
	//	...				The variable space
	//	int32_TYPE	1		numberOfScripts
	//	int32_TYPE	numberOfScripts	The offsets for each script

	// Initialise some stuff

	uint32 ip = 0;			 // Code pointer
	int scriptNumber;

	// Get the start of variables and start of code

	byte *localVars = scriptData + 4;
	byte *code = scriptData + READ_LE_UINT32(scriptData) + 4;
	uint32 noScripts = READ_LE_UINT32(code);

	code += 4;

	byte *offsetTable = code;

	if (offset < noScripts) {
		ip = READ_LE_UINT32(offsetTable + offset * 4);
		scriptNumber = offset;
		debug(8, "Starting script %d from %d", scriptNumber, ip);
	} else {
		uint i;

		ip = offset;

		for (i = 1; i < noScripts; i++) {
			if (READ_LE_UINT32(offsetTable + 4 * i) >= ip)
				break;
		}

		scriptNumber = i - 1;
		debug(8, "Resuming script %d from %d", scriptNumber, ip);
	}

	// There are a couple of known script bugs related to interacting with
	// certain objects. We try to work around a few of them.

	bool checkMopBug = false;
	bool checkPyramidBug = false;
	bool checkElevatorBug = false;

	if (scriptNumber == 2) {
		if (strcmp((char *)header.name, "mop_73") == 0)
			checkMopBug = true;
		else if (strcmp((char *)header.name, "titipoco_81") == 0)
			checkPyramidBug = true;
		else if (strcmp((char *)header.name, "lift_82") == 0)
			checkElevatorBug = true;
	}

	code += noScripts * 4;

	// Code should now be pointing at an identifier and a checksum
	byte *checksumBlock = code;

	code += 4 * 3;

	if (READ_LE_UINT32(checksumBlock) != 12345678) {
		error("Invalid script in object %s", header.name);
		return 0;
	}

	int32 codeLen = READ_LE_UINT32(checksumBlock + 4);
	int32 checksum = 0;

	for (int i = 0; i < codeLen; i++)
		checksum += (unsigned char) code[i];

	if (checksum != (int32)READ_LE_UINT32(checksumBlock + 8)) {
		debug(1, "Checksum error in object %s", header.name);
		// This could be bad, but there has been a report about someone
		// who had problems running the German version because of
		// checksum errors. Could there be a version where checksums
		// weren't properly calculated?
	}

	bool runningScript = true;

	int parameterReturnedFromMcodeFunction = 0;	// Allow scripts to return things
	int savedStartOfMcode = 0;	// For saving start of mcode commands

	while (runningScript) {
		int i;
		int32 a, b;
		int curCommand, parameter, value; // Command and parameter variables
		int retVal;
		int caseCount;
		bool foundCase;
		byte *ptr;

		curCommand = code[ip++];

		switch (curCommand) {

		// Script-related opcodes

		case CP_END_SCRIPT:
			// End the script
			runningScript = false;

			// WORKAROUND: The dreaded pyramid bug makes the torch
			// untakeable when you speak to Titipoco. This is
			// because one of the conditions for the torch to be
			// takeable is that Titipoco isn't doing anything out
			// of the ordinary. Global variable 913 has to be 0 to
			// signify that he is in his "idle" state.
			//
			// Unfortunately, simply the act of speaking to him
			// sets variable 913 to 1 (probably to stop him from
			// turning around every now and then). The script may
			// then go on to set the variable to different values
			// to trigger various behaviours in him, but if you
			// have run out of these cases the script won't ever
			// set it back to 0 again.
			//
			// So if his click hander finishes, and variable 913 is
			// 1, we set it back to 0 manually.

			if (checkPyramidBug && readVar(913) == 1) {
				warning("Working around pyramid bug: Resetting Titipoco's state");
				writeVar(913, 0);
			}

			// WORKAROUND: The not-so-known-but-should-be-dreaded
			// elevator bug.
			//
			// The click handler for the top of the elevator only
			// handles using the elevator, not examining it. When
			// examining it, the mouse cursor is removed but never
			// restored.

			if (checkElevatorBug && readVar(RIGHT_BUTTON)) {
				warning("Working around elevator bug: Restoring mouse pointer");
				fnAddHuman(NULL);
			}

			debug(9, "CP_END_SCRIPT");
			break;
		case CP_QUIT:
			// Quit out for a cycle
			WRITE_LE_UINT32(offsetPtr, ip);
			debug(9, "CP_QUIT");
			return 0;
		case CP_TERMINATE:
			// Quit out immediately without affecting the offset
			// pointer
			debug(9, "CP_TERMINATE");
			return 3;
		case CP_RESTART_SCRIPT:
			// Start the script again
			ip = FROM_LE_32(offsetTable[scriptNumber]);
			debug(9, "CP_RESTART_SCRIPT");
			break;

		// Stack-related opcodes

		case CP_PUSH_INT32:
			// Push a long word value on to the stack
			Read32ip(parameter);
			push(parameter);
			debug(9, "CP_PUSH_INT32: %d", parameter);
			break;
		case CP_PUSH_LOCAL_VAR32:
			// Push the contents of a local variable
			Read16ip(parameter);
			push(READ_LE_UINT32(localVars + parameter));
			debug(9, "CP_PUSH_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, READ_LE_UINT32(localVars + parameter));
			break;
		case CP_PUSH_GLOBAL_VAR32:
			// Push a global variable
			Read16ip(parameter);
			push(readVar(parameter));
			debug(9, "CP_PUSH_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, readVar(parameter));
			break;
		case CP_PUSH_LOCAL_ADDR:
			// Push the address of a local variable

			// From what I understand, some scripts store data
			// (e.g. mouse pointers) in their local variable space
			// from the very beginning, and use this mechanism to
			// pass that data to the opcode function. I don't yet
			// know the conceptual difference between this and the
			// CP_PUSH_DEREFERENCED_STRUCTURE opcode.

			Read16ip(parameter);
			push_ptr(localVars + parameter);
			debug(9, "CP_PUSH_LOCAL_ADDR: &localVars[%d] => %p", parameter / 4, localVars + parameter);
			break;
		case CP_PUSH_STRING:
			// Push the address of a string on to the stack
			// Get the string size
			Read8ip(parameter);

			// ip now points to the string
			ptr = code + ip;
			push_ptr(ptr);
			debug(9, "CP_PUSH_STRING: \"%s\"", ptr);
			ip += (parameter + 1);
			break;
		case CP_PUSH_DEREFERENCED_STRUCTURE:
			// Push the address of a dereferenced structure
			Read32ip(parameter);
			ptr = objectData + 4 + ResHeader::size() + ObjectHub::size() + parameter;
			push_ptr(ptr);
			debug(9, "CP_PUSH_DEREFERENCED_STRUCTURE: %d => %p", parameter, ptr);
			break;
		case CP_POP_LOCAL_VAR32:
			// Pop a value into a local word variable
			Read16ip(parameter);
			value = pop();
			WRITE_LE_UINT32(localVars + parameter, value);
			debug(9, "CP_POP_LOCAL_VAR32: localVars[%d] = %d", parameter / 4, value);
			break;
		case CP_POP_GLOBAL_VAR32:
			// Pop a global variable
			Read16ip(parameter);
			value = pop();

			// WORKAROUND for bug #1214168: The not-at-all dreaded
			// mop bug.
			//
			// At the London Docks, global variable 1003 keeps
			// track of Nico:
			//
			// 0: Hiding behind the first crate.
			// 1: Hiding behind the second crate.
			// 2: Standing in plain view on the deck.
			// 3: Hiding on the roof.
			//
			// The bug happens when trying to pick up the mop while
			// hiding on the roof. Nico climbs down, the mop is
			// picked up, but the variable remains set to 3.
			// Visually, everything looks ok. But as far as the
			// scripts are concerned, she's still hiding up on the
			// roof. This is not fatal, but leads to a number of
			// glitches until the state is corrected. E.g. trying
			// to climb back up the ladder will cause Nico to climb
			// down again.
			//
			// Global variable 1017 keeps track of the mop. Setting
			// it to 2 means that the mop has been picked up. We
			// use that as the signal that Nico's state needs to be
			// updated as well.

			if (checkMopBug && parameter == 1017 && readVar(1003) != 2) {
				warning("Working around mop bug: Setting Nico's state");
				writeVar(1003, 2);
			}

			writeVar(parameter, value);
			debug(9, "CP_POP_GLOBAL_VAR32: scriptsVars[%d] = %d", parameter, value);
			break;
		case CP_ADDNPOP_LOCAL_VAR32:
			Read16ip(parameter);
			value = READ_LE_UINT32(localVars + parameter) + pop();
			WRITE_LE_UINT32(localVars + parameter, value);
			debug(9, "CP_ADDNPOP_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, value);
			break;
		case CP_SUBNPOP_LOCAL_VAR32:
			Read16ip(parameter);
			value = READ_LE_UINT32(localVars + parameter) - pop();
			WRITE_LE_UINT32(localVars + parameter, value);
			debug(9, "CP_SUBNPOP_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, value);
			break;
		case CP_ADDNPOP_GLOBAL_VAR32:
			// Add and pop a global variable
			Read16ip(parameter);
			value = readVar(parameter) + pop();
			writeVar(parameter, value);
			debug(9, "CP_ADDNPOP_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, value);
			break;
		case CP_SUBNPOP_GLOBAL_VAR32:
			// Sub and pop a global variable
			Read16ip(parameter);
			value = readVar(parameter) - pop();
			writeVar(parameter, value);
			debug(9, "CP_SUBNPOP_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, value);
			break;

		// Jump opcodes

		case CP_SKIPONTRUE:
			// Skip if the value on the stack is true
			Read32ipLeaveip(parameter);
			value = pop();
			if (!value) {
				ip += 4;
				debug(9, "CP_SKIPONTRUE: %d (IS FALSE (NOT SKIPPED))", parameter);
			} else {
				ip += parameter;
				debug(9, "CP_SKIPONTRUE: %d (IS TRUE (SKIPPED))", parameter);
			}
			break;
		case CP_SKIPONFALSE:
			// Skip if the value on the stack is false
			Read32ipLeaveip(parameter);
			value = pop();
			if (value) {
				ip += 4;
				debug(9, "CP_SKIPONFALSE: %d (IS TRUE (NOT SKIPPED))", parameter);
			} else {
				ip += parameter;
				debug(9, "CP_SKIPONFALSE: %d (IS FALSE (SKIPPED))", parameter);
			}
			break;
		case CP_SKIPALWAYS:
			// skip a block
			Read32ipLeaveip(parameter);
			ip += parameter;
			debug(9, "CP_SKIPALWAYS: %d", parameter);
			break;
		case CP_SWITCH:
			// switch
			value = pop();
			Read32ip(caseCount);

			// Search the cases
			foundCase = false;
			for (i = 0; i < caseCount && !foundCase; i++) {
				if (value == (int32)READ_LE_UINT32(code + ip)) {
					// We have found the case, so lets
					// jump to it
					foundCase = true;
					ip += READ_LE_UINT32(code + ip + 4);
				} else
					ip += 4 * 2;
			}

			// If we found no matching case then use the default

			if (!foundCase)
				ip += READ_LE_UINT32(code + ip);

			debug(9, "CP_SWITCH: [SORRY, NO DEBUG INFO]");
			break;
		case CP_SAVE_MCODE_START:
			// Save the start position on an mcode instruction in
			// case we need to restart it again
			savedStartOfMcode = ip - 1;
			debug(9, "CP_SAVE_MCODE_START");
			break;
		case CP_CALL_MCODE:
			// Call an mcode routine
			Read16ip(parameter);
			assert(parameter < ARRAYSIZE(opcodes));
			// amount to adjust stack by (no of parameters)
			Read8ip(value);
			debug(9, "CP_CALL_MCODE: '%s', %d", opcodes[parameter].desc, value);
			stackPtr -= value;
			assert(stackPtr >= 0);
			retVal = (this->*opcodes[parameter].proc)(&stack[stackPtr]);

			switch (retVal & 7) {
			case IR_STOP:
				// Quit out for a cycle
				WRITE_LE_UINT32(offsetPtr, ip);
				return 0;
			case IR_CONT:
				// Continue as normal
				break;
			case IR_TERMINATE:
				// Return without updating the offset
				return 2;
			case IR_REPEAT:
				// Return setting offset to start of this
				// function call
				WRITE_LE_UINT32(offsetPtr, savedStartOfMcode);
				return 0;
			case IR_GOSUB:
				// that's really neat
				WRITE_LE_UINT32(offsetPtr, ip);
				return 2;
			default:
				error("Bad return code (%d) from '%s'", retVal & 7, opcodes[parameter].desc);
			}
			parameterReturnedFromMcodeFunction = retVal >> 3;
			break;
		case CP_JUMP_ON_RETURNED:
			// Jump to a part of the script depending on
			// the return value from an mcode routine

			// Get the maximum value
			Read8ip(parameter);
			debug(9, "CP_JUMP_ON_RETURNED: %d => %d",
				parameterReturnedFromMcodeFunction,
				READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4));
			ip += READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4);
			break;

		// Operators

		case OP_ISEQUAL:
			b = pop();
			a = pop();
			push(a == b);
			debug(9, "OP_ISEQUAL: RESULT = %d", a == b);
			break;
		case OP_NOTEQUAL:
			b = pop();
			a = pop();
			push(a != b);
			debug(9, "OP_NOTEQUAL: RESULT = %d", a != b);
			break;
		case OP_GTTHAN:
			b = pop();
			a = pop();
			push(a > b);
			debug(9, "OP_GTTHAN: RESULT = %d", a > b);
			break;
		case OP_LSTHAN:
			b = pop();
			a = pop();
			push(a < b);
			debug(9, "OP_LSTHAN: RESULT = %d", a < b);
			break;
		case OP_GTTHANE:
			b = pop();
			a = pop();
			push(a >= b);
			debug(9, "OP_GTTHANE: RESULT = %d", a >= b);
			break;
		case OP_LSTHANE:
			b = pop();
			a = pop();
			push(a <= b);
			debug(9, "OP_LSTHANE: RESULT = %d", a <= b);
			break;
		case OP_PLUS:
			b = pop();
			a = pop();
			push(a + b);
			debug(9, "OP_PLUS: RESULT = %d", a + b);
			break;
		case OP_MINUS:
			b = pop();
			a = pop();
			push(a - b);
			debug(9, "OP_MINUS: RESULT = %d", a - b);
			break;
		case OP_TIMES:
			b = pop();
			a = pop();
			push(a * b);
			debug(9, "OP_TIMES: RESULT = %d", a * b);
			break;
		case OP_DIVIDE:
			b = pop();
			a = pop();
			push(a / b);
			debug(9, "OP_DIVIDE: RESULT = %d", a / b);
			break;
		case OP_ANDAND:
			b = pop();
			a = pop();
			push(a && b);
			debug(9, "OP_ANDAND: RESULT = %d", a && b);
			break;
		case OP_OROR:
			b = pop();
			a = pop();
			push(a || b);
			debug(9, "OP_OROR: RESULT = %d", a || b);
			break;

		// Debugging opcodes, I think

		case CP_DEBUGON:
			debug(9, "CP_DEBUGON");
			break;
		case CP_DEBUGOFF:
			debug(9, "CP_DEBUGOFF");
			break;
		case CP_TEMP_TEXT_PROCESS:
			Read32ip(parameter);
			debug(9, "CP_TEMP_TEXT_PROCESS: %d", parameter);
			break;
		default:
			error("Invalid script command %d", curCommand);
			return 3;
		}
	}

	return 1;
}