Берсеркер
2318 постов
Карма: 216
#1 07 января 2011 в 07:06
Здравствуйте.<br />Всем известна проблема с savegame в Quake2:<br />ошибки &quot;Savegame from an older version&quot; и &quot;ReadLevel: function pointers have moved&quot;.<br />Коротко о проблеме:<br />В savegame пишутся все entity. Некоторые поля entity являются ссылками на функции (F_FUNCTION). Сохраняются смещения функций относительно InitGame.<br />Если даже немного поменять код Game.dll, то адреса функций сдвинутся, или даже InitGame может поменять адрес. Предыдущие savegames аннулируются.<br />Предлагаю вам моё решение данной проблемы: вместо смещений адресов функций нужно сохранять индекс функции. Таким образом savegames становятся намного устойчивее к смене game.dll.<br />Немного кода:<br /><br />// используется для аннулирования savegames при серьезных изменениях в game.dll<br />#define SAVEGAME_VERSION 1<br />int FunctionToIndex(void *func)<br />{<br /> int i = 0;<br /><br /> i++;<br /> if (func == (void*)misc_viper_bomb_prethink)<br /> return i;<br /> i++;<br /> if (func == (void*)M_FliesOff)<br /> return i;<br />...<br /> gi.error(&quot;FunctionToIndex: unknown function\n&quot;);<br /> return 0; // shut up compiler :(<br />}<br />void *IndexToFunction(int index)<br />{<br /> int i = 0;<br /><br /> i++;<br /> if (index == i)<br /> return (void*)misc_viper_bomb_prethink;<br /><br /> i++;<br /> if (index == i)<br /> return (void*)M_FliesOff;<br />...<br /> gi.error(&quot;IndexToFunction: unknown index\n&quot;);<br /> return NULL; // shut up compiler :(<br />}
Машина несла меня через неведомые районы Галактики сквозь пространство математической реальности быстрее скорости света. (C) Фред Саберхаген.
Берсеркер
2318 постов
Карма: 216
#2 07 января 2011 в 07:06
Вы должны зарегистрировать в FunctionToIndex и IndexToFunction все функции, которые используются в вашей game.dll.<br />Это все функции, записываемые в поля F_FUNCTION <br />field_t fields&#91;] = {<br />...<br /> {&quot;prethink&quot;, FOFS(prethink), F_FUNCTION, FFL_NOSPAWN},<br /> {&quot;think&quot;, FOFS(think), F_FUNCTION, FFL_NOSPAWN},<br />...<br />}<br /><br />все функции из mmove_t:<br />mmove_t jorg_move_pain3 = {JORG_FRAME_pain301, JORG_FRAME_pain325, jorg_frames_pain3, jorg_run};<br />(в примере: jorg_run)<br /><br />все функции из mframe_t:<br />mframe_t jorg_frames_attack2 &#91;]=<br />{<br />...<br /> ai_charge, 0, jorgBFG, <br />}<br />(в примере: ai_charge и jorgBFG)<br /><br />все функции из gitem_t:<br />gitem_t itemlist&#91;] = <br />{<br />...<br />/*QUAKED item_power_shield (.3 .3 1) (-16 -16 -16) (16 16 16)*/<br /> {<br /> &quot;item_power_shield&quot;,<br /> Pickup_PowerArmor,<br /> Use_PowerArmor,<br /> Drop_PowerArmor,<br /> NULL,<br /> &quot;misc/ar3_pkup.wav&quot;,<br /> &quot;models/items/armor/shield/tris.md2&quot;, EF_ROTATE,<br /> NULL,<br />/* icon */ &quot;i_powershield&quot;,<br />/* pickup */ &quot;Power Shield&quot;,<br />/* width */ 0,<br /> 60,<br /> NULL,<br /> IT_ARMOR,<br /> 0,<br /> NULL,<br /> 0,<br />/* precache */ &quot;misc/power2.wav misc/power1.wav&quot;<br /> },<br />...<br />}<br />(в примере: Pickup_PowerArmor, Use_PowerArmor, Drop_PowerArmor)<br />
Машина несла меня через неведомые районы Галактики сквозь пространство математической реальности быстрее скорости света. (C) Фред Саберхаген.
Берсеркер
2318 постов
Карма: 216
#3 07 января 2011 в 07:07
Затем отключаем некоторые проверки и вносим прочие изменения:<br />[code]void WriteGame (char *filename, bool autosave)<br />{<br /> FILE *f;<br /> int i;<br />//// char str[16];<br /><br /> if (!autosave)<br /> SaveClientData ();<br /><br /> f = fopen (filename, &quot;wb&quot;);<br /> if (!f)<br /> gi.error (&quot;Couldn&#39;t open %s&quot;, filename);<br /><br />/// Berserker: not need more...<br />/*<br /> Com_Memset (str, 0, sizeof(str));<br /> strcpy (str, __DATE__);<br /> fwrite (str, sizeof(str), 1, f);<br />*/<br /> game.autosaved = autosave;<br /> fwrite (&amp;game, sizeof(game), 1, f);<br /> game.autosaved = false;<br /><br /> for (i=0 ; i&lt;game.maxclients ; i++)<br /> WriteClient (f, &amp;game.clients[i]);<br /><br /> fclose (f);<br />}[/code]<br />[code]void ReadGame (char *filename)<br />{<br /> FILE *f;<br /> int i;<br />//// char str[16];<br /><br /> gi.FreeTags (TAG_GAME);<br /><br /> f = fopen (filename, &quot;rb&quot;);<br /> if (!f)<br /> gi.error (&quot;Couldn&#39;t open %s&quot;, filename);<br /><br />/// Berserker: not need more<br />/*<br /> fread (str, sizeof(str), 1, f);<br /> if (strcmp (str, __DATE__))<br /> {<br /> fclose (f);<br /> gi.error (&quot;Savegame from an older version.\n&quot;);<br /> }<br />*/<br /> g_edicts =&nbsp; (edict_s *) gi.TagMalloc (game.maxentities * sizeof(g_edicts[0]), TAG_GAME);<br /> globals.edicts = g_edicts;<br /><br /> fread (&amp;game, sizeof(game), 1, f);<br /> game.clients = (gclient_s *) gi.TagMalloc (game.maxclients * sizeof(game.clients[0]), TAG_GAME);<br /> for (i=0 ; i&lt;game.maxclients ; i++)<br /> ReadClient (f, &amp;game.clients[i]);<br /><br /> fclose (f);<br />}[/code]<br />[code]void WriteLevel (char *filename)<br />{<br /> int i;<br /> edict_t *ent;<br /> FILE *f;<br />//// void *base;<br /><br /> f = fopen (filename, &quot;wb&quot;);<br /> if (!f)<br /> gi.error (&quot;Couldn&#39;t open %s&quot;, filename);<br /><br /> // write out edict size for checking<br /> i = sizeof(edict_t);<br /> fwrite (&amp;i, sizeof(i), 1, f);<br /><br />#if 0 /// Berserker: not need more<br /> // write out a function pointer for checking<br /> base = (void *)InitGame;<br /> fwrite (&amp;base, sizeof(base), 1, f);<br />#else<br /> i = SAVEGAME_VERSION;<br /> fwrite (&amp;i, sizeof(i), 1, f);<br />#endif<br /><br /> // write out level_locals_t<br /> WriteLevelLocals (f);<br /><br /> // write out all the entities<br /> for (i=0 ; i&lt;globals.num_edicts ; i++)<br /> {<br /> ent = &amp;g_edicts[i];<br /> if (!ent-&gt;inuse)<br /> continue;<br /> fwrite (&amp;i, sizeof(i), 1, f);<br /> WriteEdict (f, ent);<br /> }<br /> i = -1;<br /> fwrite (&amp;i, sizeof(i), 1, f);<br /><br /> fclose (f);<br />}[/code]<br />void ReadLevel (char *filename)<br />{<br /> int entnum;<br /> FILE *f;<br /> int i;<br />//// void *base;<br /> edict_t *ent;<br /><br /> f = fopen (filename, &quot;rb&quot;);<br /> if (!f)<br /> gi.error (&quot;Couldn&#39;t open %s\n&quot;, filename);<br /><br /> // free any dynamic memory allocated by loading the level<br /> // base state<br /> gi.FreeTags (TAG_LEVEL);<br /><br /> // wipe all the entities<br /> Com_Memset (g_edicts, 0, game.maxentities*sizeof(g_edicts[0]));<br /> globals.num_edicts = maxclients_cvar-&gt;value+1;<br /><br /> // check edict size<br /> fread (&amp;i, sizeof(i), 1, f);<br /> if (i != sizeof(edict_t))<br /> {<br /> fclose (f);<br /> gi.error (&quot;ReadLevel: mismatched edict size\n&quot;);<br /> }<br /><br />#if 0 /// Berserker: not need more<br /> // check function pointer base address<br /> fread (&amp;base, sizeof(base), 1, f);<br />#ifdef _WIN32<br /> if (base != (void *)InitGame)<br /> {<br /> fclose (f);<br /> gi.error (&quot;ReadLevel: function pointers have moved&quot;);<br /> }<br />#else<br /> gi.dprintf(&quot;Function offsets %d\n&quot;, ((byte *)base) - ((byte *)InitGame));<br />#endif<br />#else<br /> fread (&amp;i, sizeof(i), 1, f);<br /> if (i != SAVEGAME_VERSION) /// Berserker: savegame version check<br /> {<br /> fclose (f);<br /> gi.error (&quot;ReadLevel: Savegame from a different version (%i, must be %i)\n&quot;, i, SAVEGAME_VERSION);<br /> }<br />#endif<br /><br /> // load the level locals<br /> ReadLevelLocals (f);<br />...<br />}<br />void WriteField1 (FILE *f, field_t *field, byte *base)<br />{<br />...<br /> //relative to code segment<br /> case F_FUNCTION:<br />#if 0<br /> if (*(byte **)p == NULL)<br /> index = 0;<br /> else<br /> index = *(byte **)p - ((byte *)InitGame);<br /> *(int *)p = index;<br />#else<br /> if (*(byte **)p == NULL)<br /> *(int *)p = 0;<br /> else<br /> *(int *)p = FunctionToIndex(*(byte **)p); /// Berserker: translate function to index<br />#endif<br /> break;<br />...<br />}<br />void ReadField (FILE *f, field_t *field, byte *base)<br />{<br />...<br /> //relative to code segment<br /> case F_FUNCTION:<br /> index = *(int *)p;<br />#if 0<br /> if ( index == 0 )<br /> *(byte **)p = NULL;<br /> else<br /> *(byte **)p = ((byte *)InitGame) + index;<br />#else<br /> if ( index == 0 )<br /> *(byte **)p = NULL;<br /> else<br /> *(byte **)p = (byte*)IndexToFunction(index); /// Berserker: translate index to function<br />#endif<br /> break;<br />...<br />}<br />Самое муторное тут: заполнение FunctionToIndex и IndexToFunction, я потратил на это свыше часа, собрав около 500 функций, но дело это стоящее.<br />И последнее: неплохо было бы работать со своей папкой SAVE, например у меня: SAVE.q2b&nbsp; :)<br /><br />Надеюсь, идея ясна?<br />Проверил так: переместил код одного из монстров в другое место, скомпилил game.dll, предыдущая savegame загрузилась замечательно ;)<br /><br />С Рождеством!!!&nbsp; :)
Машина несла меня через неведомые районы Галактики сквозь пространство математической реальности быстрее скорости света. (C) Фред Саберхаген.
Берсеркер
2318 постов
Карма: 216
#4 07 января 2011 в 07:53
это была первая реализация. Сделаю покрасивее, сведу функции в таблицу.
Машина несла меня через неведомые районы Галактики сквозь пространство математической реальности быстрее скорости света. (C) Фред Саберхаген.
Берсеркер
2318 постов
Карма: 216
#5 07 января 2011 в 09:06
Вот код покрасивее:<br />[code]void *savefuncs&#91;] = {<br /> actor_attack,<br /> actor_dead,<br /> actor_die,<br />...<br /> Weapon_Shotgun,<br /> Weapon_SuperShotgun<br />};<br /><br />int FunctionToIndex(void *func)<br />{<br /> int i;<br /> int size = sizeof(savefuncs)/sizeof(void*);<br /> for (i=0; i&lt;size; i++)<br /> if (savefuncs[i] == func)<br /> return i;<br /> gi.error(&quot;FunctionToIndex: unknown function\n&quot;);<br /> return 0; // shut up compiler :(<br />}<br /><br /><br />void *IndexToFunction(int index)<br />{<br /> int size = sizeof(savefuncs)/sizeof(void*);<br /> if (index &lt; size)<br /> return savefuncs[index];<br /> gi.error(&quot;IndexToFunction: unknown index\n&quot;);<br /> return NULL; // shut up compiler :(<br />}[/code]
Машина несла меня через неведомые районы Галактики сквозь пространство математической реальности быстрее скорости света. (C) Фред Саберхаген.
Берсеркер
2318 постов
Карма: 216
#6 15 января 2011 в 13:44
Не учёл, что подобное шаманство требуется для F_MMOVE (адреса mmove_t)<br /><br />void WriteField1 (FILE *f, field_t *field, byte *base)<br />...<br /> //relative to data segment<br /> case F_MMOVE:<br />#if 0<br /> if (*(byte **)p == NULL)<br /> index = 0;<br /> else<br /> index = *(byte **)p - (byte *)&amp;mmove_reloc;<br /> *(int *)p = index;<br />#else<br /> if (*(byte **)p == NULL)<br /> *(int *)p = 0;<br /> else<br /> *(int *)p = MmoveToIndex(*(byte **)p); /// Berserker: translate mmove to index<br />#endif<br /> break;<br />...<br /><br /><br />void ReadField (FILE *f, field_t *field, byte *base)<br />...<br /> //relative to data segment<br /> case F_MMOVE:<br /> index = *(int *)p;<br />#if 0<br /> if (index == 0)<br /> *(byte **)p = NULL;<br /> else<br /> *(byte **)p = (byte *)&amp;mmove_reloc + index;<br />#else<br /> if ( index == 0 )<br /> *(byte **)p = NULL;<br /> else<br /> *(byte **)p = (byte*)IndexToMmove(index); /// Berserker: translate index to mmove<br />#endif<br /> break;<br />...<br /><br />[code]void *savemmoves&#91;] = {<br /> &amp;actor_move_attack,<br />...<br /> &amp;tank_move_walk<br />};<br /><br />int MmoveToIndex(void *func)<br />{<br /> int i;<br /> int size = sizeof(savemmoves)/sizeof(void*);<br /> for (i=0; i&lt;size; i++)<br /> if (savemmoves[i] == func)<br /> return i;<br /> gi.error(&quot;MmoveToIndex: unknown mmove\n&quot;);<br /> return 0; // shut up compiler :(<br />}<br /><br /><br />void *IndexToMmove(int index)<br />{<br /> int size = sizeof(savemmoves)/sizeof(void*);<br /> if (index &lt; size)<br /> return savemmoves[index];<br /> gi.error(&quot;IndexToMmove: unknown index\n&quot;);<br /> return NULL; // shut up compiler :(<br />}[/code]
Машина несла меня через неведомые районы Галактики сквозь пространство математической реальности быстрее скорости света. (C) Фред Саберхаген.