Skip to content

Commit

Permalink
File related opcodes fixes. More tests. (#88)
Browse files Browse the repository at this point in the history
* Fixes in file operations plugin. New opcodes and unit tests.

* fixup! Fixes in file operations plugin. New opcodes and unit tests.

* fixup! Fixes in file operations plugin. New opcodes and unit tests.

* fixup! Fixes in file operations plugin. New opcodes and unit tests.

* File mode handling updated.
  • Loading branch information
MiranDMC authored Mar 5, 2024
1 parent c23720b commit edbed65
Show file tree
Hide file tree
Showing 11 changed files with 1,197 additions and 32 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@
- added/fixed support of all file stream opcodes in legacy mode (Cleo3)
- new opcode **2300 ([get_file_position](https://library.sannybuilder.com/#/sa/file/2300))**
- new opcode **2301 ([read_block_from_file](https://library.sannybuilder.com/#/sa/file/2301))**
- **2302 ([resolve_filepath](https://library.sannybuilder.com/#/sa/file/2302))**
- **2303 ([get_script_filename](https://library.sannybuilder.com/#/sa/file/2303))**
- new opcode **2302 ([write_block_to_file](https://library.sannybuilder.com/#/sa/file/2302))**
- new opcode **2303 ([resolve_filepath](https://library.sannybuilder.com/#/sa/file/2303))**
- new opcode **2304 ([get_script_filename](https://library.sannybuilder.com/#/sa/file/2304))**
- new [MemoryOperations](https://github.com/cleolibrary/CLEO5/tree/master/cleo_plugins/MemoryOperations) plugin
- memory related opcodes moved from CLEO core into separated plugin
- validation of input and output parameters for all opcodes
Expand All @@ -43,6 +44,7 @@
- new opcode **2404 ([get_script_struct_just_created](https://library.sannybuilder.com/#/sa/memory/2404))**
- new opcode **2405 ([is_script_running](https://library.sannybuilder.com/#/sa/memory/2405))**
- new opcode **2406 ([get_script_struct_from_filename](https://library.sannybuilder.com/#/sa/memory/2406))**
- new opcode **2407 ([is_memory_equal](https://library.sannybuilder.com/#/sa/memory/2407))**
- new and updated opcodes
- **0B1E ([sign_extend](https://library.sannybuilder.com/#/sa/bitwise/0B1E))**
- **0DD5 ([get_game_platform](https://library.sannybuilder.com/#/sa/CLEO/0DD5))**
Expand Down Expand Up @@ -102,6 +104,7 @@


#### CLEO internal
- introduced unit test scripts
- project migrated to VS 2022
- configured game debugging settings
- plugins moved into single solution
Expand Down
69 changes: 39 additions & 30 deletions cleo_plugins/FileSystemOperations/FileSystemOperations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ class FileSystemOperations

CLEO_RegisterOpcode(0x2300, opcode_2300); // get_file_position
CLEO_RegisterOpcode(0x2301, opcode_2301); // read_block_from_file
CLEO_RegisterOpcode(0x2302, opcode_2302); // resolve_filepath
CLEO_RegisterOpcode(0x2303, opcode_2303); // get_script_filename
CLEO_RegisterOpcode(0x2302, opcode_2302); // write_block_to_file
CLEO_RegisterOpcode(0x2303, opcode_2303); // resolve_filepath
CLEO_RegisterOpcode(0x2304, opcode_2304); // get_script_filename

// register event callbacks
CLEO_RegisterCallback(eCallbackId::ScriptsFinalize, OnFinalizeScriptObjects);
Expand Down Expand Up @@ -209,6 +210,7 @@ class FileSystemOperations

if (size == 0)
{
OPCODE_SKIP_PARAMS(1); // from
return OR_CONTINUE; // done
}

Expand Down Expand Up @@ -298,37 +300,20 @@ class FileSystemOperations

if (size < 0)
{
auto info = ScriptInfoStr(thread);
SHOW_ERROR("Invalid size argument (%d) in script %s\nScript suspended.", size, info.c_str());
SHOW_ERROR("Invalid size argument (%d) in script %s\nScript suspended.", size, ScriptInfoStr(thread).c_str());
return thread->Suspend();
}

if (size == 0)
{
if (bufferSize > 0) buffer[0] = '\0';
OPCODE_CONDITION_RESULT(false);
return OR_CONTINUE;
}

std::vector<char> tmpBuff;
tmpBuff.resize(size);
auto data = tmpBuff.data();

bool ok = File::readString(handle, data, size) != nullptr;
if(!ok)
{
OPCODE_CONDITION_RESULT(false);
OPCODE_CONDITION_RESULT(true);
return OR_CONTINUE;
}

// copy into result param
int len = strlen(data);
int resultSize = min(len, bufferSize - (int)needsTerminator);

memcpy(buffer, data, resultSize);
if(resultSize < bufferSize) buffer[resultSize] = '\0'; // terminate string whenever possible
// use caller's size argument, ignoring actual target type size. Intended for legacy reasons.
bool ok = File::readString(handle, buffer, size) != nullptr;

OPCODE_CONDITION_RESULT(true);
OPCODE_CONDITION_RESULT(ok);
return OR_CONTINUE;
}

Expand Down Expand Up @@ -686,8 +671,7 @@ class FileSystemOperations

if (size < 0)
{
auto info = ScriptInfoStr(thread);
SHOW_ERROR("Invalid size argument (%d) in script %s\nScript suspended.", size, info.c_str());
SHOW_ERROR("Invalid size argument (%d) in script %s\nScript suspended.", size, ScriptInfoStr(thread).c_str());
return thread->Suspend();
}

Expand All @@ -708,17 +692,42 @@ class FileSystemOperations
return OR_CONTINUE;
}

//2302=2,%2s% = resolve_filepath %1s%
static OpcodeResult __stdcall opcode_2302(CRunningScript* thread)
//2302=3,write_block_to_file %1d% size %2d% address %3d% // IF and SET
static OpcodeResult WINAPI opcode_2302(CRunningScript* thread)
{
auto handle = READ_FILE_HANDLE_PARAM();
auto size = OPCODE_READ_PARAM_INT();
auto source = OPCODE_READ_PARAM_PTR();

if (size < 0)
{
SHOW_ERROR("Invalid size argument (%d) in script %s\nScript suspended.", size, ScriptInfoStr(thread).c_str());
return thread->Suspend();
}

if (size == 0)
{
OPCODE_CONDITION_RESULT(true);
return OR_CONTINUE;
}

auto readCount = File::write(handle, source, size);

OPCODE_CONDITION_RESULT(readCount == size);
return OR_CONTINUE;
}

//2303=2,%2s% = resolve_filepath %1s%
static OpcodeResult __stdcall opcode_2303(CRunningScript* thread)
{
OPCODE_READ_PARAM_FILEPATH(path); // it also resolves the path to absolute form

OPCODE_WRITE_PARAM_STRING(path);
return OR_CONTINUE;
}

//2303=3,%3s% = get_script_filename %1d% full_path %2d% // IF and SET
static OpcodeResult __stdcall opcode_2303(CRunningScript* thread)
//2304=3,%3s% = get_script_filename %1d% full_path %2d% // IF and SET
static OpcodeResult __stdcall opcode_2304(CRunningScript* thread)
{
auto script = OPCODE_READ_PARAM_INT();
auto fullPath = OPCODE_READ_PARAM_BOOL();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ if defined GTA_SA_DIR (
<ClCompile Include="FileUtils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\cleo_sdk\CLEO.h" />
<ClInclude Include="..\..\cleo_sdk\CLEO_Utils.h" />
<ClInclude Include="FileUtils.h" />
<ClInclude Include="Utils.h" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,16 @@
<ItemGroup>
<ClInclude Include="FileUtils.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="..\..\cleo_sdk\CLEO.h">
<Filter>cleo_sdk</Filter>
</ClInclude>
<ClInclude Include="..\..\cleo_sdk\CLEO_Utils.h">
<Filter>cleo_sdk</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="cleo_sdk">
<UniqueIdentifier>{a2c39c52-f49e-4ffe-bb0a-661ab07131b9}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
46 changes: 46 additions & 0 deletions cleo_plugins/FileSystemOperations/FileUtils.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "FileUtils.h"
#include "CLEO_Utils.h"
#include <string>

DWORD File::FUNC_fopen = 0;
Expand Down Expand Up @@ -84,6 +85,51 @@ bool File::flush(DWORD handle)

DWORD File::open(const char* filename, const char* mode, bool legacy)
{
// validate the mode argument
if (!legacy)
{
static char modeUpdated[12];
const std::string allowed = "+abcnrtwxDRST"; // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170

bool valid = false;
bool binary = false;
bool text = false;
auto modeLen = mode != nullptr ? strlen(mode) : 0;
if (modeLen > 0 && modeLen < (sizeof(modeUpdated) - 1)) // keep space for extra binary mode char
{
valid = true;

for (auto ch : std::string_view(mode))
{
if (allowed.find(ch) == std::string_view::npos)
{
valid = false;
break; // invalid character
}

if (ch == 'b') binary = true;
if (ch == 't') text = true;
}

if (binary && text) valid = false;

// By default open as binary mode.
// Generally text mode is not well documented in C and many file related functions has undefined behavior. For example 'ftell' returns invalid values.
if (valid && !binary)
{
strcpy(modeUpdated, mode);
strcat(modeUpdated, "b");
mode = modeUpdated;
}
}

if (!valid)
{
LOG_WARNING(0, "Invalid mode argument '%s' while opening file \"%s\" stream!", mode, filename);
return 0; // invalid handle
}
}

FILE* file = nullptr;
if (legacy)
{
Expand Down
25 changes: 25 additions & 0 deletions cleo_plugins/MemoryOperations/MemoryOperations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class MemoryOperations
CLEO_RegisterOpcode(0x2404, opcode_2404); // get_script_struct_just_created
CLEO_RegisterOpcode(0x2405, opcode_2405); // is_script_running
CLEO_RegisterOpcode(0x2406, opcode_2406); // get_script_struct_from_filename
CLEO_RegisterOpcode(0x2407, opcode_2407); // is_memory_equal


// register event callbacks
Expand Down Expand Up @@ -788,6 +789,30 @@ class MemoryOperations
OPCODE_CONDITION_RESULT(address != nullptr);
return OR_CONTINUE;
}

//2407=3, is_memory_equal address_a %1d% address_b %2d% size %d3%
static OpcodeResult __stdcall opcode_2407(CLEO::CScriptThread* thread)
{
auto addressA = OPCODE_READ_PARAM_PTR();
auto addressB = OPCODE_READ_PARAM_PTR();
auto size = OPCODE_READ_PARAM_INT();

if (size == 0)
{
OPCODE_CONDITION_RESULT(true);
return OR_CONTINUE;
}
if (size < 0)
{
SHOW_ERROR("Invalid '%d' size argument in script %s\nScript suspended.", size, ScriptInfoStr(thread).c_str());
return thread->Suspend();
}

auto result = memcmp(addressA, addressB, size);

OPCODE_CONDITION_RESULT(result == 0);
return OR_CONTINUE;
}
} Memory;

std::set<void*> MemoryOperations::m_allocations;
Expand Down
Loading

0 comments on commit edbed65

Please sign in to comment.