#if 0 gcc -s -O2 -o ~/bin/heromesh main.c picture.o bindings.o smallxrm.o sqlite3.o `sdl-config --cflags --libs` -ldl -lpthread exit #endif /* This program is part of Free Hero Mesh and is public domain. */ #define _BSD_SOURCE #define HEROMESH_MAIN #include "SDL.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "sqlite3.h" #include "smallxrm.h" #include "names.h" #include "quarks.h" #include "cursorshapes.h" #include "heromesh.h" static const char schema[]= "BEGIN;" "PRAGMA APPLICATION_ID(1296388936);" "PRAGMA RECURSIVE_TRIGGERS(1);" "CREATE TABLE IF NOT EXISTS `USERCACHEINDEX`(`ID` INTEGER PRIMARY KEY, `NAME` TEXT, `TIME` INT);" "CREATE TABLE IF NOT EXISTS `USERCACHEDATA`(`ID` INTEGER PRIMARY KEY, `FILE` INT, `LEVEL` INT, `NAME` TEXT, `OFFSET` INT, `DATA` BLOB, `USERSTATE` BLOB);" "CREATE INDEX IF NOT EXISTS `USERCACHEDATA_I1` ON `USERCACHEDATA`(`FILE`, `LEVEL`) WHERE `LEVEL` IS NOT NULL;" "CREATE TEMPORARY TABLE `PICTURES`(`ID` INTEGER PRIMARY KEY, `NAME` TEXT, `OFFSET` INT);" "COMMIT;" ; sqlite3*userdb; xrm_db*resourcedb; const char*basefilename; xrm_quark optionquery[16]; Uint32 generation_number; static const char*globalclassname; static SDL_Cursor*cursor[77]; static FILE*levelfp; static FILE*solutionfp; static FILE*hamarc_fp; static long hamarc_pos; static char main_options[128]; static void hamarc_begin(FILE*fp,const char*name) { while(*name) fputc(*name++,fp); fwrite("\0\0\0\0",1,5,hamarc_fp=fp); hamarc_pos=ftell(fp); } static long hamarc_end(void) { long end=ftell(hamarc_fp); long len=end-hamarc_pos; fseek(hamarc_fp,hamarc_pos-4,SEEK_SET); fputc(len>>16,hamarc_fp); fputc(len>>24,hamarc_fp); fputc(len>>0,hamarc_fp); fputc(len>>8,hamarc_fp); fseek(hamarc_fp,end,SEEK_SET); } static void init_sql(void) { char*s; char*p; const char*v; int z; sqlite3_config(SQLITE_CONFIG_URI,0); optionquery[1]=Q_sqlMemStatus; v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:""; sqlite3_config(SQLITE_CONFIG_MEMSTATUS,(int)boolxrm(v,0)); optionquery[1]=Q_sqlSmallAllocations; v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:""; sqlite3_config(SQLITE_CONFIG_SMALL_MALLOC,(int)boolxrm(v,0)); optionquery[1]=Q_sqlCoveringIndexScan; v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:""; sqlite3_config(SQLITE_CONFIG_COVERING_INDEX_SCAN,(int)boolxrm(v,1)); if(sqlite3_initialize()) fatal("Failure to initialize SQLite.\n"); optionquery[1]=Q_sqlFile; v=xrm_get_resource(resourcedb,optionquery,optionquery,2); if(v && *v) { s=sqlite3_mprintf("%s",v); } else { v=getenv("HOME")?:"."; s=sqlite3_mprintf("%s%s.heromeshsession",v,v[strlen(v)-1]=='/'?"":"/"); } if(!s) fatal("Allocation failed\n"); if(z=sqlite3_open(s,&userdb)) fatal("Failed to open user database %s (%s)\n",s,userdb?sqlite3_errmsg(userdb):sqlite3_errstr(z)); sqlite3_free(s); optionquery[1]=Q_sqlExtensions; v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:""; sqlite3_db_config(userdb,SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,*v?1:0,&z); if(*v) { p=s=strdup(v); if(!s) fatal("Allocation failed\n"); while(*v) { if(*v==' ') { *p=0; if(*s) { p=0; if(sqlite3_load_extension(userdb,s,0,&p)) fatal("Failed to load extension '%s' (%s)\n",s,p?:"unknown error"); p=s; } v++; } else { *p++=*v++; } } *p=0; p=0; if(*s && sqlite3_load_extension(userdb,s,0,&p)) fatal("Failed to load extension '%s' (%s)\n",s,p?:"unknown error"); free(s); } if(sqlite3_exec(userdb,schema,0,0,&s)) fatal("Failed to initialize database schema (%s)\n",s?:"unknown error"); optionquery[1]=Q_sqlInit; v=xrm_get_resource(resourcedb,optionquery,optionquery,2); if(v && sqlite3_exec(userdb,v,0,0,&s)) fatal("Failed to execute user-defined SQL statements (%s)\n",s?:"unknown error"); } void set_cursor(int id) { id>>=1; if(!cursor[id]) cursor[id]=SDL_CreateCursor((void*)cursorimg+(id<<6),(void*)cursorimg+(id<<6)+32,16,16,cursorhot[id]>>4,cursorhot[id]&15); SDL_SetCursor(cursor[id]); } static void load_options(void) { const char*home=getenv("HOME")?:"."; char*nam=malloc(strlen(home)+16); FILE*fp; sprintf(nam,"%s%s.heromeshrc",home,home[strlen(home)-1]=='/'?"":"/"); fp=fopen(nam,"r"); if(!fp) fatal("Failed to open %s (%m)\n",nam); free(nam); if(xrm_load(resourcedb,fp,1)) fatal("Error while loading .heromeshrc\n"); fclose(fp); } static void read_options(int argc,char**argv) { xrm_db*db=xrm_sub(resourcedb,0,xrm_make_quark(globalclassname,0)?:xrm_anyq); while(argc--) xrm_load_line(db,*argv++,1); } static int find_globalclassname(void) { char*s=malloc(strlen(basefilename)+7); FILE*fp; if(!s) fatal("Allocation failed\n"); sprintf(s,"%s.name",basefilename); fp=fopen(s,"r"); free(s); if(!fp) return 1; s=malloc(256); if(!s) fatal("Allocation failed\n"); if(fscanf(fp," %255s",s)!=1) fatal("Unable to scan name of class set\n"); globalclassname=s; return !*s; } static int test_sql_callback(void*usr,int argc,char**argv,char**name) { int i; if(argc) printf("%s",*argv); for(i=1;i<argc;i++) printf("|%s",argv[i]); putchar('\n'); return 0; } static void test_mode(void) { Uint32 n=0; SDLKey k; SDL_Event ev; char buf[32]; const UserCommand*uc; set_cursor(XC_tcross); SDL_LockSurface(screen); draw_text(0,0,"Hello, World!",0xF0,0xFF); SDL_UnlockSurface(screen); SDL_Flip(screen); while(SDL_WaitEvent(&ev)) switch(ev.type) { case SDL_KEYDOWN: switch(ev.key.keysym.sym) { case SDLK_BACKSPACE: n/=10; snprintf(buf,30,"%u",n); SDL_WM_SetCaption(buf,buf); break; case SDLK_SPACE: n=0; SDL_WM_SetCaption("0","0"); break; case SDLK_0 ... SDLK_9: n=10*n+ev.key.keysym.sym-SDLK_0; snprintf(buf,30,"%u",n); SDL_WM_SetCaption(buf,buf); break; case SDLK_c: SDL_FillRect(screen,0,n); SDL_Flip(screen); break; case SDLK_e: n=1; goto keytest; case SDLK_g: n=0; goto keytest; case SDLK_p: sqlite3_exec(userdb,"SELECT * FROM `PICTURES`;",test_sql_callback,0,0); break; case SDLK_q: exit(0); break; } break; case SDL_MOUSEBUTTONDOWN: draw_picture(ev.button.x,ev.button.y,n); SDL_Flip(screen); break; case SDL_QUIT: exit(0); break; } fatal("An error occurred waiting for events.\n"); keytest: SDL_FillRect(screen,0,0xF0); SDL_LockSurface(screen); draw_text(1,5,n?"Edit Key":"Game Key",0xF1,0xF7); SDL_UnlockSurface(screen); SDL_EnableUNICODE(1); SDL_Flip(screen); set_cursor(XC_arrow); while(SDL_WaitEvent(&ev)) switch(ev.type) { case SDL_KEYDOWN: printf("[%d %d %d %d] ",ev.key.keysym.scancode,ev.key.keysym.sym,ev.key.keysym.mod,ev.key.keysym.unicode); goto bindingtest; case SDL_MOUSEBUTTONDOWN: printf("[%d %d %d] ",ev.button.x,ev.button.y,ev.button.button); bindingtest: uc=find_key_binding(&ev,n); switch(uc->cmd) { case 0: printf("<Unbound>\n"); break; case '^': printf("<Misc> %c\n",uc->n); break; case '=': printf("<Reset> %d\n",uc->n); break; case '-': printf("<Rewind> %d\n",uc->n); break; case '+': printf("<Advance> %d\n",uc->n); break; case '\'': printf("<Play> %s (%d)\n",heromesh_key_names[uc->n],uc->n); break; case '!': printf("<System> %s",uc->txt); break; case 's': printf("<SQL> %s",sqlite3_sql(uc->stmt)); break; default: printf("<Unknown>\n"); } break; case SDL_QUIT: exit(0); break; } } int main(int argc,char**argv) { int optind=1; fprintf(stderr,"FREE HERO MESH\n"); while(argc>optind && argv[optind][0]=='-') { int i; const char*s=argv[optind++]; if(s[1]=='-' && !s[2]) break; for(i=1;s[i];i++) main_options[s[i]&127]=1; } if(argc<=optind) fatal("usage: %s [switches] [--] basename [options...]\n",argc?argv[0]:"heromesh"); if(xrm_init(realloc)) fatal("Failed to initialize resource manager\n"); if(xrm_init_quarks(global_quarks)) fatal("Failed to initialize resource manager\n"); resourcedb=xrm_create(); if(!resourcedb) fatal("Allocation of resource database failed\n"); basefilename=argv[optind++]; if(argc>optind && argv[1][0]=='=') { globalclassname=argv[optind++]+1; } else if(find_globalclassname()) { globalclassname=strrchr(basefilename,'/'); globalclassname=globalclassname?globalclassname+1:basefilename; } load_options(); if(argc>optind) read_options(argc-optind,argv+optind); *optionquery=xrm_make_quark(globalclassname,0)?:xrm_anyq; init_sql(); load_key_bindings(); init_screen(); load_pictures(); if(main_options['T']) { test_mode(); return 0; } return 0; }