A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 785 lines 26 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2014 Franklin Wei, Benjamin Brown 11 * Copyright (C) 2004 Gregory Montoir 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License 15 * as published by the Free Software Foundation; either version 2 16 * of the License, or (at your option) any later version. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22 23#include "plugin.h" 24#include "vm.h" 25#include "mixer.h" 26#include "resource.h" 27#include "video.h" 28#include "serializer.h" 29#include "sfxplayer.h" 30#include "sys.h" 31#include "parts.h" 32#include "file.h" 33 34static const uint16_t vm_frequenceTable[] = { 35 0x0CFF, 0x0DC3, 0x0E91, 0x0F6F, 0x1056, 0x114E, 0x1259, 0x136C, 36 0x149F, 0x15D9, 0x1726, 0x1888, 0x19FD, 0x1B86, 0x1D21, 0x1EDE, 37 0x20AB, 0x229C, 0x24B3, 0x26D7, 0x293F, 0x2BB2, 0x2E4C, 0x3110, 38 0x33FB, 0x370D, 0x3A43, 0x3DDF, 0x4157, 0x4538, 0x4998, 0x4DAE, 39 0x5240, 0x5764, 0x5C9A, 0x61C8, 0x6793, 0x6E19, 0x7485, 0x7BBD 40}; 41 42void vm_create(struct VirtualMachine* m, struct Mixer *mix, struct Resource* res, struct SfxPlayer *ply, struct Video *vid, struct System *stub) 43{ 44 m->res = res; 45 m->video = vid; 46 m->sys = stub; 47 m->mixer = mix; 48 m->player = ply; 49} 50 51void vm_init(struct VirtualMachine* m) { 52 53 rb->memset(m->vmVariables, 0, sizeof(m->vmVariables)); 54 m->vmVariables[0x54] = 0x81; 55 m->vmVariables[VM_VARIABLE_RANDOM_SEED] = *rb->current_tick % 0x10000; 56 57 /* rawgl has these, but they don't seem to do anything */ 58 //m->vmVariables[0xBC] = 0x10; 59 //m->vmVariables[0xC6] = 0x80; 60 //m->vmVariables[0xF2] = 4000; 61 //m->vmVariables[0xDC] = 33; 62 63 m->_fastMode = false; 64 m->player->_markVar = &m->vmVariables[VM_VARIABLE_MUS_MARK]; 65} 66 67void vm_op_movConst(struct VirtualMachine* m) { 68 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr); 69 int16_t value = scriptPtr_fetchWord(&m->_scriptPtr); 70 debug(DBG_VM, "vm_op_movConst(0x%02X, %d)", variableId, value); 71 m->vmVariables[variableId] = value; 72} 73 74void vm_op_mov(struct VirtualMachine* m) { 75 uint8_t dstVariableId = scriptPtr_fetchByte(&m->_scriptPtr); 76 uint8_t srcVariableId = scriptPtr_fetchByte(&m->_scriptPtr); 77 debug(DBG_VM, "vm_op_mov(0x%02X, 0x%02X)", dstVariableId, srcVariableId); 78 m->vmVariables[dstVariableId] = m->vmVariables[srcVariableId]; 79} 80 81void vm_op_add(struct VirtualMachine* m) { 82 uint8_t dstVariableId = scriptPtr_fetchByte(&m->_scriptPtr); 83 uint8_t srcVariableId = scriptPtr_fetchByte(&m->_scriptPtr); 84 debug(DBG_VM, "vm_op_add(0x%02X, 0x%02X)", dstVariableId, srcVariableId); 85 m->vmVariables[dstVariableId] += m->vmVariables[srcVariableId]; 86} 87 88void vm_op_addConst(struct VirtualMachine* m) { 89 if (m->res->currentPartId == 0x3E86 && m->_scriptPtr.pc == m->res->segBytecode + 0x6D48) { 90 //warning("vm_op_addConst() hack for non-stop looping gun sound bug"); 91 // the script 0x27 slot 0x17 doesn't stop the gun sound from looping, I 92 // don't really know why ; for now, let's play the 'stopping sound' like 93 // the other scripts do 94 // (0x6D43) jmp(0x6CE5) 95 // (0x6D46) break 96 // (0x6D47) VAR(6) += -50 97 vm_snd_playSound(m, 0x5B, 1, 64, 1); 98 } 99 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr); 100 int16_t value = scriptPtr_fetchWord(&m->_scriptPtr); 101 debug(DBG_VM, "vm_op_addConst(0x%02X, %d)", variableId, value); 102 m->vmVariables[variableId] += value; 103} 104 105void vm_op_call(struct VirtualMachine* m) { 106 107 uint16_t offset = scriptPtr_fetchWord(&m->_scriptPtr); 108 uint8_t sp = m->_stackPtr; 109 110 debug(DBG_VM, "vm_op_call(0x%X)", offset); 111 m->_scriptStackCalls[sp] = m->_scriptPtr.pc - m->res->segBytecode; 112 if (m->_stackPtr == 0xFF) { 113 error("vm_op_call() ec=0x%X stack overflow", 0x8F); 114 } 115 ++m->_stackPtr; 116 m->_scriptPtr.pc = m->res->segBytecode + offset ; 117} 118 119void vm_op_ret(struct VirtualMachine* m) { 120 debug(DBG_VM, "vm_op_ret()"); 121 if (m->_stackPtr == 0) { 122 error("vm_op_ret() ec=0x%X stack underflow", 0x8F); 123 } 124 --m->_stackPtr; 125 uint8_t sp = m->_stackPtr; 126 m->_scriptPtr.pc = m->res->segBytecode + m->_scriptStackCalls[sp]; 127} 128 129void vm_op_pauseThread(struct VirtualMachine* m) { 130 debug(DBG_VM, "vm_op_pauseThread()"); 131 m->gotoNextThread = true; 132} 133 134void vm_op_jmp(struct VirtualMachine* m) { 135 uint16_t pcOffset = scriptPtr_fetchWord(&m->_scriptPtr); 136 debug(DBG_VM, "vm_op_jmp(0x%02X)", pcOffset); 137 m->_scriptPtr.pc = m->res->segBytecode + pcOffset; 138} 139 140void vm_op_setSetVect(struct VirtualMachine* m) { 141 uint8_t threadId = scriptPtr_fetchByte(&m->_scriptPtr); 142 uint16_t pcOffsetRequested = scriptPtr_fetchWord(&m->_scriptPtr); 143 debug(DBG_VM, "vm_op_setSetVect(0x%X, 0x%X)", threadId, pcOffsetRequested); 144 m->threadsData[REQUESTED_PC_OFFSET][threadId] = pcOffsetRequested; 145} 146 147void vm_op_jnz(struct VirtualMachine* m) { 148 uint8_t i = scriptPtr_fetchByte(&m->_scriptPtr); 149 debug(DBG_VM, "vm_op_jnz(0x%02X)", i); 150 --m->vmVariables[i]; 151 if (m->vmVariables[i] != 0) { 152 vm_op_jmp(m); 153 } else { 154 scriptPtr_fetchWord(&m->_scriptPtr); 155 } 156} 157 158#define BYPASS_PROTECTION 159void vm_op_condJmp(struct VirtualMachine* m) { 160 161 //debug(DBG_VM, "Jump : %X \n",m->_scriptPtr.pc-m->res->segBytecode); 162//FCS Whoever wrote this is patching the bytecode on the fly. This is ballzy !! 163#if 0 164 if (m->res->currentPartId == GAME_PART_FIRST && m->_scriptPtr.pc == m->res->segBytecode + 0xCB9) { 165 166 // (0x0CB8) condJmp(0x80, VAR(41), VAR(30), 0xCD3) 167 *(m->_scriptPtr.pc + 0x00) = 0x81; 168 *(m->_scriptPtr.pc + 0x03) = 0x0D; 169 *(m->_scriptPtr.pc + 0x04) = 0x24; 170 // (0x0D4E) condJmp(0x4, VAR(50), 6, 0xDBC) 171 *(m->_scriptPtr.pc + 0x99) = 0x0D; 172 *(m->_scriptPtr.pc + 0x9A) = 0x5A; 173 debug(DBG_VM, "vm_op_condJmp() bypassing protection"); 174 debug(DBG_VM, "bytecode has been patched"); 175 176 //warning("bypassing protection"); 177 178 //vm_bypassProtection(m); 179 } 180 181 182#endif 183 184 uint8_t opcode = scriptPtr_fetchByte(&m->_scriptPtr); 185 uint8_t var = scriptPtr_fetchByte(&m->_scriptPtr); 186 int16_t b = m->vmVariables[var]; 187 uint8_t c = scriptPtr_fetchByte(&m->_scriptPtr); 188 int16_t a; 189 190 if (opcode & 0x80) { 191 a = m->vmVariables[c]; 192 } else if (opcode & 0x40) { 193 a = c * 256 + scriptPtr_fetchByte(&m->_scriptPtr); 194 } else { 195 a = c; 196 } 197 debug(DBG_VM, "vm_op_condJmp(%d, 0x%02X, 0x%02X)", opcode, b, a); 198 199 // Check if the conditional value is met. 200 bool expr = false; 201 switch (opcode & 7) { 202 case 0: // jz 203 expr = (b == a); 204 205#ifdef BYPASS_PROTECTION 206 /* always succeed in code wheel verification */ 207 if (m->res->currentPartId == GAME_PART_FIRST && var == 0x29 && (opcode & 0x80) != 0) { 208 209 m->vmVariables[0x29] = m->vmVariables[0x1E]; 210 m->vmVariables[0x2A] = m->vmVariables[0x1F]; 211 m->vmVariables[0x2B] = m->vmVariables[0x20]; 212 m->vmVariables[0x2C] = m->vmVariables[0x21]; 213 // counters 214 m->vmVariables[0x32] = 6; 215 m->vmVariables[0x64] = 20; 216 expr = true; 217 //warning("Script::op_condJmp() bypassing protection"); 218 } 219#endif 220 break; 221 case 1: // jnz 222 expr = (b != a); 223 break; 224 case 2: // jg 225 expr = (b > a); 226 break; 227 case 3: // jge 228 expr = (b >= a); 229 break; 230 case 4: // jl 231 expr = (b < a); 232 break; 233 case 5: // jle 234 expr = (b <= a); 235 break; 236 default: 237 warning("vm_op_condJmp() invalid condition %d", (opcode & 7)); 238 break; 239 } 240 241 if (expr) { 242 vm_op_jmp(m); 243 } else { 244 scriptPtr_fetchWord(&m->_scriptPtr); 245 } 246 247} 248 249void vm_op_setPalette(struct VirtualMachine* m) { 250 uint16_t paletteId = scriptPtr_fetchWord(&m->_scriptPtr); 251 debug(DBG_VM, "vm_op_changePalette(%d)", paletteId); 252 m->video->paletteIdRequested = paletteId >> 8; 253} 254 255void vm_op_resetThread(struct VirtualMachine* m) { 256 257 uint8_t threadId = scriptPtr_fetchByte(&m->_scriptPtr); 258 uint8_t i = scriptPtr_fetchByte(&m->_scriptPtr); 259 260 // FCS: WTF, this is cryptic as hell !! 261 // int8_t n = (i & 0x3F) - threadId; //0x3F = 0011 1111 262 // The following is so much clearer 263 264 //Make sure i is within [0-VM_NUM_THREADS-1] 265 i = i & (VM_NUM_THREADS - 1) ; 266 int8_t n = i - threadId; 267 268 if (n < 0) { 269 warning("vm_op_m->resetThread() ec=0x%X (n < 0)", 0x880); 270 return; 271 } 272 ++n; 273 uint8_t a = scriptPtr_fetchByte(&m->_scriptPtr); 274 275 debug(DBG_VM, "vm_op_m->resetThread(%d, %d, %d)", threadId, i, a); 276 277 if (a == 2) { 278 uint16_t *p = &m->threadsData[REQUESTED_PC_OFFSET][threadId]; 279 while (n--) { 280 *p++ = 0xFFFE; 281 } 282 } else if (a < 2) { 283 uint8_t *p = &m->vmIsChannelActive[REQUESTED_STATE][threadId]; 284 while (n--) { 285 *p++ = a; 286 } 287 } 288} 289 290void vm_op_selectVideoPage(struct VirtualMachine* m) { 291 uint8_t frameBufferId = scriptPtr_fetchByte(&m->_scriptPtr); 292 debug(DBG_VM, "vm_op_selectVideoPage(%d)", frameBufferId); 293 video_changePagePtr1(m->video, frameBufferId); 294} 295 296void vm_op_fillVideoPage(struct VirtualMachine* m) { 297 uint8_t pageId = scriptPtr_fetchByte(&m->_scriptPtr); 298 uint8_t color = scriptPtr_fetchByte(&m->_scriptPtr); 299 debug(DBG_VM, "vm_op_fillVideoPage(%d, %d)", pageId, color); 300 video_fillPage(m->video, pageId, color); 301} 302 303void vm_op_copyVideoPage(struct VirtualMachine* m) { 304 uint8_t srcPageId = scriptPtr_fetchByte(&m->_scriptPtr); 305 uint8_t dstPageId = scriptPtr_fetchByte(&m->_scriptPtr); 306 debug(DBG_VM, "vm_op_copyVideoPage(%d, %d)", srcPageId, dstPageId); 307 video_copyPage(m->video, srcPageId, dstPageId, m->vmVariables[VM_VARIABLE_SCROLL_Y]); 308} 309 310 311static uint32_t lastTimeStamp = 0; 312void vm_op_blitFramebuffer(struct VirtualMachine* m) { 313 314 uint8_t pageId = scriptPtr_fetchByte(&m->_scriptPtr); 315 debug(DBG_VM, "vm_op_blitFramebuffer(%d)", pageId); 316 vm_inp_handleSpecialKeys(m); 317 318 /* Nasty hack....was this present in the original assembly ??!! */ 319 if (m->res->currentPartId == GAME_PART_FIRST && m->vmVariables[0x67] == 1) 320 m->vmVariables[0xDC] = 0x21; 321 322 if (!m->_fastMode) { 323 324 int32_t delay = sys_getTimeStamp(m->sys) - lastTimeStamp; 325 int32_t timeToSleep = m->vmVariables[VM_VARIABLE_PAUSE_SLICES] * 20 - delay; 326 327 /* The bytecode will set m->vmVariables[VM_VARIABLE_PAUSE_SLICES] from 1 to 5 */ 328 /* The virtual machine hence indicates how long the image should be displayed. */ 329 330 if (timeToSleep > 0) 331 { 332 sys_sleep(m->sys, timeToSleep); 333 } 334 335 lastTimeStamp = sys_getTimeStamp(m->sys); 336 } 337 338 /* WTF ? */ 339 m->vmVariables[0xF7] = 0; 340 341 video_updateDisplay(m->video, pageId); 342} 343 344void vm_op_killThread(struct VirtualMachine* m) { 345 debug(DBG_VM, "vm_op_killThread()"); 346 m->_scriptPtr.pc = m->res->segBytecode + 0xFFFF; 347 m->gotoNextThread = true; 348} 349 350void vm_op_drawString(struct VirtualMachine* m) { 351 uint16_t stringId = scriptPtr_fetchWord(&m->_scriptPtr); 352 uint16_t x = scriptPtr_fetchByte(&m->_scriptPtr); 353 uint16_t y = scriptPtr_fetchByte(&m->_scriptPtr); 354 uint16_t color = scriptPtr_fetchByte(&m->_scriptPtr); 355 356 debug(DBG_VM, "vm_op_drawString(0x%03X, %d, %d, %d)", stringId, x, y, color); 357 358 video_drawString(m->video, color, x, y, stringId); 359} 360 361void vm_op_sub(struct VirtualMachine* m) { 362 uint8_t i = scriptPtr_fetchByte(&m->_scriptPtr); 363 uint8_t j = scriptPtr_fetchByte(&m->_scriptPtr); 364 debug(DBG_VM, "vm_op_sub(0x%02X, 0x%02X)", i, j); 365 m->vmVariables[i] -= m->vmVariables[j]; 366} 367 368void vm_op_and(struct VirtualMachine* m) { 369 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr); 370 uint16_t n = scriptPtr_fetchWord(&m->_scriptPtr); 371 debug(DBG_VM, "vm_op_and(0x%02X, %d)", variableId, n); 372 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] & n; 373} 374 375void vm_op_or(struct VirtualMachine* m) { 376 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr); 377 uint16_t value = scriptPtr_fetchWord(&m->_scriptPtr); 378 debug(DBG_VM, "vm_op_or(0x%02X, %d)", variableId, value); 379 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] | value; 380} 381 382void vm_op_shl(struct VirtualMachine* m) { 383 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr); 384 uint16_t leftShiftValue = scriptPtr_fetchWord(&m->_scriptPtr); 385 debug(DBG_VM, "vm_op_shl(0x%02X, %d)", variableId, leftShiftValue); 386 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] << leftShiftValue; 387} 388 389void vm_op_shr(struct VirtualMachine* m) { 390 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr); 391 uint16_t rightShiftValue = scriptPtr_fetchWord(&m->_scriptPtr); 392 debug(DBG_VM, "vm_op_shr(0x%02X, %d)", variableId, rightShiftValue); 393 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] >> rightShiftValue; 394} 395 396void vm_op_playSound(struct VirtualMachine* m) { 397 uint16_t resourceId = scriptPtr_fetchWord(&m->_scriptPtr); 398 uint8_t freq = scriptPtr_fetchByte(&m->_scriptPtr); 399 uint8_t vol = scriptPtr_fetchByte(&m->_scriptPtr); 400 uint8_t channel = scriptPtr_fetchByte(&m->_scriptPtr); 401 debug(DBG_VM, "vm_op_playSound(0x%X, %d, %d, %d)", resourceId, freq, vol, channel); 402 vm_snd_playSound(m, resourceId, freq, vol, channel); 403} 404 405void vm_op_updateMemList(struct VirtualMachine* m) { 406 407 uint16_t resourceId = scriptPtr_fetchWord(&m->_scriptPtr); 408 debug(DBG_VM, "vm_op_updateMemList(%d)", resourceId); 409 410 if (resourceId == 0) { 411 player_stop(m->player); 412 mixer_stopAll(m->mixer); 413 res_invalidateRes(m->res); 414 } else { 415 res_loadPartsOrMemoryEntry(m->res, resourceId); 416 } 417} 418 419void vm_op_playMusic(struct VirtualMachine* m) { 420 uint16_t resNum = scriptPtr_fetchWord(&m->_scriptPtr); 421 uint16_t delay = scriptPtr_fetchWord(&m->_scriptPtr); 422 uint8_t pos = scriptPtr_fetchByte(&m->_scriptPtr); 423 debug(DBG_VM, "vm_op_playMusic(0x%X, %d, %d)", resNum, delay, pos); 424 vm_snd_playMusic(m, resNum, delay, pos); 425} 426 427void vm_initForPart(struct VirtualMachine* m, uint16_t partId) { 428 429 player_stop(m->player); 430 mixer_stopAll(m->mixer); 431 432 /* WTF is that ? */ 433 m->vmVariables[0xE4] = 0x14; 434 435 res_setupPart(m->res, partId); 436 437 /* Set all thread to inactive (pc at 0xFFFF or 0xFFFE ) */ 438 rb->memset((uint8_t *)m->threadsData, 0xFF, sizeof(m->threadsData)); 439 440 rb->memset((uint8_t *)m->vmIsChannelActive, 0, sizeof(m->vmIsChannelActive)); 441 442 int firstThreadId = 0; 443 m->threadsData[PC_OFFSET][firstThreadId] = 0; 444} 445 446/* 447 This is called every frames in the infinite loop. 448*/ 449void vm_checkThreadRequests(struct VirtualMachine* m) { 450 451 /* Check if a part switch has been requested. */ 452 if (m->res->requestedNextPart != 0) { 453 vm_initForPart(m, m->res->requestedNextPart); 454 m->res->requestedNextPart = 0; 455 } 456 457 458 /* Check if a state update has been requested for any thread during the previous VM execution: */ 459 /* - Pause */ 460 /* - Jump */ 461 462 /* JUMP: */ 463 /* Note: If a jump has been requested, the jump destination is stored */ 464 /* in m->threadsData[REQUESTED_PC_OFFSET]. Otherwise m->threadsData[REQUESTED_PC_OFFSET] == 0xFFFF */ 465 466 /* PAUSE: */ 467 /* Note: If a pause has been requested it is stored in m->vmIsChannelActive[REQUESTED_STATE][i] */ 468 469 for (int threadId = 0; threadId < VM_NUM_THREADS; threadId++) { 470 471 m->vmIsChannelActive[CURR_STATE][threadId] = m->vmIsChannelActive[REQUESTED_STATE][threadId]; 472 473 uint16_t n = m->threadsData[REQUESTED_PC_OFFSET][threadId]; 474 475 if (n != VM_NO_SETVEC_REQUESTED) { 476 477 m->threadsData[PC_OFFSET][threadId] = (n == 0xFFFE) ? VM_INACTIVE_THREAD : n; 478 m->threadsData[REQUESTED_PC_OFFSET][threadId] = VM_NO_SETVEC_REQUESTED; 479 } 480 } 481} 482 483void vm_hostFrame(struct VirtualMachine* m) { 484 485 /* Run the Virtual Machine for every active threads (one vm frame). */ 486 /* Inactive threads are marked with a thread instruction pointer set to 0xFFFF (VM_INACTIVE_THREAD). */ 487 /* A thread must feature a break opcode so the interpreter can move to the next thread. */ 488 489 for (int threadId = 0; threadId < VM_NUM_THREADS; threadId++) { 490 491 if (m->vmIsChannelActive[CURR_STATE][threadId]) 492 continue; 493 494 uint16_t n = m->threadsData[PC_OFFSET][threadId]; 495 496 if (n != VM_INACTIVE_THREAD) { 497 498 /* Set the script pointer to the right location. */ 499 /* script pc is used in executeThread in order */ 500 /* to get the next opcode. */ 501 m->_scriptPtr.pc = m->res->segBytecode + n; 502 m->_stackPtr = 0; 503 504 m->gotoNextThread = false; 505 debug(DBG_VM, "vm_hostFrame() i=0x%02X n=0x%02X *p=0x%02X", threadId, n, *m->_scriptPtr.pc); 506 vm_executeThread(m); 507 508 /* Since .pc is going to be modified by this next loop iteration, we need to save it. */ 509 m->threadsData[PC_OFFSET][threadId] = m->_scriptPtr.pc - m->res->segBytecode; 510 511 512 debug(DBG_VM, "vm_hostFrame() i=0x%02X pos=0x%X", threadId, m->threadsData[PC_OFFSET][threadId]); 513 if (m->sys->input.quit) { 514 break; 515 } 516 } 517 } 518} 519 520#define COLOR_BLACK 0xFF 521#define DEFAULT_ZOOM 0x40 522 523 524void vm_executeThread(struct VirtualMachine* m) { 525 526 while (!m->gotoNextThread) { 527 uint8_t opcode = scriptPtr_fetchByte(&m->_scriptPtr); 528 529 /* 1000 0000 is set */ 530 if (opcode & 0x80) 531 { 532 uint16_t off = ((opcode << 8) | scriptPtr_fetchByte(&m->_scriptPtr)) * 2; 533 m->res->_useSegVideo2 = false; 534 int16_t x = scriptPtr_fetchByte(&m->_scriptPtr); 535 int16_t y = scriptPtr_fetchByte(&m->_scriptPtr); 536 int16_t h = y - 199; 537 if (h > 0) { 538 y = 199; 539 x += h; 540 } 541 debug(DBG_VIDEO, "vid_opcd_0x80 : opcode=0x%X off=0x%X x=%d y=%d", opcode, off, x, y); 542 543 /* This switch the polygon database to "cinematic" and probably draws a black polygon */ 544 /* over all the screen. */ 545 video_setDataBuffer(m->video, m->res->segCinematic, off); 546 struct Point temp; 547 temp.x = x; 548 temp.y = y; 549 video_readAndDrawPolygon(m->video, COLOR_BLACK, DEFAULT_ZOOM, &temp); 550 551 continue; 552 } 553 554 /* 0100 0000 is set */ 555 if (opcode & 0x40) 556 { 557 int16_t x, y; 558 uint16_t off = scriptPtr_fetchWord(&m->_scriptPtr) * 2; 559 x = scriptPtr_fetchByte(&m->_scriptPtr); 560 561 m->res->_useSegVideo2 = false; 562 563 if (!(opcode & 0x20)) 564 { 565 if (!(opcode & 0x10)) /* 0001 0000 is set */ 566 { 567 x = (x << 8) | scriptPtr_fetchByte(&m->_scriptPtr); 568 } else { 569 x = m->vmVariables[x]; 570 } 571 } 572 else 573 { 574 if (opcode & 0x10) { /* 0001 0000 is set */ 575 x += 0x100; 576 } 577 } 578 579 y = scriptPtr_fetchByte(&m->_scriptPtr); 580 581 if (!(opcode & 8)) /* 0000 1000 is set */ 582 { 583 if (!(opcode & 4)) { /* 0000 0100 is set */ 584 y = (y << 8) | scriptPtr_fetchByte(&m->_scriptPtr); 585 } else { 586 y = m->vmVariables[y]; 587 } 588 } 589 590 uint16_t zoom = scriptPtr_fetchByte(&m->_scriptPtr); 591 592 if (!(opcode & 2)) /* 0000 0010 is set */ 593 { 594 if (!(opcode & 1)) /* 0000 0001 is set */ 595 { 596 --m->_scriptPtr.pc; 597 zoom = 0x40; 598 } 599 else 600 { 601 zoom = m->vmVariables[zoom]; 602 } 603 } 604 else 605 { 606 607 if (opcode & 1) { /* 0000 0001 is set */ 608 m->res->_useSegVideo2 = true; 609 --m->_scriptPtr.pc; 610 zoom = 0x40; 611 } 612 } 613 debug(DBG_VIDEO, "vid_opcd_0x40 : off=0x%X x=%d y=%d", off, x, y); 614 video_setDataBuffer(m->video, m->res->_useSegVideo2 ? m->res->_segVideo2 : m->res->segCinematic, off); 615 struct Point temp; 616 temp.x = x; 617 temp.y = y; 618 video_readAndDrawPolygon(m->video, 0xFF, zoom, &temp); 619 620 continue; 621 } 622 623 624 if (opcode > 0x1A) 625 { 626 error("vm_executeThread() ec=0x%X invalid opcode=0x%X", 0xFFF, opcode); 627 } 628 else 629 { 630 (vm_opcodeTable[opcode])(m); 631 } 632 } 633} 634 635void vm_inp_updatePlayer(struct VirtualMachine* m) { 636 637 sys_processEvents(m->sys); 638 639 if (m->res->currentPartId == 0x3E89) { 640 char c = m->sys->input.lastChar; 641 if (c == 8 || /*c == 0xD |*/ c == 0 || (c >= 'a' && c <= 'z')) { 642 m->vmVariables[VM_VARIABLE_LAST_KEYCHAR] = c & ~0x20; 643 m->sys->input.lastChar = 0; 644 } 645 } 646 647 int16_t lr = 0; 648 int16_t mask = 0; 649 int16_t ud = 0; 650 651 if (m->sys->input.dirMask & DIR_RIGHT) { 652 lr = 1; 653 mask |= 1; 654 } 655 if (m->sys->input.dirMask & DIR_LEFT) { 656 lr = -1; 657 mask |= 2; 658 } 659 if (m->sys->input.dirMask & DIR_DOWN) { 660 ud = 1; 661 mask |= 4; 662 } 663 664 m->vmVariables[VM_VARIABLE_HERO_POS_UP_DOWN] = ud; 665 666 if (m->sys->input.dirMask & DIR_UP) { 667 m->vmVariables[VM_VARIABLE_HERO_POS_UP_DOWN] = -1; 668 } 669 670 if (m->sys->input.dirMask & DIR_UP) { /* inpJump */ 671 ud = -1; 672 mask |= 8; 673 } 674 675 m->vmVariables[VM_VARIABLE_HERO_POS_JUMP_DOWN] = ud; 676 m->vmVariables[VM_VARIABLE_HERO_POS_LEFT_RIGHT] = lr; 677 m->vmVariables[VM_VARIABLE_HERO_POS_MASK] = mask; 678 int16_t button = 0; 679 680 if (m->sys->input.button) { 681 button = 1; 682 mask |= 0x80; 683 } 684 685 m->vmVariables[VM_VARIABLE_HERO_ACTION] = button; 686 m->vmVariables[VM_VARIABLE_HERO_ACTION_POS_MASK] = mask; 687} 688 689void vm_inp_handleSpecialKeys(struct VirtualMachine* m) { 690 691 if (m->sys->input.pause) { 692 693 if (m->res->currentPartId != GAME_PART1 && m->res->currentPartId != GAME_PART2) { 694 m->sys->input.pause = false; 695 while (!m->sys->input.pause) { 696 sys_processEvents(m->sys); 697 sys_sleep(m->sys, 200); 698 } 699 } 700 m->sys->input.pause = false; 701 } 702 703 if (m->sys->input.code) { 704 m->sys->input.code = false; 705 if (m->res->currentPartId != GAME_PART_LAST && m->res->currentPartId != GAME_PART_FIRST) { 706 m->res->requestedNextPart = GAME_PART_LAST; 707 } 708 } 709 710 /* User has inputted a bad code, the "ERROR" screen is showing */ 711 if (m->vmVariables[0xC9] == 1) { 712 debug(DBG_VM, "vm_inp_handleSpecialKeys() unhandled case (m->vmVariables[0xC9] == 1)"); 713 } 714 715} 716 717void vm_snd_playSound(struct VirtualMachine* m, uint16_t resNum, uint8_t freq, uint8_t vol, uint8_t channel) { 718 719 debug(DBG_SND, "snd_playSound(0x%X, %d, %d, %d)", resNum, freq, vol, channel); 720 721 struct MemEntry *me = &m->res->_memList[resNum]; 722 723 if (me->state != MEMENTRY_STATE_LOADED) 724 return; 725 726 727 if (vol == 0) { 728 mixer_stopChannel(m->mixer, channel); 729 } else { 730 struct MixerChunk mc; 731 rb->memset(&mc, 0, sizeof(mc)); 732 mc.data = me->bufPtr + 8; /* skip header */ 733 mc.len = READ_BE_UINT16(me->bufPtr) * 2; 734 mc.loopLen = READ_BE_UINT16(me->bufPtr + 2) * 2; 735 if (mc.loopLen != 0) { 736 mc.loopPos = mc.len; 737 } 738 assert(freq < 40); 739 mixer_playChannel(m->mixer, channel & 3, &mc, vm_frequenceTable[freq], MIN(vol, 0x3F)); 740 } 741 742} 743 744void vm_snd_playMusic(struct VirtualMachine* m, uint16_t resNum, uint16_t delay, uint8_t pos) { 745 746 debug(DBG_SND, "snd_playMusic(0x%X, %d, %d)", resNum, delay, pos); 747 748 if (resNum != 0) { 749 player_loadSfxModule(m->player, resNum, delay, pos); 750 player_start(m->player); 751 } else if (delay != 0) { 752 player_setEventsDelay(m->player, delay); 753 } else { 754 player_stop(m->player); 755 } 756} 757 758void vm_saveOrLoad(struct VirtualMachine* m, struct Serializer *ser) { 759 struct Entry entries[] = { 760 SE_ARRAY(m->vmVariables, 0x100, SES_INT16, VER(1)), 761 SE_ARRAY(m->_scriptStackCalls, 0x100, SES_INT16, VER(1)), 762 SE_ARRAY(m->threadsData, 0x40 * 2, SES_INT16, VER(1)), 763 SE_ARRAY(m->vmIsChannelActive, 0x40 * 2, SES_INT8, VER(1)), 764 SE_END() 765 }; 766 ser_saveOrLoadEntries(ser, entries); 767} 768 769void vm_bypassProtection(struct VirtualMachine* m) 770{ 771 File f; 772 file_create(&f, true); 773 if (!file_open(&f, "bank0e", res_getDataDir(m->res), "rb")) { 774 warning("Unable to bypass protection: add bank0e file to datadir"); 775 } else { 776 struct Serializer s; 777 ser_create(&s, &f, SM_LOAD, m->res->_memPtrStart, 2); 778 vm_saveOrLoad(m, &s); 779 res_saveOrLoad(m->res, &s); 780 video_saveOrLoad(m->video, &s); 781 player_saveOrLoad(m->player, &s); 782 mixer_saveOrLoad(m->mixer, &s); 783 } 784 file_close(&f); 785}