[SA] GSX - Garage Save eXtender
Para jogadores:
Agora será possível que outros mods salvem informações próprias nos carros que são salvos pela garagem, são possibilidades ilimitadas, por exemplo: mods de Auto Posto salvando quantidade de combustível, entre outras coisas.
O mod de salvar automaticamente placas dos carros foi feito como uma pequena demonstração do Garage Save eXtender.
Usos:
VehFuncs
Em andamento...
Para modders:
O GSX possui uma interface (aqui no sentido de programação do termo) para ser usada através de mods CLEO (feitos no Sanny Builder ou gta3script) e ASI.
É possível ser notificado de save/load de carros em garagens e então salvar ou carregar dados, pode ser através de scripts CLEO ou ASI.
O mod trabalha com "dois tipos de dados":
- Dados que já serão salvos no arquivo da forma como se apresentam e/ou já estavam salvos anteriormente (em caso de load game e/ou fechar e abrir garagem com um veículo).
- Dados que serão copiados para a memória específica do GSX no momento em que o carro for colocado em uma garagem e fechar a porta.
1. Esses dados já estão copiados para memória a própria do GSX e permanecerão os mesmos caso não sejam alterados pelos dados do 2º tipo ou não forem atualizados manualmente por algum script.
2. O GSX guarda apenas o endereço desse tipo de dado e o tamanho dele e quando o carro guardado, esse ponteiro (endereço de memória) será lido e a quantidade especificada no tamanho será copiada para a memória própria do GSX, caso já exista um dado pelo mesmo nome, será atualizado.
A identificação desses dados na memória do GSX é pelo nome atribuído a ele pelo scripter.


Explicando a API:
Código: Selecionar tudo
static const wchar_t *GSXPath = L"gsx.asi";É o nome do arquivo do .asi, se preferir, pode inserir um arquivo de configuração que carrega esse nome, caso pense que alguém pode mudar o nome do .asi principal.
Código: Selecionar tudo
struct journalNewsÉ a struct interna que guarda registros de veículos salvos/carregados.
Código: Selecionar tudo
struct apiCarNotifyÉ a struct que deve ser enviada para a função getNewCarForeach para retornar um dos veículos recentemente adicionados ou carregados em uma garagem (veremos mais a frente).
Código: Selecionar tudo
struct externalCallbackStructureÉ a struct que sua função de callback deve aceitar como parâmetro.
Código: Selecionar tudo
void(__cdecl externalCbFun_t)(const externalCallbackStructure*);Forma de protótipo que sua função de callback deve ter.
As funções a seguir NÃO devem ser usadas na função WinMain.
Código: Selecionar tudo
void addNotifyCallback(externalCbFun_t fun);Adiciona uma função sua na lista de callbacks do mod, será sempre chamada quando um carro for carregado ou salvo em uma garagem, é chamada uma vez por carro, então se sua garagem tiver dois carros, será chamada duas vezes e assim por diante.
Código: Selecionar tudo
void addNotifyTempCallback(externalCbFun_t fun);Funcionamento quase igual a função acima, com a diferença que a lista de funções adicionadas por essa é limpa quando recarrega o save ou carrega outro save.
Código: Selecionar tudo
bool getNewCarForeach(size_t &i, apiCarNotify &out);Caso não seja escolhida a forma de callbacks automáticos, pode usar esta que funciona sendo chamada manualmente e retorna um carro por vez (caso tenha algum mais recente que a última vez checada).
Código: Selecionar tudo
size_t i = 0;
...
void yourfunction()
{
using namespace GSX;
apiCarNotify out;
while (GSXAPI::getNewCarForeach(i, out))
{
out.veh; // vehicle
switch(out.status)
{
case GSX::LOAD_CAR:
//
break;
case GSX::SAVE_CAR:
//
break;
default:
break;
}
}
}
void frameThings()
{
...
yourfunction();
...
}Código: Selecionar tudo
{$CLEO}
0AA2: 0@ = load_library "grgsaveextra.asi" // IF and SET
0AA4: 6@ = get_proc_address "getNewCarGrgForeach" library 0@ // IF and SET
const
LOAD_CAR = 0
SAVE_CAR = 1
end
while true
wait 0
// Outras coisas
0AC7: 13@ = var 16@ offset // carInfo - 8 bytes
0AC6: 14@ = label @positionRegister offset // 8 bytes
while true
0AA7: call_function 6@ num_params 2 pop 2 13@ 14@ retorno 15@ // getNewCarGrgForeach / GSX::getNewCarForeach(i, out)
if 15@ <> 0
jf break
0AD0: show_formatted_text_lowpriority "Teste car %.8X status %d" time 1500 16@ 17@ // status: 0 carregado / 1 salvo
wait 2000
end
// Outras coisas
end
:carInfo
hex
00 00 00 00 // veh ptr
00 00 00 00 // status
end
:positionRegister
hex
00 00 00 00 00 00 00 00
endCódigo: Selecionar tudo
void setDataToSaveLater(CVehicle *veh, const char *name, int size, void *ptr, bool forceCopyNow);Aqui precisa ser observado uma coisa, existem duas formas de salvar os dados no mod:
1ª - os dados serão salvos pelo GSX automaticamente quando o carro for salvo. Para isso acontecer, sempre que o carro for carregado é necessário chamar a função acima com o endereço atualizado do dado a ser salvo.
2ª - os dados são salvos instantaneamente, ficando disponível para as funções que testam se existe dado a ser carregado, etc. pode ser feito usando a função pushDirectlyToSavedData (explicação abaixo), ou usando true no parâmetro forceCopyNow.
OBS: ao usar setDataToSaveLater SEM usar true no parâmetro forceCopyNow e salvar o carro, será considerado um dado temporário que estará disponível a próxima vez que o carro ser carregado, mas se não for renovado através de qualquer uma das funções de salvar dados, ele não será salvo de novo.
Use a função de forçar cópia do dado imediatamente com cuidado, principalmente se estiver mexendo com dados grandes.
Código: Selecionar tudo
void pushDirectlyToSavedData(CVehicle *veh, const char *name, int size, void *ptr);Salva o dado diretamente nos dados que podem ser carregados através de funções de load (Não atualiza o arquivo de save, ele só é atualizado quando o salvar o jogo).
Em andamento...
Downloads:
Download do GSX - Garage Save eXtender
Mod salvar placas dos carros automaticamente (depende do gsx.asi)
Source do mod de salvar as placas dos carros automaticamente:
Código: Selecionar tudo
//#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <injector\injector.hpp>
#include <injector\assembly.hpp>
#include <injector\calling.hpp>
#include <injector\saving.hpp>
#include <game_sa\CVehicle.h>
#include <game_sa\CMatrix.h>
#include <game_sa\RenderWare.h>
#include <string>
#include "CStoredCar.h"
#include "GSXAPI.h"
static auto getVehicleModelInfoByID = injector::cstd<CVehicleModelInfo*(int id)>::call<0x00403DA0>;
auto CustomCarPlate_TextureCreate = injector::thiscall<bool(CVehicle *, CVehicleModelInfo *)>::call<0x006D10E0>;
void callback(const GSX::externalCallbackStructure *test)
{
using namespace GSX;
if (test->veh)
{
switch (test->status)
{
case GSX::LOAD_CAR:
{
if (dataToLoadExists(test->veh, "carplate"))
{
const char *plateText = (const char *)getSavedData(test->veh, "carplate");
if (plateText != nullptr)
{
CVehicleModelInfo *info = getVehicleModelInfoByID(test->veh->m_wModelIndex);
strncpy(info->m_plateText, plateText, 8u);
if (CustomCarPlate_TextureCreate(test->veh, info))
{
}
else
{
//MessageBoxA(0, "error plate", "", 0);
}
}
}
break;
}
case GSX::SAVE_CAR:
{
if (test->veh->m_pCustomCarPlate)
{
setDataToSaveLater(test->veh, "carplate", 8, test->veh->m_pCustomCarPlate->name, true);
}
break;
}
default:
break;
}
}
}
void onload(int id)
{
static bool loaded = false;
if (!loaded)
{
loaded = true;
addNotifyCallback(callback);
}
}
void inject()
{
injector::save_manager::on_load(onload);
}
BOOL WINAPI DllMain(
_In_ HINSTANCE hinstDLL,
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved
){
if (fdwReason == DLL_PROCESS_ATTACH) inject();
return true;
}No download do GSX estão os arquivos da API para C++, Moonloader e alguns exemplos.
É possível usar em CLEO (exemplo bem simples, farei um melhor):
Código: Selecionar tudo
{$CLEO}
0AA2: 0@ = load_library "gsx.asi" // IF and SET
0AA4: 1@ = get_proc_address "dataToSaveLaterExists" library 0@ // IF and SET
0AA4: 2@ = get_proc_address "dataToLoadExists" library 0@ // IF and SET
0AA4: 3@ = get_proc_address "getLoadDataByVehPtr" library 0@ // IF and SET
0AA4: 4@ = get_proc_address "setDataPtrToSaveVehPtr" library 0@ // IF and SET
0AA4: 5@ = get_proc_address "pushDirectlyToSavedData" library 0@ // IF and SET
0AA4: 6@ = get_proc_address "getNewCarGrgForeach" library 0@ // IF and SET
while true
wait 0
0AC7: 13@ = var 16@ offset // carInfo - 8 bytes
0AC6: 14@ = label @positionRegister offset // 8 bytes
while true
0AA7: call_function 6@ num_params 2 pop 2 13@ 14@ retorno 15@
if 15@ <> 0
jf break
// status: 0 carregado / 1 salvo
0AD0: show_formatted_text_lowpriority "Teste car %.8X status %d" time 1500 16@ 17@
wait 2000
end
if 0256: 0
jf continue
if 00DF: actor $PLAYER_ACTOR driving
jf continue
0811: 8@ = actor $PLAYER_ACTOR used_car
0A97: 9@ = car 8@ struct
//0AA7: call_function 1@ num_params 2 pop 2 "carplate" 9@ retorno 11@
0AA7: call_function 2@ num_params 2 pop 2 "carplate" 9@ retorno 12@
if 12@ <> 0
then
0AA7: call_function 3@ num_params 2 pop 2 "carplate" 9@ retorno 10@
0AD0: show_formatted_text_lowpriority "Teste %s opcode" time 10 10@
end
end
0A93: end_custom_thread
:buflicenseplate
hex
00 00 00 00 00 00 00 00 00 00 00
end
:carInfo
hex
00 00 00 00 // veh ptr
00 00 00 00 // status
end
:positionRegister
hex
00 00 00 00 00 00 00 00
endEstruturas:
Código: Selecionar tudo
struct externalCallbackStructure
{
CVehicle *veh;
int32_t status;
CStoredCar *gameStoredData;
};- veh - ponteiro para estrutura do veículo.
- status - status do veículo.
- 0 Carregado
- 1 Salvo
- 0 Carregado
- gameStoredData ponteiro para uma estrutura que o jogo usa nas garagens, na versão atual não recomendo usar elas, o hook do GSX funciona pouco antes de todos os dados do veículo serem aplicados a essa estrutura.
Código: Selecionar tudo
struct apiCarNotify
{
CVehicle *veh;
int32_t status;
};- veh - ponteiro para estrutura do veículo.
- status - status do veículo.
- 0 Carregado
- 1 Salvo
- 0 Carregado
Código: Selecionar tudo
void (__cdecl externalCbFun_t)(const externalCallbackStructure*)Protótipo que uma função deve ter para ser enviada ao GSX, não possui retorno e recebe um ponteiro para uma estrutura constante externalCallbackStructure.
Código: Selecionar tudo
int __cdecl addNotifyCallback(externalCbFun_t fun)Adiciona um callback que poderá ser chamado a qualquer momento enquanto o jogo estiver aberto, ideal para mods ASI e outros que não mudam de endereço ao recarregar o jogo.
Retorna referência para callback.
- fun endereço da função.
Código: Selecionar tudo
void __cdecl removeNotifyCallback(int cbRef)Remove um callback adicionado com a função addNotifyCallback.
- cbRef referência do callback.
Código: Selecionar tudo
int __cdecl addNotifyTempCallback(externalCbFun_t fun)Adiciona um callback que funciona como o anterior, entretanto ele é removido da lista quando o jogo recarrega.
Retorna referência para callback.
- fun endereço da função.
Código: Selecionar tudo
void __cdecl removeNotifyTempCallback(int cbRef)Remove um callback adicionado com a função addNotifyTempCallback.
- cbRef referência do callback.
Código: Selecionar tudo
int __cdecl getNewCarGrgForeach(size_t *i, apiCarNotify *out)O GSX guarda um pequeno histórico de veículos salvos e/ou carregados que pode ser conferido de um a um.
- i ponteiro para uma variável que guardará o último item lido do histórico, recomendo deixar alocado 64 bits (8 bytes) para ela.
- out envie o endereço para uma memória com tamanho de 8 bytes. O GSX gravará uma estrutura do tipo apiCarNotify nessa memória.
Essa função retorna 1 caso tenha veículo salvo/carregado, 0 caso não tenha.
Dados do tipo 2 (ponteiros para dados que serão copiados assim que o carro for guardado na garagem):
Código: Selecionar tudo
void __cdecl setDataToSaveLaterVehPtr(CVehicle *veh, const char *name, int size, void *ptr, bool forceCopyNow)Adiciona um endereço para uma lista de dados a serem copiados quando o veículo for salvo em uma garagem.
ATENÇÃO: Tenha certeza que os dados estarão disponíveis no momento em que o carro estiver sendo salvo.
- veh ponteiro para o veículo
- name nome do dado a ser salvo (ATENÇÃO: substituirá dados com o mesmo nome)
- size tamanho em bytes do dado.
- ptr endereço do dado
- forceCopyNow faz uma cópia interna do dado nesse exato momento para outra lista com os dados salvos.
Código: Selecionar tudo
int __cdecl getDataToSaveSize(CVehicle *veh, const char *name)Retorna o tamanho de um dado adicionado com a função setDataToSaveLaterVehPtr.
- veh ponteiro para o veículo
- name nome do dado a ser procurado
Retorna o tamanho do dado caso encontrado, -1 caso não encontrado.
Código: Selecionar tudo
int __cdecl dataToSaveLaterExists(CVehicle *veh, const char *name)Verifica se um dado com certo nome existe na lista de dados a serem copiados futuramente. (Dados adicionados com a função setDataToSaveLaterVehPtr)
- veh ponteiro para o veículo
- name nome do dado a ser procurado
Retorna true ou false.
Código: Selecionar tudo
void __cdecl removeToSaveLaterVehPtr(CVehicle *veh, const char *name)Remove um dado adicionado pela função setDataToSaveLaterVehPtr.
- veh ponteiro para o veículo
- name nome do dado a ser removido
Dados do tipo 1 (dados que são copiados no momento em que a função é chamada):
Código: Selecionar tudo
void __cdecl pushDirectlyToSavedData(CVehicle *veh, const char *name, int size, void *ptr)Copia o dado para a memória de dados salvos do GSX sobre o veículo.
- veh ponteiro para o veículo
- name nome do dado a ser salvo (ATENÇÃO: substituirá dados com o mesmo nome)
- size tamanho em bytes do dado.
- ptr endereço do dado
Código: Selecionar tudo
void __cdecl removeToLoadDataVehPtr(CVehicle *veh, const char *name)Apaga dados que foram salvos anteriormente ou dados adicionados com a função pushDirectlyToSavedData ou com a setDataToSaveLaterVehPtr usando forceCopyNow como true.
- veh ponteiro para o veículo
- name nome do dado a ser removido
Código: Selecionar tudo
int __cdecl dataToLoadExists(CVehicle *veh, const char *name)Verifica se existe um dado salvo anteriormente ou com a função pushDirectlyToSavedData ou com a setDataToSaveLaterVehPtr usando forceCopyNow como true.
- veh ponteiro para o veículo
- name nome do dado a ser procurado
Retorna true ou false.
Código: Selecionar tudo
void* __cdecl getLoadDataByVehPtr(CVehicle *veh, const char *name)Retorna o ponteiro de um dado salvo anteriormente ou com a função pushDirectlyToSavedData ou com a setDataToSaveLaterVehPtr usando forceCopyNow como true.
ATENÇÃO: Esse endereço pode mudar caso os dados sejam alterados, copie o conteúdo dele logo após a função retornar.
- veh ponteiro para o veículo
- name nome do dado a ser procurado
Retorna o endereço do dado caso sucesso ou 0 caso não encontrado.
Código: Selecionar tudo
int __cdecl getDataToLoadSize(CVehicle *veh, const char *name)Retorna o tamanho de um dado salvo anteriormente ou com a função pushDirectlyToSavedData ou com a setDataToSaveLaterVehPtr usando forceCopyNow como true.
- veh ponteiro para o veículo
- name nome do dado a ser procurado
Retorna o tamanho do dado caso encontrado, -1 caso não encontrado.
ATENÇÃO: As funções abaixo podem não existir em versões de testes iniciais:
Código: Selecionar tudo
const char * __cdecl GSX_getCompileTime()Retorna a data e a hora que o GSX.asi foi compilado em string.
Código: Selecionar tudo
const char * __cdecl GSX_getVersionString()Retorna a versão do GSX.asi em string.
Código: Selecionar tudo
float __cdecl GSX_getVersionNum()Retorna a versão do GSX.asi em float. Recomendo usar a versão em string.
Nomes reservados para dados gravados pelo próprio GSX.asi:
_hash é uma hash criada na primeira detecção do carro pelo código.
- Sempre estará presente e com 8 bytes na atual versão.
Caso não seja encontrado ou tenha um tamanho diferente, por favor avise.
Tópico no Fórum BMS
gsx-data, usar gsx em scripts lua facilmente
Editado pela última vez por Fabio em 13 Abr 2020, 00:17, em um total de 23 vezes.
