SCI: Add SetCursor detection. Cleanup.

svn-id: r43812
This commit is contained in:
Walter van Niftrik 2009-08-30 01:37:52 +00:00
parent fff023794f
commit cf5483c3d8
6 changed files with 137 additions and 134 deletions

View file

@ -301,82 +301,62 @@ static gfx_color_t graph_map_color(EngineState *s, int color, int priority, int
return retval;
}
reg_t kSetCursor(EngineState *s, int funct_nr, int argc, reg_t *argv) {
SciVersion version = s->resourceManager->sciVersion();
static reg_t kSetCursorSci0(EngineState *s, int funct_nr, int argc, reg_t *argv) {
uint16 cursor = argv[0].toSint16();
switch (argc) {
case 1 :
if (version < SCI_VERSION_1_LATE) {
GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, argv[0].toSint16()));
} else if (version < SCI_VERSION_1_1) {
if (argv[0].toSint16() <= 1) {
// Newer (SCI1.1) semantics: show/hide cursor
CursorMan.showMouse(argv[0].toSint16() != 0);
} else {
// Pre-SCI1.1: set cursor according to the first parameter
GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, argv[0].toSint16()));
}
} else {
// SCI1.1: Show/hide cursor
CursorMan.showMouse(argv[0].toSint16() != 0);
}
break;
case 2 :
if (version < SCI_VERSION_1_LATE) {
GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state,
argv[1].toSint16() == 0 ? GFXOP_NO_POINTER : argv[0].toSint16()));
} else if (version < SCI_VERSION_1_1) {
// Pre-SCI1.1: set cursor according to the first parameter, and toggle its
// visibility based on the second parameter
// Some late SCI1 games actually use the SCI1.1 version of this call (EcoQuest 1
// and KQ5 CD, but I haven't seen this case happen), but we can determine the
// semantics from the second parameter passed.
// Rationale: with the older behavior, the second parameter can either be 0
// (hide cursor) or 1/-1 (show cursor). This could be problematic if the engine
// tries to place the cursor at (x, 0) or (x, 1), but no SCI1 game does that, as
// this would open the menu on top. LSL5 is an exception, as the game can open
// the menu when the player presses a button during the intro, but the cursor is
// not placed on (x, 0) or (x, 1)
if (argv[1].toSint16() <= 1) {
GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state,
argv[1].toSint16() == 0 ? GFXOP_NO_POINTER : argv[0].toSint16()));
} else { // newer (SCI1.1) semantics: set pointer position
GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state,
Common::Point(argv[0].toUint16(), argv[1].toUint16())));
}
} else {
// SCI1.1 and newer: set pointer position
GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state,
Common::Point(argv[0].toUint16(), argv[1].toUint16())));
}
break;
case 4 :
GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state,
argv[0].toUint16() == 0 ? GFXOP_NO_POINTER : argv[0].toSint16()));
if ((argc >= 2) && (argv[1].toSint16() == 0))
cursor = GFXOP_NO_POINTER;
GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, cursor));
// Set pointer position, if requested
if (argc > 2) {
if (argc >= 4) {
Common::Point newPos = Common::Point(argv[2].toSint16() + s->port->_bounds.x, argv[3].toSint16() + s->port->_bounds.y);
GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, newPos));
}
return s->r_acc;
}
static reg_t kSetCursorSci11(EngineState *s, int funct_nr, int argc, reg_t *argv) {
Common::Point *hotspot = NULL;
switch (argc) {
case 1:
CursorMan.showMouse(argv[0].toSint16() != 0);
break;
case 2:
GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state,
Common::Point(argv[0].toUint16(), argv[1].toUint16())));
break;
case 3 :
case 5:
case 9:
if (argc > 3) {
Common::Point hotspot = Common::Point(argv[3].toSint16(), argv[4].toSint16());
GFX_ASSERT(gfxop_set_pointer_view(s->gfx_state, argv[0].toUint16(), argv[1].toUint16(), argv[2].toUint16(), &hotspot));
} else {
GFX_ASSERT(gfxop_set_pointer_view(s->gfx_state, argv[0].toUint16(), argv[1].toUint16(), argv[2].toUint16(), NULL));
}
hotspot = new Common::Point(argv[3].toSint16(), argv[4].toSint16());
// Fallthrough
case 3:
GFX_ASSERT(gfxop_set_pointer_view(s->gfx_state, argv[0].toUint16(), argv[1].toUint16(), argv[2].toUint16(), hotspot));
if (hotspot)
delete hotspot;
break;
default :
error("kSetCursor: Unhandled case: %d arguments given", argc);
warning("kSetCursor: Unhandled case: %d arguments given", argc);
break;
}
return s->r_acc;
}
reg_t kSetCursor(EngineState *s, int funct_nr, int argc, reg_t *argv) {
switch (s->detectSetCursorType()) {
case SCI_VERSION_0_EARLY:
return kSetCursorSci0(s, funct_nr, argc, argv);
case SCI_VERSION_1_1:
return kSetCursorSci11(s, funct_nr, argc, argv);
default:
warning("Unknown SetCursor type");
return NULL_REG;
}
}
reg_t kMoveCursor(EngineState *s, int funct_nr, int argc, reg_t *argv) {
Common::Point newPos;

View file

@ -206,7 +206,7 @@ void process_sound_events(EngineState *s) { /* Get all sound events, apply their
}
reg_t kDoSoundSci0(EngineState *s, int funct_nr, int argc, reg_t *argv) {
static reg_t kDoSoundSci0(EngineState *s, int funct_nr, int argc, reg_t *argv) {
SegManager *segManager = s->segmentManager;
reg_t obj = (argc > 1) ? argv[1] : NULL_REG;
uint16 command = argv[0].toUint16();
@ -386,7 +386,7 @@ reg_t kDoSoundSci0(EngineState *s, int funct_nr, int argc, reg_t *argv) {
}
reg_t kDoSoundSci1Early(EngineState *s, int funct_nr, int argc, reg_t *argv) {
static reg_t kDoSoundSci1Early(EngineState *s, int funct_nr, int argc, reg_t *argv) {
SegManager *segManager = s->segmentManager;
uint16 command = argv[0].toUint16();
reg_t obj = (argc > 1) ? argv[1] : NULL_REG;
@ -677,7 +677,7 @@ reg_t kDoSoundSci1Early(EngineState *s, int funct_nr, int argc, reg_t *argv) {
return s->r_acc;
}
reg_t kDoSoundSci1Late(EngineState *s, int funct_nr, int argc, reg_t *argv) {
static reg_t kDoSoundSci1Late(EngineState *s, int funct_nr, int argc, reg_t *argv) {
SegManager *segManager = s->segmentManager;
uint16 command = argv[0].toUint16();
reg_t obj = (argc > 1) ? argv[1] : NULL_REG;
@ -994,11 +994,11 @@ reg_t kDoSoundSci1Late(EngineState *s, int funct_nr, int argc, reg_t *argv) {
*/
reg_t kDoSound(EngineState *s, int funct_nr, int argc, reg_t *argv) {
switch(s->detectDoSoundType()) {
case EngineState::kDoSoundTypeSci0:
case SCI_VERSION_0_EARLY:
return kDoSoundSci0(s, funct_nr, argc, argv);
case EngineState::kDoSoundTypeSci1Early:
case SCI_VERSION_1_EARLY:
return kDoSoundSci1Early(s, funct_nr, argc, argv);
case EngineState::kDoSoundTypeSci1Late:
case SCI_VERSION_1_LATE:
return kDoSoundSci1Late(s, funct_nr, argc, argv);
default:
warning("Unknown DoSound type");

View file

@ -241,6 +241,7 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(parseLang);
FIND_SELECTOR(motionCue);
FIND_SELECTOR(egoMoveSpeed);
FIND_SELECTOR(setCursor);
}
void Kernel::dumpScriptObject(char *data, int seeker, int objsize) {

View file

@ -119,7 +119,8 @@ EngineState::EngineState(ResourceManager *res, uint32 flags)
speedThrottler = new SpeedThrottler(res->sciVersion());
_doSoundType = kDoSoundTypeUnknown;
_setCursorType = SCI_VERSION_AUTODETECT;
_doSoundType = SCI_VERSION_AUTODETECT;
}
EngineState::~EngineState() {
@ -246,78 +247,97 @@ Common::String EngineState::strSplit(const char *str, const char *sep) {
return retval;
}
EngineState::DoSoundType EngineState::detectDoSoundType() {
if (_doSoundType == kDoSoundTypeUnknown) {
reg_t soundClass;
const uint checkBytes = 6; // Number of bytes to check
if (!parse_reg_t(this, "?Sound", &soundClass)) {
int EngineState::methodChecksum(reg_t objAddress, Selector sel, int offset, uint size) const {
reg_t fptr;
Object *obj = obj_get(segmentManager, soundClass);
SelectorType sel = lookup_selector(this->segmentManager, soundClass, ((SciEngine*)g_engine)->getKernel()->_selectorMap.play, NULL, &fptr);
Object *obj = obj_get(segmentManager, objAddress);
SelectorType selType = lookup_selector(this->segmentManager, objAddress, sel, NULL, &fptr);
if (!obj || (selType != kSelectorMethod))
return -1;
if (obj && (sel == kSelectorMethod)) {
Script *script = segmentManager->getScript(fptr.segment);
if (fptr.offset > checkBytes) {
// Go to the last portion of Sound::init, should be right before the play function
fptr.offset -= checkBytes;
if (!script->buf || (fptr.offset + offset < 0))
return -1;
fptr.offset += offset;
if (fptr.offset + size > script->buf_size)
return -1;
byte *buf = script->buf + fptr.offset;
// Check the call to DoSound's INIT_HANDLE function.
// It's either subfunction 0, 5 or 6, depending on the version of DoSound.
uint sum = 0;
for (uint i = 0; i < checkBytes; i++)
for (uint i = 0; i < size; i++)
sum += buf[i];
return sum;
}
SciVersion EngineState::detectDoSoundType() {
if (_doSoundType == SCI_VERSION_AUTODETECT) {
reg_t soundClass;
if (!parse_reg_t(this, "?Sound", &soundClass)) {
int sum = methodChecksum(soundClass, ((SciEngine *)g_engine)->getKernel()->_selectorMap.play, -6, 6);
switch(sum) {
case 0x1B2: // SCI0
case 0x1AE: // SCI01
_doSoundType = kDoSoundTypeSci0;
_doSoundType = SCI_VERSION_0_EARLY;
break;
case 0x13D:
_doSoundType = kDoSoundTypeSci1Early;
_doSoundType = SCI_VERSION_1_EARLY;
break;
case 0x13E:
#ifdef ENABLE_SCI32
case 0x14B:
#endif
_doSoundType = kDoSoundTypeSci1Late;
}
}
_doSoundType = SCI_VERSION_1_LATE;
}
}
if (_doSoundType == kDoSoundTypeUnknown) {
if (_doSoundType == SCI_VERSION_AUTODETECT) {
warning("DoSound detection failed, taking an educated guess");
if (resourceManager->sciVersion() >= SCI_VERSION_1_MIDDLE)
_doSoundType = kDoSoundTypeSci1Late;
_doSoundType = SCI_VERSION_1_LATE;
else if (resourceManager->sciVersion() > SCI_VERSION_01)
_doSoundType = kDoSoundTypeSci1Early;
_doSoundType = SCI_VERSION_1_EARLY;
else
_doSoundType = kDoSoundTypeSci0;
_doSoundType = SCI_VERSION_0_EARLY;
}
debugCN(1, kDebugLevelSound, "Detected DoSound type: ");
switch(_doSoundType) {
case kDoSoundTypeSci0:
debugC(1, kDebugLevelSound, "SCI0");
break;
case kDoSoundTypeSci1Early:
debugC(1, kDebugLevelSound, "SCI1 Early");
break;
case kDoSoundTypeSci1Late:
debugC(1, kDebugLevelSound, "SCI1 Late");
break;
default:
break;
}
debugC(1, kDebugLevelSound, "Detected DoSound type: %s", ((SciEngine *)g_engine)->getSciVersionDesc(_doSoundType).c_str());
}
return _doSoundType;
}
SciVersion EngineState::detectSetCursorType() {
if (_setCursorType == SCI_VERSION_AUTODETECT) {
int sum = methodChecksum(game_obj, ((SciEngine *)g_engine)->getKernel()->_selectorMap.setCursor, 0, 21);
if ((sum == 0x4D5) || (sum == 0x552)) {
// Standard setCursor
_setCursorType = SCI_VERSION_0_EARLY;
} else if (sum != -1) {
// Assume that others use fancy cursors
_setCursorType = SCI_VERSION_1_1;
} else {
warning("SetCursor detection failed, taking an educated guess");
if (resourceManager->sciVersion() >= SCI_VERSION_1_1)
_setCursorType = SCI_VERSION_1_1;
else
_setCursorType = SCI_VERSION_0_EARLY;
}
debugC(0, kDebugLevelGraphics, "Detected SetCursor type: %s", ((SciEngine *)g_engine)->getSciVersionDesc(_setCursorType).c_str());
}
return _setCursorType;
}
} // End of namespace Sci

View file

@ -164,13 +164,6 @@ public:
EngineState(ResourceManager *res, uint32 flags);
virtual ~EngineState();
enum DoSoundType {
kDoSoundTypeUnknown,
kDoSoundTypeSci0,
kDoSoundTypeSci1Early,
kDoSoundTypeSci1Late
};
virtual void saveLoadWithSerializer(Common::Serializer &ser);
kLanguage getLanguage();
@ -283,7 +276,13 @@ public:
* Autodetects the DoSound type
* @return DoSound type
*/
DoSoundType detectDoSoundType();
SciVersion detectDoSoundType();
/**
* Autodetects the SetCursor type
* @return SetCursor type
*/
SciVersion detectSetCursorType();
/* Debugger data: */
Breakpoint *bp_list; /**< List of breakpoints */
@ -315,8 +314,9 @@ public:
Common::String getLanguageString(const char *str, kLanguage lang) const;
private:
DoSoundType _doSoundType;
SciVersion _doSoundType, _setCursorType;
kLanguage charToLanguage(const char c) const;
int methodChecksum(reg_t objAddress, Selector sel, int offset, uint size) const;
};
/**

View file

@ -200,6 +200,8 @@ struct selector_map_t {
Selector printLang; /**< Used for i18n */
Selector subtitleLang;
Selector parseLang;
Selector setCursor; /** For autodetection */
};
// A reference to an object's variable.