载入中...
搜索中...
未找到
FileApi.cpp
浏览该文件的文档.
2
3#include "common/Module.h"
4#include "common/Runtime.h"
7
8#include <simplesquirrel/simplesquirrel.hpp>
9#include <squirrel.h>
10
11#include <cstring>
12#include <fstream>
13#include <iterator>
14#include <string>
15
16#include "physfs/physfs.h"
17
18namespace eve {
19namespace filesystem {
20namespace physfs {
21namespace {
22
23bool readViaPhysFS(const char* path, std::string& out) {
24 if (!PHYSFS_isInit() || !PHYSFS_exists(path)) return false;
25 PHYSFS_file* f = PHYSFS_openRead(path);
26 if (!f) return false;
27 const PHYSFS_uint64 len = PHYSFS_fileLength(f);
28 out.resize(static_cast<size_t>(len));
29 if (len > 0 && PHYSFS_readBytes(f, out.data(), len) != static_cast<PHYSFS_sint64>(len)) {
30 PHYSFS_close(f);
31 out.clear();
32 return false;
33 }
34 PHYSFS_close(f);
35 return true;
36}
37
38bool readViaOS(const char* path, std::string& out) {
39 std::ifstream ifs(path, std::ios::binary);
40 if (!ifs) return false;
41 out.assign(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
42 return true;
43}
44
45bool readFileContent(const char* path, std::string& out) {
46 if (readViaPhysFS(path, out) && !out.empty()) return true;
47 out.clear();
48 return readViaOS(path, out);
49}
50
51// `load.nut` loads the game with `dofile("main.nut")`. A raw `sq_compilebuffer`
52// compiles `import` edges but never instantiates them, so the import opcode
53// fails with "module was not instantiated". Give the chunk a `game:/` URI and
54// run the same ScriptCompiler + instantiate path EditorHost uses.
55std::string scriptUriFromPath(const char* path) {
56 std::string uri = path ? path : "";
57 for (char& ch : uri) {
58 if (ch == '\\') ch = '/';
59 }
60 const size_t scheme = uri.find(":/");
61 const bool drive = uri.size() >= 2 && uri[1] == ':';
62 if (scheme != std::string::npos && !drive) return uri;
63 while (uri.rfind("./", 0) == 0) uri.erase(0, 2);
64 while (!uri.empty() && uri.front() == '/') uri.erase(0, 1);
65 return "game:/" + uri;
66}
67
68// Shared implementation of dofile (execute) and loadfile (return closure).
69SQInteger compileAndMaybeRun(HSQUIRRELVM vm, bool run) {
70 const SQChar* path = nullptr;
71 if (SQ_FAILED(sq_getstring(vm, 2, &path)) || !path)
72 return sq_throwerror(vm, "expected a script filename");
73
74 std::string content;
75 if (!readFileContent(path, content))
76 return sq_throwerror(vm, "cannot open script");
77
78 // Squirrel's sq_readscripfile skips a UTF-8 BOM; sq_compilebuffer does not.
79 if (content.size() >= 3 && (unsigned char)content[0] == 0xEF &&
80 (unsigned char)content[1] == 0xBB && (unsigned char)content[2] == 0xBF)
81 content.erase(0, 3);
82
83 const std::string uri = scriptUriFromPath(path);
84 if (SQ_FAILED(eve::script::ScriptCompiler::compileBuffer(vm, content.c_str(),
85 static_cast<SQInteger>(content.size()),
86 uri.c_str(), SQTrue)))
87 return SQ_ERROR;
88
90 if (!rt) rt = ModuleManager::runtime();
91 if (rt) {
92 try {
93 rt->scriptModules().instantiateDependencies(uri);
94 } catch (const std::exception& error) {
95 sq_pop(vm, 1);
96 return sq_throwerror(vm, error.what());
97 }
98 }
99
100 if (!run) return 1; // leave the closure on the stack
101
102 sq_pushroottable(vm);
103 if (SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue))) return SQ_ERROR;
104 sq_pop(vm, 1);
105 return 0;
106}
107
108SQInteger sq_dofile(HSQUIRRELVM vm) { return compileAndMaybeRun(vm, true); }
109SQInteger sq_loadfile(HSQUIRRELVM vm) { return compileAndMaybeRun(vm, false); }
110
111// --- file() handle: a lightweight PhysFS-backed file table ---
112
113bool getThisStr(HSQUIRRELVM vm, const SQChar* field, std::string& out) {
114 sq_pushstring(vm, field, -1);
115 if (SQ_SUCCEEDED(sq_get(vm, 1))) {
116 const SQChar* s = nullptr;
117 if (SQ_SUCCEEDED(sq_getstring(vm, -1, &s)) && s) out = s;
118 sq_pop(vm, 1);
119 return !out.empty();
120 }
121 return false;
122}
123
124SQInteger sq_f_close(HSQUIRRELVM vm) {
125 sq_pushnull(vm);
126 return 1;
127}
128
129SQInteger sq_f_read(HSQUIRRELVM vm) {
130 std::string data;
131 if (!getThisStr(vm, _SC("_data"), data)) return sq_throwerror(vm, "file not readable");
132 sq_pushstring(vm, data.c_str(), static_cast<SQInteger>(data.size()));
133 return 1;
134}
135
136SQInteger sq_f_getSize(HSQUIRRELVM vm) {
137 std::string data;
138 if (!getThisStr(vm, _SC("_data"), data)) return sq_throwerror(vm, "file not readable");
139 sq_pushinteger(vm, static_cast<SQInteger>(data.size()));
140 return 1;
141}
142
143SQInteger sq_f_isEOF(HSQUIRRELVM vm) {
144 sq_pushbool(vm, SQTrue);
145 return 1;
146}
147
148SQInteger sq_f_tell(HSQUIRRELVM vm) {
149 sq_pushinteger(vm, 0);
150 return 1;
151}
152
153SQInteger sq_f_seek(HSQUIRRELVM vm) {
154 sq_pushbool(vm, SQTrue);
155 return 1;
156}
157
158SQInteger sq_f_getFilename(HSQUIRRELVM vm) {
159 std::string path;
160 getThisStr(vm, _SC("_path"), path);
161 sq_pushstring(vm, path.c_str(), static_cast<SQInteger>(path.size()));
162 return 1;
163}
164
165SQInteger sq_file(HSQUIRRELVM vm) {
166 const SQChar* path = nullptr;
167 const SQChar* mode = nullptr;
168 if (SQ_FAILED(sq_getstring(vm, 2, &path)) || SQ_FAILED(sq_getstring(vm, 3, &mode)))
169 return sq_throwerror(vm, "file: expected (path, mode)");
170
171 std::string m(mode ? mode : "");
172 const bool writing = m.find('w') != std::string::npos || m.find('a') != std::string::npos;
173
174 std::string content;
175 if (!writing) {
176 // A missing file must fail here, otherwise `file_exists()` (which tries
177 // file(path, "r")) returns an empty handle and reports true.
178 if (!readFileContent(path, content))
179 return sq_throwerror(vm, "cannot open file");
180 }
181
182 sq_newtable(vm); // the handle table (becomes 'this')
183 // Squirrel 的 sq_newslot(-3) 需要 [table, key, value] 三层栈;
184 // 只推一个值会把目标错指到调用帧的 mode 参数上(非表对象会被静默跳过),
185 // 导致句柄永远没有 _path/_data,read()/getSize() 报 "file not readable"。
186 sq_pushstring(vm, "_path", -1); // key
187 sq_pushstring(vm, path, -1); // value
188 sq_newslot(vm, -3, SQFalse);
189 sq_pushstring(vm, "_data", -1); // key
190 sq_pushstring(vm, content.c_str(), static_cast<SQInteger>(content.size())); // value
191 sq_newslot(vm, -3, SQFalse);
192
193 struct { const SQChar* name; SQFUNCTION fn; } methods[] = {
194 {_SC("close"), sq_f_close},
195 {_SC("read"), sq_f_read},
196 {_SC("readString"), sq_f_read},
197 {_SC("getSize"), sq_f_getSize},
198 {_SC("isEOF"), sq_f_isEOF},
199 {_SC("tell"), sq_f_tell},
200 {_SC("seek"), sq_f_seek},
201 {_SC("getFilename"), sq_f_getFilename},
202 };
203 for (const auto& mth : methods) {
204 sq_pushstring(vm, mth.name, -1);
205 sq_newclosure(vm, mth.fn, 0);
206 sq_newslot(vm, -3, SQFalse);
207 }
208 return 1;
209}
210
211} // namespace
212
213void installScriptFileApi(ssq::VM& vm) {
214 HSQUIRRELVM h = vm.getHandle();
215 const SQInteger top = sq_gettop(h);
216
217 struct { const SQChar* name; SQFUNCTION fn; } globals[] = {
218 {_SC("dofile"), sq_dofile},
219 {_SC("loadfile"), sq_loadfile},
220 {_SC("file"), sq_file},
221 };
222 for (const auto& g : globals) {
223 sq_pushroottable(h);
224 sq_pushstring(h, g.name, -1);
225 sq_newclosure(h, g.fn, 0);
226 sq_newslot(h, -3, SQTrue);
227 sq_pop(h, 1);
228 }
229
230 sq_settop(h, top);
231}
232
233} // namespace physfs
234} // namespace filesystem
235} // namespace eve
SQInteger top
const std::string & s
struct SQVM * HSQUIRRELVM
HSQUIRRELVM vm
Definition ECS.cpp:20
tensor::Graph g
Definition GpuGraph.cpp:7
int h
std::string name
std::weak_ptr< Run > run
Definition OnnxGpgpu.cpp:25
std::string error
Definition Package.cpp:60
float f
std::string path
Definition PlayHost.cpp:110
std::string uri
float m[16]
static Runtime * runtime()
Active runtime associated with the last expose() call, or nullptr. @ownership Borrowed; the embedding...
Definition Module.cpp:292
static Runtime * current() noexcept
The runtime currently at the top of this thread's runtime stack, or nullptr.
Definition Runtime.cpp:495
static SQRESULT compileBuffer(HSQUIRRELVM vm, const SQChar *source, SQInteger size, const SQChar *sourceName, SQBool raiseError)
Unified raw-VM entry used by REPL, debugger, MCP, and editor adapters.
void installScriptFileApi(ssq::VM &vm)
用 PhysFS 版本覆盖 Squirrel 的 file / dofile / loadfile 全局, 使脚本与资源从已挂载的游戏源(真实目录或内存挂载的 .eve 归档)解析; PhysFS 中不...
Definition FileApi.cpp:213
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
SettlementPipeline::Stage fn