home *** CD-ROM | disk | FTP | other *** search
/ hobbes.nmsu.edu 2008 / 2008-06-02_hobbes.nmsu.edu.zip / new / scummc-0.2.0-os2.zip / ScummC / src / scvm.c < prev    next >
Encoding:
C/C++ Source or Header  |  2008-02-03  |  29.4 KB  |  1,059 lines

  1. /* ScummC
  2.  * Copyright (C) 2006  Alban Bedel
  3.  *
  4.  * This program is free software; you can redistribute it and/or
  5.  * modify it under the terms of the GNU General Public License
  6.  * as published by the Free Software Foundation; either version 2
  7.  * of the License, or (at your option) any later version.
  8.  
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  17.  *
  18.  */
  19.  
  20. /**
  21.  * @file scvm.c
  22.  * @ingroup scvm
  23.  * @brief A primitive SCUMM VM implementation.
  24.  */
  25.  
  26. #include "config.h"
  27.  
  28. #include <stdlib.h>
  29. #include <stdio.h>
  30. #include <string.h>
  31. #include <inttypes.h>
  32. #include <errno.h>
  33. #include <sys/types.h>
  34. #include <sys/stat.h>
  35. #include <fcntl.h>
  36. #include <signal.h>
  37.  
  38. #include <SDL.h>
  39.  
  40. #include "scc_fd.h"
  41. #include "scc_util.h"
  42. #include "scc_param.h"
  43. #include "scc_cost.h"
  44. #include "scc_box.h"
  45. #include "scvm_res.h"
  46. #include "scvm_thread.h"
  47. #include "scvm.h"
  48.  
  49. #include "scvm_help.h"
  50.  
  51. static char* scvm_error[0x100] = {
  52.   "no error",
  53.   "script bound",
  54.   "op not supported or invalid",
  55.   "stack overflow",
  56.   "stack underflow",
  57.   "bad address",
  58.   "out of array bounds",
  59.   "bad array type",
  60.   "jump out of bounds",
  61.   "out of arrays",
  62.   "out of bound string",
  63.   "bad resource",
  64.   "bad thread",
  65.   "bad VM state",
  66.   "bad actor",
  67.   "bad costume",
  68.   "override overflow",
  69.   "override underflow",
  70.   "bad object",
  71.   "no room",
  72.   "bad palette",
  73.   "uninitialized vm",
  74.   "failed to set video mode",
  75.   "interrupted",
  76.   "breakpoint",
  77.   NULL
  78. };
  79.  
  80. extern scvm_op_t scvm_optable[0x100];
  81. extern scvm_op_t scvm_suboptable[0x100];
  82.  
  83. char* scvm_state_name(unsigned state) {
  84.   switch(state) {
  85.   case SCVM_UNINITED:
  86.     return "uninited";
  87.   case SCVM_BOOT:
  88.     return "boot";
  89.   case SCVM_BEGIN_CYCLE:
  90.     return "begin cycle";
  91.   case SCVM_RUNNING:
  92.     return "running";
  93.   case SCVM_START_SCRIPT:
  94.     return "start script";
  95.   case SCVM_OPEN_ROOM:
  96.     return "open room";
  97.   case SCVM_RUN_PRE_EXIT:
  98.     return "run pre exit";
  99.   case SCVM_RUN_EXCD:
  100.     return "run excd";
  101.   case SCVM_RUN_POST_EXIT:
  102.     return "run post exit";
  103.   case SCVM_SETUP_ROOM:
  104.     return "setup room";
  105.   case SCVM_RUN_PRE_ENTRY:
  106.     return "run pre entry";
  107.   case SCVM_RUN_ENCD:
  108.     return "run encd";
  109.   case SCVM_RUN_POST_ENTRY:
  110.     return "run post entry";
  111.   case SCVM_OPENED_ROOM:
  112.     return "opened room";
  113.   }
  114.   return NULL;
  115. }
  116.  
  117. static int scvm_default_random(struct scvm_backend_priv* be,int min,int max) {
  118.   int diff = max-min;
  119.   int r = (int)((diff+1.0)*rand()/(RAND_MAX+1.0));
  120.   return min+r;
  121. }
  122.  
  123. static void scvm_vars_init(scvm_vars_t* var) {
  124.   // init some engine variables
  125.   // soundcard: 0 = none
  126.   //            1 = pc speaker
  127.   //            3 = adlib
  128.   var->soundcard = 0;
  129.   // video mode: 4  = CGA
  130.   //             13 = EGA
  131.   //             19 = VGA?
  132.   //             30 = Hercule
  133.   //             42 = FMTowns
  134.   //             50 = MAC
  135.   //             82 = Amiga
  136.   var->videomode = 19;
  137.   // Heap size
  138.   var->heap_space = 1400;
  139.   // Playing from HD
  140.   var->fixed_disk = 1;
  141.   // Input mode: 0 = keyboard
  142.   //             1 = joystick
  143.   //             3 = mouse
  144.   var->input_mode = 3;
  145.   // EMS size
  146.   var->ems_space = 10000;
  147.   // Set an initial room size
  148.   var->room_width = 320;
  149.   var->room_height = 200;
  150.   // Subtitle speed
  151.   var->charinc = 4;
  152. }
  153.  
  154. scvm_t *scvm_new(scvm_backend_t* be, char* path,char* basename, uint8_t key) {
  155.   scc_fd_t* fd;
  156.   scvm_t* vm;
  157.   int i,num,len = (path ? strlen(path) + 1 : 0) + strlen(basename);
  158.   char idx_file[len+1];
  159.   uint32_t type;
  160.  
  161.   if(!be) {
  162.     scc_log(LOG_ERR,"No backend?\n");
  163.     return NULL;
  164.   }
  165.  
  166.   if(be->init && !be->init(be)) {
  167.       scc_log(LOG_ERR,"Backend initialization failed.\n");
  168.       return NULL;
  169.   }
  170.  
  171.   if(path)
  172.     sprintf(idx_file,"%s/%s.000",path,basename);
  173.   else
  174.     sprintf(idx_file,"%s.000",basename);
  175.   
  176.   fd = new_scc_fd(idx_file,O_RDONLY,key);
  177.   if(!fd) {
  178.     scc_log(LOG_ERR,"Failed to open index file %s: %s\n",
  179.             idx_file,strerror(errno));
  180.     return NULL;
  181.   }
  182.   
  183.   // Read the initial header
  184.   type = scc_fd_r32(fd);
  185.   len = scc_fd_r32be(fd);
  186.   if(type != MKID('R','N','A','M') || len < 9 ) {
  187.     scc_log(LOG_ERR,"Invalid index file, no RNAM block at the start.\n");
  188.     scc_fd_close(fd);
  189.     return NULL;
  190.   }
  191.   scc_fd_seek(fd,len-8,SEEK_CUR);
  192.   
  193.   // Read the maxs block
  194.   type = scc_fd_r32(fd);
  195.   len = scc_fd_r32be(fd);
  196.   if(type != MKID('M','A','X','S') || len != 8+15*2) {
  197.     scc_log(LOG_ERR,"Invalid index file, bad MAXS block.\n");
  198.     scc_fd_close(fd);
  199.     return NULL;
  200.   }
  201.   vm = calloc(1,sizeof(scvm_t));
  202.   vm->backend = be;
  203.   // vars
  204.   vm->num_var = scc_fd_r16le(fd);
  205.   vm->var_mem = calloc(vm->num_var,sizeof(int));
  206.   vm->var = (scvm_vars_t*)vm->var_mem;
  207.   scvm_vars_init(vm->var);
  208.   // unknown
  209.   scc_fd_r16le(fd);
  210.   // bit vars, don't be so stupid as the original vm, round up
  211.   vm->num_bitvar = (scc_fd_r16le(fd)+7)&~7;
  212.   vm->bitvar = calloc(vm->num_bitvar>>3,1);
  213.   // local objs
  214.   vm->num_local_object = scc_fd_r16le(fd);
  215.   // arrays
  216.   vm->num_array = scc_fd_r16le(fd);
  217.   vm->array = calloc(vm->num_array,sizeof(scvm_array_t));
  218.   // unkonwn
  219.   scc_fd_r16le(fd);
  220.   // verbs
  221.   scc_fd_r16le(fd);
  222.   // fl objects
  223.   scc_fd_r16le(fd);
  224.   // inventory
  225.   scc_fd_r16le(fd);
  226.   // rooms
  227.   scvm_res_init(&vm->res[SCVM_RES_ROOM],"room",scc_fd_r16le(fd),
  228.                 scvm_load_room,NULL);
  229.   // scripts
  230.   scvm_res_init(&vm->res[SCVM_RES_SCRIPT],"script",scc_fd_r16le(fd),
  231.                 scvm_load_script,free);
  232.   // sounds
  233.   scvm_res_init(&vm->res[SCVM_RES_SOUND],"sound",scc_fd_r16le(fd),NULL,NULL);
  234.   // charsets
  235.   scvm_res_init(&vm->res[SCVM_RES_CHARSET],"charset",scc_fd_r16le(fd),
  236.                 scvm_load_charset,NULL);
  237.   // costumes
  238.   scvm_res_init(&vm->res[SCVM_RES_COSTUME],"costume",scc_fd_r16le(fd),
  239.                 scvm_load_costume,NULL);
  240.   // objects
  241.   scvm_res_init(&vm->res[SCVM_RES_OBJECT],"object",scc_fd_r16le(fd),NULL,NULL);
  242.   vm->num_object = vm->res[SCVM_RES_OBJECT].num;
  243.   vm->object_pdata = calloc(vm->num_object,sizeof(scvm_object_pdata_t));
  244.   
  245.   // load room index
  246.   type = scc_fd_r32(fd);
  247.   len = scc_fd_r32be(fd);
  248.   if(type != MKID('D','R','O','O') || len < 8+2 ||
  249.      (num = scc_fd_r16le(fd))*5+2+8 != len ||
  250.      num != vm->res[SCVM_RES_ROOM].num) {
  251.     scc_log(LOG_ERR,"Invalid index file, bad DROO block.\n");
  252.     scc_fd_close(fd);
  253.     // scvm_free(vm);
  254.     return NULL;
  255.   }
  256.   for(i = 0 ; i < num ; i++) {
  257.     vm->res[SCVM_RES_ROOM].idx[i].file = scc_fd_r8(fd);
  258.     vm->res[SCVM_RES_ROOM].idx[i].room = i;
  259.     if(vm->res[SCVM_RES_ROOM].idx[i].file >= vm->num_file)
  260.       vm->num_file = vm->res[SCVM_RES_ROOM].idx[i].file+1;
  261.   }
  262.   if(!vm->num_file) {
  263.     scc_log(LOG_ERR,"Invalid index file, no file ?\n");
  264.     scc_fd_close(fd);
  265.     // scvm_free(vm);
  266.     return NULL;
  267.   }
  268.   // setup the file stuff
  269.   vm->path = strdup(path ? path : ".");
  270.   vm->basename = strdup(basename);
  271.   vm->file_key = key;
  272.   vm->file = calloc(vm->num_file,sizeof(scc_fd_t*));
  273.   vm->file[0] = fd;
  274.   // all 0 ??
  275.   for(i = 0 ; i < num ; i++)
  276.     vm->res[SCVM_RES_ROOM].idx[i].offset = scc_fd_r32le(fd);
  277.   // load loff
  278.   for(i = 1 ; i < vm->num_file ; i++) {
  279.     int j;
  280.     scc_fd_t* loff_fd = scvm_open_file(vm,i);
  281.     if(!fd) continue;
  282.     type = scc_fd_r32(loff_fd);
  283.     len = scc_fd_r32be(loff_fd);
  284.     if(type != MKID('L','E','C','F') || len < 17 ) {
  285.       scc_log(LOG_ERR,"Invalid LECF block.\n");
  286.       continue;
  287.     }
  288.     type = scc_fd_r32(loff_fd);
  289.     len = scc_fd_r32be(loff_fd);
  290.     if(type != MKID('L','O','F','F') || len < 9 ) {
  291.       scc_log(LOG_ERR,"Invalid LOFF block.\n");
  292.       continue;
  293.     }
  294.     num = scc_fd_r8(loff_fd);
  295.     for(j = 0 ; j < num ; j++) {
  296.       unsigned rno = scc_fd_r8(loff_fd);
  297.       if(rno >= vm->res[SCVM_RES_ROOM].num) {
  298.         scc_log(LOG_ERR,"LOFF block has bad room number: %d\n",rno);
  299.         continue;
  300.       }
  301.       vm->res[SCVM_RES_ROOM].idx[rno].offset = scc_fd_r32le(loff_fd);
  302.     }
  303.     scvm_close_file(vm,i);
  304.   }
  305.   
  306.     
  307.   // script index
  308.   if(!scvm_res_load_index(vm,&vm->res[SCVM_RES_SCRIPT],fd,
  309.                           MKID('D','S','C','R'))) {
  310.     // scvm_free(vm);
  311.     return NULL;
  312.   }
  313.   
  314.   // sound index
  315.   if(!scvm_res_load_index(vm,&vm->res[SCVM_RES_SOUND],fd,
  316.                           MKID('D','S','O','U'))) {
  317.     // scvm_free(vm);
  318.     return NULL;
  319.   }
  320.   
  321.   // costume index
  322.   if(!scvm_res_load_index(vm,&vm->res[SCVM_RES_COSTUME],fd,
  323.                           MKID('D','C','O','S'))) {
  324.     // scvm_free(vm);
  325.     return NULL;
  326.   }
  327.   
  328.   // charset index
  329.   if(!scvm_res_load_index(vm,&vm->res[SCVM_RES_CHARSET],fd,
  330.                           MKID('D','C','H','R'))) {
  331.     // scvm_free(vm);
  332.     return NULL;
  333.   }
  334.   
  335.   // objects
  336.   type = scc_fd_r32(fd);
  337.   len = scc_fd_r32be(fd);
  338.   if(type != MKID('D','O','B','J') || len < 10) {
  339.     scc_log(LOG_ERR,"Invalid index file, bad DOBJ block.\n");
  340.     return NULL;
  341.   }
  342.   num = scc_fd_r16le(fd);
  343.   if(len != 8+2+5*num || num > vm->num_object) {
  344.     scc_log(LOG_ERR,"Invalid index file, bad DOBJ block.\n");
  345.     return NULL;
  346.   }
  347.   for(i = 0 ; i < num ; i++) {
  348.     uint8_t owner_state = scc_fd_r8(fd);
  349.     vm->object_pdata[i].state = owner_state>>4;
  350.     vm->object_pdata[i].owner = owner_state&0x0F;
  351.   }
  352.   for(i = 0 ; i < num ; i++)
  353.     vm->object_pdata[i].klass = scc_fd_r32le(fd);
  354.  
  355.   // arrays
  356.  
  357.   // actors
  358.   vm->num_actor = 16;
  359.   vm->actor = calloc(vm->num_actor,sizeof(scvm_actor_t));
  360.   for(i = 0 ; i < vm->num_actor ; i++) {
  361.     vm->actor[i].id = i;
  362.     scvm_actor_init(&vm->actor[i]);
  363.     scc_cost_dec_init(&vm->actor[i].costdec);
  364.   }
  365.   vm->current_actor = vm->actor;
  366.   
  367.   // view
  368.   vm->view = calloc(1,sizeof(scvm_view_t));
  369.   vm->view->screen_width = 320;
  370.   vm->view->screen_height = 200;
  371.   
  372.   // threads
  373.   vm->state = SCVM_BOOT;
  374.   vm->num_thread = 16;
  375.   vm->current_thread = NULL;
  376.   vm->thread = calloc(vm->num_thread,sizeof(scvm_thread_t));
  377.   for(i = 0 ; i < vm->num_thread ; i++)
  378.     vm->thread[i].id = i;
  379.   vm->optable = scvm_optable;
  380.   vm->suboptable = scvm_suboptable;
  381.   // stack
  382.   vm->stack_size = 1024;
  383.   vm->stack = calloc(vm->stack_size,sizeof(int));
  384.  
  385.   // close fd
  386.   scvm_close_file(vm,0);
  387.   
  388.   // set default system callbacks
  389.   if(!vm->backend->random)
  390.     vm->backend->random = scvm_default_random;
  391.   
  392.   return vm;
  393. }
  394.  
  395. int scvm_add_breakpoint(scvm_t* vm, unsigned room_id,
  396.                         unsigned script_id, unsigned pos) {
  397.   scvm_debug_t* dbg;
  398.   scvm_breakpoint_t* bp;
  399.   int id;
  400.  
  401.   if(script_id < 200 && script_id >= vm->res[SCVM_RES_SCRIPT].num)
  402.     return -1;
  403.   // TODO: check room script id if the room is loaded
  404.   if(room_id >= vm->res[SCVM_RES_ROOM].num)
  405.     return -1;
  406.  
  407.   if(!vm->dbg)
  408.     vm->dbg = calloc(1,sizeof(scvm_debug_t));
  409.  
  410.   dbg = vm->dbg;
  411.   id = dbg->num_breakpoint;
  412.   if((id & 3) == 0)
  413.     dbg->breakpoint = realloc(dbg->breakpoint,
  414.                               (id+4)*sizeof(scvm_breakpoint_t));
  415.   bp = dbg->breakpoint + id;
  416.   dbg->num_breakpoint++;
  417.  
  418.   bp->id = id;
  419.   bp->room_id = room_id;
  420.   bp->script_id = script_id;
  421.   bp->pos = pos;
  422.   return bp->id;
  423. }
  424.  
  425. unsigned scvm_get_time(scvm_t* vm) {
  426.   return vm->backend->get_time(vm->backend->priv);
  427. }
  428.  
  429. int scvm_random(scvm_t* vm, int min, int max) {
  430.   return vm->backend->random(vm->backend->priv,min,max);
  431. }
  432.  
  433. int scvm_init_video(scvm_t* vm, unsigned w, unsigned h, unsigned bpp) {
  434.   return vm->backend->init_video(vm->backend->priv,w,h,bpp);
  435. }
  436.  
  437. void scvm_update_palette(scvm_t* vm, scvm_color_t* pal) {
  438.   vm->backend->update_palette(vm->backend->priv,pal);
  439. }
  440.  
  441. void scvm_draw(scvm_t* vm, scvm_view_t* view) {
  442.   vm->backend->draw(vm->backend->priv,vm,view);
  443. }
  444.  
  445. void scvm_sleep(scvm_t* vm, unsigned delay) {
  446.   vm->backend->sleep(vm->backend->priv,delay);
  447. }
  448.  
  449. void scvm_flip(scvm_t* vm) {
  450.   vm->backend->flip(vm->backend->priv);
  451. }
  452.  
  453. void scvm_uninit_video(scvm_t* vm) {
  454.   vm->backend->uninit_video(vm->backend->priv);
  455. }
  456.  
  457. void scvm_check_events(scvm_t* vm) {
  458.   vm->backend->check_events(vm->backend->priv,vm);
  459. }
  460.  
  461. void scvm_press_key(scvm_t* vm, uint8_t key) {
  462.   vm->keypress = key;
  463.   vm->key_state[key>>3] |= 1<<(key&7);
  464. }
  465.  
  466. void scvm_release_key(scvm_t* vm, uint8_t key) {
  467.   vm->key_state[key>>3] &= ~(1<<(key&7));
  468. }
  469.  
  470. void scvm_press_button(scvm_t* vm, int btn) {
  471.   if(btn > 3) return;
  472.   vm->btnpress = btn;
  473.   vm->btn_state |= 1<<btn;
  474. }
  475.  
  476. void scvm_release_button(scvm_t* vm, int btn) {
  477.   if(btn > 3) return;
  478.   vm->btn_state &= ~(1<<btn);
  479. }
  480.  
  481. void scvm_set_mouse_position(scvm_t* vm, int x, int y) {
  482.     if(x < 0)
  483.         x = 0;
  484.     else if(x >= vm->view->screen_width)
  485.         x = vm->view->screen_width-1;
  486.     if(y < 0)
  487.         y = 0;
  488.     else if(y >= vm->view->screen_height)
  489.         y = vm->view->screen_height-1;
  490.     vm->var->mouse_x = x;
  491.     vm->var->mouse_y = y;
  492. }
  493.  
  494. static void scvm_switch_to_thread(scvm_t* vm,unsigned thid, unsigned next_state) {
  495.   if(vm->current_thread)
  496.     vm->current_thread->state = SCVM_THREAD_PENDED;
  497.   vm->thread[thid].parent = vm->current_thread;
  498.   vm->thread[thid].next_state = next_state;
  499.   vm->current_thread = &vm->thread[thid];
  500.   vm->state = SCVM_RUNNING;
  501. }
  502.  
  503. int scvm_run_threads(scvm_t* vm,unsigned cycles) {
  504.   int i=0,r;
  505.   unsigned delta,now;
  506.   scvm_thread_t* parent;
  507.   
  508.   while(cycles > 0) {
  509.     scc_log(LOG_MSG,"VM run threads state: %d\n",vm->state);
  510.     switch(vm->state) {
  511.     case SCVM_BOOT:
  512.       if(!scvm_init_video(vm,640,480,8)) {
  513.        scc_log(LOG_ERR,"Failed to open video output.\n");
  514.         return SCVM_ERR_VIDEO_MODE;
  515.       }
  516.       if((r=scvm_start_script(vm,0,1,NULL)) < 0) {
  517.         scc_log(LOG_MSG,"Failed to start boot script: %s\n",scvm_error[-r]);
  518.         return r;
  519.       }
  520.       vm->state = SCVM_BEGIN_CYCLE;
  521.  
  522.     case SCVM_BEGIN_CYCLE:
  523.       // Reschedule the delayed threads
  524.       now = scvm_get_time(vm);
  525.       if(now < vm->time) now = vm->time;
  526.       delta = now - vm->time;
  527.       for(i = 0 ; i < vm->num_thread ; i++) {
  528.         if(vm->thread[i].state != SCVM_THREAD_DELAYED) continue;
  529.         if(!(vm->thread[i].flags & SCVM_THREAD_DELAY))
  530.           vm->thread[i].flags |= SCVM_THREAD_DELAY;
  531.         else if(vm->thread[i].delay > delta)
  532.           vm->thread[i].delay -= delta;
  533.         else {
  534.           vm->thread[i].delay = 0;
  535.           vm->thread[i].flags &= ~SCVM_THREAD_DELAY;
  536.           vm->thread[i].state = SCVM_THREAD_RUNNING;
  537.         }
  538.       }
  539.       // Update the timers
  540.       vm->var->timer = 0;
  541.       vm->var->timer1 += vm->var->timer_next;
  542.       vm->var->timer2 += vm->var->timer_next;
  543.       vm->var->timer3 += vm->var->timer_next;
  544.       // Update the mouse virtual position
  545.       {
  546.           int x = vm->var->mouse_x, y = vm->var->mouse_y;
  547.           if(!scvm_abs_position_to_virtual(vm,&x,&y)) {
  548.               vm->var->virtual_mouse_x = x;
  549.               vm->var->virtual_mouse_y = y;
  550.           } else {
  551.               vm->var->virtual_mouse_x = -1;
  552.               vm->var->virtual_mouse_y = -1;
  553.           }
  554.       }
  555.       vm->var->camera_pos_x = vm->view->camera_x;
  556.       // Run the scripts      
  557.       vm->state = SCVM_RUNNING;
  558.       vm->time = now;
  559.       break;
  560.     case SCVM_START_SCRIPT:
  561.       parent = vm->current_thread;
  562.       vm->current_thread = vm->next_thread;
  563.       vm->next_thread = NULL;
  564.       vm->current_thread->parent = parent;
  565.       if(parent) parent->state = SCVM_THREAD_PENDED;
  566.       vm->state = SCVM_RUNNING;
  567.     case SCVM_RUNNING:
  568.       if(!vm->current_thread) {
  569.         for(i = 0 ; i < vm->num_thread ; i++)
  570.           if(vm->thread[i].state == SCVM_THREAD_RUNNING &&
  571.              vm->thread[i].cycle <= vm->cycle) break;
  572.         if(i >= vm->num_thread) {
  573.           vm->state = SCVM_FINISHED_SCRIPTS;
  574.           break;
  575.         }
  576.         vm->current_thread = &vm->thread[i];
  577.       }
  578.       scc_log(LOG_MSG,"\n == VM enter thread %d / script %d @ 0x%x / room %d ==\n",
  579.               vm->current_thread->id,vm->current_thread->script->id,
  580.               vm->current_thread->code_ptr,
  581.               vm->room ? vm->room->id : -1);
  582.       r = scvm_thread_run(vm,vm->current_thread);
  583.       scc_log(LOG_MSG," == VM leave thread %d / script %d @ 0x%x / room %d ==\n\n",
  584.               vm->current_thread->id,vm->current_thread->script->id,
  585.               vm->current_thread->code_ptr,
  586.               vm->room ? vm->room->id : -1);
  587.       if(r < 0) return r; // error
  588.       if(r > 0) {         // switch state
  589.         vm->state = r;
  590.         break;
  591.       }
  592.       // Done with this thread for this cycle
  593.       if(vm->current_thread->cycle <= vm->cycle)
  594.         vm->current_thread->cycle = vm->cycle+1;
  595.       // Continue a job
  596.       if(vm->current_thread->next_state) {
  597.         vm->state = vm->current_thread->next_state;
  598.         parent = vm->current_thread->parent;
  599.         if(parent) parent->state = SCVM_THREAD_RUNNING;
  600.         vm->current_thread->next_state = 0;
  601.         vm->current_thread->parent = NULL;
  602.         vm->current_thread = parent;
  603.         break;
  604.       }
  605.       // nested call, switch back to the parent
  606.       if(vm->current_thread->parent &&
  607.          vm->current_thread->parent->state == SCVM_THREAD_PENDED) {
  608.         parent = vm->current_thread->parent;
  609.         vm->current_thread->parent = NULL;
  610.         parent->state = SCVM_THREAD_RUNNING;
  611.         vm->current_thread = parent;
  612.         break;
  613.       } else {
  614.         vm->current_thread = NULL;
  615.       }
  616.       break;
  617.       
  618.     case SCVM_OPEN_ROOM:
  619.       vm->var->new_room = vm->next_room;
  620.     case SCVM_RUN_PRE_EXIT:
  621.       if(vm->var->pre_exit_script) {
  622.         if((r = scvm_start_script(vm,0,vm->var->pre_exit_script,NULL)) < 0)
  623.           return r;
  624.         scvm_switch_to_thread(vm,r,SCVM_RUN_EXCD);
  625.         break;
  626.       }
  627.       vm->state = SCVM_RUN_EXCD;
  628.     case SCVM_RUN_EXCD:
  629.       if(vm->room && vm->room->exit) {
  630.         if((r = scvm_start_thread(vm,vm->room->exit,0,0,NULL)) < 0)
  631.           return r;
  632.         scvm_switch_to_thread(vm,r,SCVM_RUN_POST_EXIT);
  633.         break;
  634.       }
  635.       vm->state = SCVM_RUN_POST_EXIT;
  636.     case SCVM_RUN_POST_EXIT:
  637.       if(vm->var->post_exit_script) {
  638.         if((r = scvm_start_script(vm,0,vm->var->post_exit_script,NULL)) < 0)
  639.           return r;
  640.         scvm_switch_to_thread(vm,r,SCVM_SETUP_ROOM);
  641.         break;
  642.       }
  643.       vm->state = SCVM_SETUP_ROOM;
  644.     case SCVM_SETUP_ROOM:
  645.       if(!vm->next_room) {
  646.         vm->state = SCVM_RUNNING;
  647.         break;
  648.       } else {
  649.         scvm_room_t* room = scvm_load_res(vm,SCVM_RES_ROOM,vm->next_room);
  650.         // load room
  651.         if(!room) return SCVM_ERR_BAD_RESOURCE;
  652.         // cleanup the old room
  653.         // copy the room palette
  654.         if(room->num_palette > 0) {
  655.           room->current_palette = room->palette[0];
  656.           memcpy(vm->view->palette,room->palette[0],sizeof(scvm_palette_t));
  657.           vm->view->flags |= SCVM_VIEW_PALETTE_CHANGED;
  658.         }
  659.         // set the camera min/max
  660.         vm->var->camera_min_x = vm->view->screen_width/2;
  661.         vm->var->camera_max_x = room->width - vm->var->camera_min_x;
  662.         vm->var->room = room->id;
  663.         vm->room = room;
  664.         vm->view->camera_x = vm->var->camera_min_x;
  665.         vm->view->camera_x = vm->var->camera_min_x;
  666.         vm->var->camera_pos_x = vm->view->camera_x;
  667.         scvm_put_actors(vm);
  668.         vm->state = SCVM_RUN_PRE_ENTRY;
  669.       }
  670.     case SCVM_RUN_PRE_ENTRY:
  671.       if(vm->var->pre_entry_script) {
  672.         if((r = scvm_start_script(vm,0,vm->var->pre_entry_script,NULL)) < 0)
  673.           return r;
  674.         scvm_switch_to_thread(vm,r,SCVM_RUN_ENCD);
  675.         break;
  676.       }
  677.       vm->state = SCVM_RUN_ENCD;
  678.     case SCVM_RUN_ENCD:
  679.       if(vm->room->entry) {
  680.         if((r = scvm_start_thread(vm,vm->room->entry,0,0,NULL)) < 0)
  681.           return r;
  682.         scvm_switch_to_thread(vm,r,SCVM_RUN_POST_ENTRY);
  683.         break;
  684.       }
  685.       vm->state = SCVM_RUN_POST_ENTRY;
  686.     case SCVM_RUN_POST_ENTRY:
  687.       if(vm->var->post_entry_script) {
  688.         if((r = scvm_start_script(vm,0,vm->var->post_entry_script,NULL)) < 0)
  689.           return r;
  690.         scvm_switch_to_thread(vm,r,0);
  691.         break;
  692.       }      
  693.       if(vm->next_room_state) {
  694.         vm->state = vm->next_room_state;
  695.         vm->next_room_state = 0;
  696.       } else
  697.         vm->state = SCVM_RUNNING;
  698.       break;
  699.  
  700.     case SCVM_CAMERA_FOLLOW_ACTOR:
  701.       if(vm->actor[vm->view->follow].room != vm->room->id) {
  702.         vm->next_room = vm->actor[vm->view->follow].room;
  703.         vm->next_room_state = SCVM_CAMERA_START_FOLLOWING_ACTOR_IN_ROOM;
  704.         vm->state = SCVM_OPEN_ROOM;
  705.       } else
  706.         vm->state = SCVM_CAMERA_START_FOLLOWING_ACTOR;
  707.       break;
  708.  
  709.     case SCVM_CAMERA_START_FOLLOWING_ACTOR:
  710.       // TODO: We should only call set camera if needed here
  711.       // if(noNeedToSetCamera) {
  712.       //   vm->state = SCVM_RUN_INVENTORY;
  713.       //   break;
  714.       // }
  715.  
  716.     case SCVM_CAMERA_START_FOLLOWING_ACTOR_IN_ROOM:
  717.       if((r = scvm_set_camera_at(vm,vm->actor[vm->view->follow].x)) < 0)
  718.         return r;
  719.       if(r > 0) {
  720.         scvm_switch_to_thread(vm,r-1,SCVM_RUN_INVENTORY);
  721.         break;
  722.       }
  723.       vm->state = SCVM_RUN_INVENTORY;
  724.  
  725.  
  726.     case SCVM_RUN_INVENTORY:
  727.       if(vm->var->inventory_script) {
  728.         if((r = scvm_start_script(vm,0,vm->var->inventory_script,NULL)) < 0)
  729.           return r;
  730.         scvm_switch_to_thread(vm,r,0);
  731.         break;
  732.       }
  733.       vm->state = SCVM_RUNNING;
  734.       break;
  735.  
  736.     case SCVM_FINISHED_SCRIPTS:
  737.       vm->state = SCVM_CHECK_INPUT;
  738.  
  739.     case SCVM_CHECK_INPUT:
  740.       if(!vm->var->verb_script /*|| !vm->userput*/) {
  741.         vm->state = SCVM_CHECKED_INPUT;
  742.         break;
  743.       }
  744.       if(vm->keypress) {
  745.         int args[] = { 3, SCVM_INPUT_KEY, vm->keypress, 1 };
  746.         vm->keypress = 0;
  747.         // TODO: check verb keys
  748.         if((r = scvm_start_script(vm,0,vm->var->verb_script,args)) < 0)
  749.           return r;
  750.         // Keep the same state to also check mouse button
  751.         // press in the same frame. ScummVM don't do that,
  752.         // but that seems more correct to me. Dunno what the
  753.         // original engine did.
  754.         scvm_switch_to_thread(vm,r,SCVM_CHECK_INPUT);
  755.         break;
  756.       }
  757.       if(vm->btnpress) {
  758.         int args[] = { 3, SCVM_INPUT_VERB, 0, vm->btnpress };
  759.         vm->btnpress = 0;
  760.         if(0 /* clickOnVerb */) {
  761.           //args[2] = verb->id;
  762.         } else if(vm->var->virtual_mouse_x >= 0)
  763.           args[1] = SCVM_INPUT_ROOM;
  764.         if((r = scvm_start_script(vm,0,vm->var->verb_script,args)) < 0)
  765.           return r;
  766.         scvm_switch_to_thread(vm,r,SCVM_CHECKED_INPUT);
  767.         break;
  768.       }
  769.     case SCVM_CHECKED_INPUT:
  770.       vm->state = SCVM_MOVE_CAMERA;
  771.  
  772.     case SCVM_MOVE_CAMERA:
  773.       if((r = scvm_move_camera(vm)) < 0)
  774.         return r;
  775.       if(r > 0) {
  776.         scvm_switch_to_thread(vm,r-1,SCVM_WALK_ACTORS);
  777.         break;
  778.       }
  779.       vm->state = SCVM_WALK_ACTORS;
  780.  
  781.     case SCVM_WALK_ACTORS:
  782.       scvm_step_actors(vm);
  783.       vm->state = SCVM_FINISH_CYCLE;
  784.  
  785.     case SCVM_FINISH_CYCLE:
  786.       scc_log(LOG_MSG,"\n == VM finished cycle %d == \n\n",vm->cycle);
  787.       cycles--;
  788.       vm->cycle++;
  789.       vm->state = SCVM_BEGIN_CYCLE;
  790.       break;
  791.  
  792.     default:
  793.       scc_log(LOG_ERR,"Invalid VM state: %d\n",vm->state);
  794.       return SCVM_ERR_BAD_STATE;
  795.     }
  796.   }
  797.   
  798.   return 0;    
  799. }
  800.  
  801. void scvm_cycle_palette(scvm_t* vm) {
  802.   int i,step,add;
  803.   scvm_color_t color;
  804.   scvm_cycle_t* cycle;
  805.   if(!vm->room || !vm->room->cycle) return;
  806.   step = vm->var->timer;
  807.   if(step < vm->var->timer_next)
  808.     step = vm->var->timer_next;
  809.   for(i = 0 ; i < vm->room->num_cycle ; i++) {
  810.     cycle = &vm->room->cycle[i];
  811.     if(!cycle->delay) continue;
  812.     cycle->counter += add;
  813.     if(cycle->counter < cycle->delay) continue;
  814.     // backward
  815.     if(cycle->flags & 2) {
  816.       color = vm->view->palette[cycle->start];
  817.       if(cycle->end-cycle->start > 0)
  818.         memmove(&vm->view->palette[cycle->start],
  819.                 &vm->view->palette[cycle->start+1],
  820.                 (cycle->end-cycle->start-1)*sizeof(scvm_color_t));
  821.       vm->view->palette[cycle->end] = color;
  822.     } else { // forward
  823.       color = vm->view->palette[cycle->end];
  824.       if(cycle->end-cycle->start > 0)
  825.         memmove(&vm->view->palette[cycle->start+1],
  826.                 &vm->view->palette[cycle->start],
  827.                 (cycle->end-cycle->start-1)*sizeof(scvm_color_t));
  828.       vm->view->palette[cycle->start] = color;
  829.     }
  830.     vm->view->flags |= SCVM_VIEW_PALETTE_CHANGED;
  831.   }
  832. }
  833.  
  834. int scvm_run_once(scvm_t* vm) {
  835.   unsigned start,end,delay;
  836.   int r;
  837.  
  838.   if(!vm->backend)
  839.     return SCVM_ERR_UNINITED_VM;;
  840.  
  841.   start = scvm_get_time(vm);
  842.   scvm_check_events(vm);
  843.   r = scvm_run_threads(vm,1);
  844.   if(r < 0) {
  845.     if(vm->current_thread)
  846.       scc_log(LOG_MSG,"Script error: %s @ %03d:0x%04X\n",
  847.               scvm_error[-r],vm->current_thread->script->id,
  848.               vm->current_thread->op_start);
  849.     else
  850.       scc_log(LOG_MSG,"Script error: %s\n",scvm_error[-r]);
  851.     return r;
  852.   }
  853.  
  854.   scvm_cycle_palette(vm);
  855.   if(vm->view->flags & SCVM_VIEW_PALETTE_CHANGED) {
  856.     scvm_update_palette(vm,vm->view->palette);
  857.     vm->view->flags &= ~SCVM_VIEW_PALETTE_CHANGED;
  858.   }
  859.  
  860.   scvm_draw(vm,vm->view);
  861.  
  862.   end = scvm_get_time(vm);
  863.   if(end < start) end = start;
  864.   delay = vm->var->timer_next*1000/60;
  865.   if(delay > end-start)
  866.     scvm_sleep(vm,delay-(end-start));
  867.  
  868.   scvm_flip(vm);
  869.  
  870.   return 0;
  871. }
  872.  
  873. int scvm_run(scvm_t* vm) {
  874.   int r = 0;
  875.   while(r >= 0)
  876.     r = scvm_run_once(vm);
  877.   return r;
  878. }
  879.  
  880. int scvm_get_object_position(scvm_t* vm, unsigned id, int* x, int* y) {
  881.   scvm_object_t* obj;
  882.   if(id < vm->num_actor) {
  883.     if(x) *x = vm->actor[id].x;
  884.     if(y) *y = vm->actor[id].y;
  885.     return 0;
  886.   }
  887.   if(id >= vm->res[SCVM_RES_OBJECT].num)
  888.     return SCVM_ERR_BAD_OBJECT;
  889.   if(!(obj = vm->res[SCVM_RES_OBJECT].idx[id].data)) {
  890.     if(x) *x = 0;
  891.     if(y) *y = 0;
  892.     return 1;
  893.   }
  894.   if(x) *x = obj->x;
  895.   if(y) *y = obj->y;
  896.   return 0;
  897. }
  898.  
  899. /////////////////////
  900.  
  901. typedef struct scvm_backend_priv {
  902.   int inited_video;
  903.   SDL_Surface* screen;
  904. } scvm_backend_sdl_t;
  905.  
  906. static int sdl_scvm_init_video(scvm_backend_sdl_t* be, unsigned width,
  907.                                unsigned height, unsigned bpp) {
  908.   if(!be->inited_video) {
  909.     sig_t oldint = signal(SIGINT,SIG_DFL);
  910.     sig_t oldquit = signal(SIGQUIT,SIG_DFL);
  911.     sig_t oldterm = signal(SIGTERM,SIG_DFL);
  912.     if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
  913.       scc_log(LOG_ERR,"SDL video init failed.\n");
  914.       return 0;
  915.     }
  916.     signal(SIGINT,oldint);
  917.     signal(SIGQUIT,oldquit);
  918.     signal(SIGTERM,oldterm);
  919.     // Lame, but this must be called after initing the video
  920.     SDL_EnableUNICODE(1);
  921.     be->inited_video = 1;
  922.   }
  923.   if(!(be->screen =
  924.        SDL_SetVideoMode(width,height,bpp,SDL_HWPALETTE))) {
  925.     scc_log(LOG_ERR,"Failed to create SDL screen.\n");
  926.     return 0;
  927.   }
  928.   return 1;
  929. }
  930.  
  931. static void sdl_scvm_uninit_video(scvm_backend_sdl_t* be) {
  932.   if(be->inited_video) {
  933.     SDL_QuitSubSystem(SDL_INIT_VIDEO);
  934.     be->inited_video = 0;
  935.   }
  936. }
  937.  
  938.  
  939. static void sdl_scvm_update_palette(scvm_backend_sdl_t* be, scvm_color_t* pal) {
  940.   SDL_Color colors[256];
  941.   int i;
  942.   for(i = 0 ; i < 256 ; i++) {
  943.     colors[i].r = pal[i].r;
  944.     colors[i].g = pal[i].g;
  945.     colors[i].b = pal[i].b;
  946.   }
  947.   SDL_SetPalette(be->screen, SDL_LOGPAL|SDL_PHYSPAL, colors, 0, 256);
  948. }
  949.  
  950. void sdl_scvm_draw(scvm_backend_sdl_t* be, scvm_t* vm, scvm_view_t* view) {
  951.   if(SDL_MUSTLOCK(be->screen)) SDL_LockSurface(be->screen);
  952.   scvm_view_draw(vm,vm->view,be->screen->pixels,be->screen->pitch,
  953.                  be->screen->w,be->screen->h);
  954.   if(SDL_MUSTLOCK(be->screen)) SDL_UnlockSurface(be->screen);
  955. }
  956.  
  957. static void sdl_scvm_sleep(scvm_backend_sdl_t* be, unsigned delay) {
  958.   SDL_Delay(delay);
  959. }
  960.  
  961. static void sdl_scvm_flip(scvm_backend_sdl_t* be) {
  962.   SDL_Flip(be->screen);
  963. }
  964.  
  965. static unsigned sdl_scvm_get_time(scvm_backend_sdl_t* be) {
  966.   return SDL_GetTicks();
  967. }
  968.  
  969. static void sdl_scvm_check_events(scvm_backend_sdl_t* be, scvm_t* vm) {
  970.   SDL_Event ev;
  971.   while(SDL_PollEvent(&ev)) {
  972.     switch(ev.type) {
  973.     case SDL_MOUSEBUTTONDOWN:
  974.       scvm_press_button(vm,ev.button.button);
  975.       break;
  976.     case SDL_MOUSEBUTTONUP:
  977.       scvm_release_button(vm,ev.button.button);
  978.       break;
  979.     case SDL_MOUSEMOTION:
  980.       scvm_set_mouse_position(vm,ev.motion.x*
  981.                               vm->view->screen_width/be->screen->w,
  982.                               ev.motion.y*
  983.                               vm->view->screen_height/be->screen->h);
  984.       break;
  985.     case SDL_KEYDOWN:
  986.       if(!ev.key.keysym.unicode ||
  987.          ev.key.keysym.unicode > 255) break;
  988.       scvm_press_key(vm,ev.key.keysym.unicode);
  989.       break;
  990.  
  991.     case SDL_KEYUP:
  992.       if(!ev.key.keysym.unicode ||
  993.          ev.key.keysym.unicode > 255) break;
  994.       scvm_release_key(vm,ev.key.keysym.unicode);
  995.       break;
  996.     }
  997.   }
  998. }
  999.  
  1000. static void sdl_backend_uninit(scvm_backend_t* be) {
  1001.   SDL_Quit();
  1002.   if(be->priv)
  1003.     free(be->priv);
  1004. }
  1005.  
  1006. static int sdl_backend_init(scvm_backend_t* be) {
  1007.   be->uninit = sdl_backend_uninit;
  1008.   be->get_time = sdl_scvm_get_time;
  1009.   be->update_palette = sdl_scvm_update_palette;
  1010.   be->init_video = sdl_scvm_init_video;
  1011.   be->draw = sdl_scvm_draw;
  1012.   be->sleep = sdl_scvm_sleep;
  1013.   be->flip = sdl_scvm_flip;
  1014.   be->uninit_video = sdl_scvm_uninit_video;
  1015.   be->check_events = sdl_scvm_check_events;
  1016.   be->priv = calloc(1,sizeof(scvm_backend_sdl_t));
  1017.   if(SDL_Init(SDL_INIT_NOPARACHUTE) < 0) {
  1018.     scc_log(LOG_ERR,"SDL init failed.\n");
  1019.     return 0;
  1020.   }
  1021.   return 1;
  1022. }
  1023.  
  1024. static char* basedir = NULL;
  1025. static int file_key = 0;
  1026. static int run_debugger = 0;
  1027.  
  1028. static scc_param_t scc_parse_params[] = {
  1029.   { "dir", SCC_PARAM_STR, 0, 0, &basedir },
  1030.   { "key", SCC_PARAM_INT, 0, 0xFF, &file_key },
  1031.   { "dbg", SCC_PARAM_FLAG, 0, 1, &run_debugger },
  1032.   { "help", SCC_PARAM_HELP, 0, 0, &scvm_help },
  1033.   { NULL, 0, 0, 0, NULL }
  1034. };
  1035.  
  1036.  
  1037. int main(int argc,char** argv) {
  1038.   scvm_t* vm;
  1039.   scc_cl_arg_t* files;
  1040.   scvm_backend_t backend = { sdl_backend_init };
  1041.  
  1042.   files = scc_param_parse_argv(scc_parse_params,argc-1,&argv[1]);
  1043.   if(!files) scc_print_help(&scvm_help,1);
  1044.  
  1045.   vm = scvm_new(&backend,basedir,files->val,file_key);
  1046.  
  1047.   if(!vm) {
  1048.     scc_log(LOG_ERR,"Failed to create VM.\n");
  1049.     return 1;
  1050.   }
  1051.   scc_log(LOG_MSG,"VM created.\n");
  1052.  
  1053.   if(run_debugger)
  1054.     scvm_debugger(vm);
  1055.   else
  1056.     scvm_run(vm);
  1057.   return 0;
  1058. }
  1059.