FTE plugin for support to run berkelium. This is an in-game web browser, that can be used with playfilm or with custom shaders. CSQC also has some control over it, for feeding it mouse/keyboard events.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4109 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
b937bb8161
commit
459c99efec
4 changed files with 567 additions and 3 deletions
189
plugins/berkelium/berkelium.vcproj
Normal file
189
plugins/berkelium/berkelium.vcproj
Normal file
|
@ -0,0 +1,189 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="berkelium"
|
||||
ProjectGUID="{4877586B-E85B-4DF8-BCCE-59D31514D240}"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
EnableFunctionLevelLinking="true"
|
||||
BrowseInformation="1"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="berkelium/berkelium.lib"
|
||||
OutputFile="C:\Games\Quake\fte\plugins\$(ProjectName)x86.dll"
|
||||
LinkIncremental="2"
|
||||
GenerateManifest="false"
|
||||
ModuleDefinitionFile="..\plugin.def"
|
||||
GenerateDebugInformation="true"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
EmbedManifest="false"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\plugapi.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\plugin.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\qvm_api.c"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath="..\plugin.def"
|
||||
>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
309
plugins/berkelium/plugapi.cpp
Normal file
309
plugins/berkelium/plugapi.cpp
Normal file
|
@ -0,0 +1,309 @@
|
|||
#include "../plugin.h"
|
||||
#include "../engine.h"
|
||||
|
||||
#include "berkelium/Berkelium.hpp"
|
||||
#include "berkelium/Window.hpp"
|
||||
#include "berkelium/WindowDelegate.hpp"
|
||||
#include "berkelium/Context.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
class decctx
|
||||
{
|
||||
public:
|
||||
Berkelium::Window *wnd;
|
||||
int width;
|
||||
int height;
|
||||
unsigned int *buffer;
|
||||
bool repainted;
|
||||
};
|
||||
|
||||
class MyDelegate : public Berkelium::WindowDelegate
|
||||
{
|
||||
private:
|
||||
decctx *ctx;
|
||||
|
||||
virtual void onPaint(Berkelium::Window *wini, const unsigned char *bitmap_in, const Berkelium::Rect &bitmap_rect, size_t num_copy_rects, const Berkelium::Rect *copy_rects, int dx, int dy, const Berkelium::Rect& scroll_rect)
|
||||
{
|
||||
int i;
|
||||
// handle paint events...
|
||||
if (dx || dy)
|
||||
{
|
||||
int y;
|
||||
int t = scroll_rect.top();
|
||||
int l = scroll_rect.left();
|
||||
int w = scroll_rect.width();
|
||||
int h = scroll_rect.height();
|
||||
if (dy > 0)
|
||||
{
|
||||
//if we're moving downwards, we need to write the bottom before the top (so we don't overwrite the data before its copied)
|
||||
for (y = t+h-1; y >= t; y--)
|
||||
{
|
||||
if (y < 0 || y >= ctx->height)
|
||||
continue;
|
||||
if (y+dy < 0 || y+dy >= ctx->height)
|
||||
continue;
|
||||
memmove(ctx->buffer + ((l+dx) + (y+dy)*ctx->width), ctx->buffer + (l + y*ctx->width), w*4);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//moving upwards requires we write the top row first
|
||||
for (y = t; y < t+h; y++)
|
||||
{
|
||||
if (y < 0 || y >= ctx->height)
|
||||
continue;
|
||||
if (y+dy < 0 || y+dy >= ctx->height)
|
||||
continue;
|
||||
|
||||
memmove(ctx->buffer + ((l+dx) + (y+dy)*ctx->width), ctx->buffer + (l + y*ctx->width), w*4);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < num_copy_rects; i++)
|
||||
{
|
||||
unsigned int *out = ctx->buffer;
|
||||
const unsigned int *in = (const unsigned int*)bitmap_in;
|
||||
int x, y;
|
||||
int t = copy_rects[i].top() - bitmap_rect.top();
|
||||
int l = copy_rects[i].left() - bitmap_rect.left();
|
||||
int r = copy_rects[i].width() + l;
|
||||
int b = copy_rects[i].height() + t;
|
||||
unsigned int instride = bitmap_rect.width() - (r - l);
|
||||
unsigned int outstride = ctx->width - (r - l);
|
||||
|
||||
out += copy_rects[i].left();
|
||||
out += copy_rects[i].top() * ctx->width;
|
||||
|
||||
in += l;
|
||||
in += t * bitmap_rect.width();
|
||||
|
||||
for (y = t; y < b; y++)
|
||||
{
|
||||
for (x = l; x < r; x++)
|
||||
{
|
||||
*out++ = *in++;
|
||||
}
|
||||
in += instride;
|
||||
out += outstride;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->repainted = true;
|
||||
}
|
||||
|
||||
public:
|
||||
MyDelegate(decctx *_ctx) : ctx(_ctx) {};
|
||||
};
|
||||
|
||||
static void *Dec_Create(char *medianame)
|
||||
{
|
||||
/*only respond to berkelium: media prefixes*/
|
||||
if (!strncmp(medianame, "berkelium:", 10))
|
||||
medianame = medianame + 10;
|
||||
else if (!strcmp(medianame, "berkelium"))
|
||||
medianame = "about:blank";
|
||||
else if (!strncmp(medianame, "http:", 5))
|
||||
medianame = medianame; //and direct http requests.
|
||||
else
|
||||
return NULL;
|
||||
|
||||
decctx *ctx = new decctx();
|
||||
|
||||
Berkelium::Context* context = Berkelium::Context::create();
|
||||
ctx->width = 1024;
|
||||
ctx->height = 1024;
|
||||
ctx->repainted = false;
|
||||
ctx->buffer = (unsigned int*)malloc(ctx->width * ctx->height * 4);
|
||||
ctx->wnd = Berkelium::Window::create(context);
|
||||
delete context;
|
||||
|
||||
ctx->wnd->setDelegate(new MyDelegate(ctx));
|
||||
|
||||
|
||||
ctx->wnd->resize(ctx->width, ctx->height);
|
||||
std::string url = medianame;
|
||||
ctx->wnd->navigateTo(Berkelium::URLString::point_to(url.data(), url.length()));
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void *Dec_DisplayFrame(void *vctx, qboolean nosound, enum uploadfmt_e *fmt, int *width, int *height)
|
||||
{
|
||||
decctx *ctx = (decctx*)vctx;
|
||||
*fmt = TF_BGRA32;
|
||||
*width = ctx->width;
|
||||
*height = ctx->height;
|
||||
|
||||
if (!ctx->repainted)
|
||||
return NULL;
|
||||
ctx->repainted = false;
|
||||
return ctx->buffer;
|
||||
}
|
||||
static void Dec_Destroy(void *vctx)
|
||||
{
|
||||
decctx *ctx = (decctx*)vctx;
|
||||
ctx->wnd->destroy();
|
||||
delete ctx;
|
||||
}
|
||||
static void Dec_GetSize (void *vctx, int *width, int *height)
|
||||
{
|
||||
decctx *ctx = (decctx*)vctx;
|
||||
*width = ctx->width;
|
||||
*height = ctx->height;
|
||||
}
|
||||
static qboolean Dec_SetSize (void *vctx, int width, int height)
|
||||
{
|
||||
decctx *ctx = (decctx*)vctx;
|
||||
if (ctx->width == width || ctx->height == height)
|
||||
return qtrue; //no point
|
||||
|
||||
//there's no resize notification. apparently javascript cannot resize windows. yay.
|
||||
unsigned int *newbuf = (unsigned int*)realloc(ctx->buffer, width * height * sizeof(*newbuf));
|
||||
if (!newbuf)
|
||||
return qfalse; //failed?!?
|
||||
ctx->width = width;
|
||||
ctx->height = height;
|
||||
ctx->buffer = newbuf;
|
||||
ctx->repainted = false;
|
||||
|
||||
ctx->wnd->resize(ctx->width, ctx->height);
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
static void Dec_CursorMove (void *vctx, float posx, float posy)
|
||||
{
|
||||
decctx *ctx = (decctx*)vctx;
|
||||
ctx->wnd->mouseMoved((int)(posx * ctx->width), (int)(posy * ctx->height));
|
||||
}
|
||||
static void Dec_Key (void *vctx, int code, int unicode, int isup)
|
||||
{
|
||||
decctx *ctx = (decctx*)vctx;
|
||||
wchar_t wchr = unicode;
|
||||
|
||||
if (code >= 178 && code < 178+6)
|
||||
ctx->wnd->mouseButton(code - 178, !isup);
|
||||
else if (code == 188 || code == 189)
|
||||
ctx->wnd->mouseWheel(0, (code==189)?-30:30);
|
||||
else
|
||||
{
|
||||
if (code)
|
||||
{
|
||||
int mods = 0;
|
||||
if (code == 127)
|
||||
code = 0x2e;
|
||||
ctx->wnd->keyEvent(!isup, mods, code, 0);
|
||||
}
|
||||
if (unicode && !isup)
|
||||
{
|
||||
wchar_t chars[2] = {unicode};
|
||||
if (unicode == 127 || unicode == 8 || unicode == 9 || unicode == 27)
|
||||
return;
|
||||
ctx->wnd->textEvent(chars, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Dec_ChangeStream(void *vctx, char *newstream)
|
||||
{
|
||||
decctx *ctx = (decctx*)vctx;
|
||||
|
||||
if (!strncmp(newstream, "cmd:", 4))
|
||||
{
|
||||
if (!strcmp(newstream+4, "refresh"))
|
||||
ctx->wnd->refresh();
|
||||
else if (!strcmp(newstream+4, "transparent"))
|
||||
ctx->wnd->setTransparent(true);
|
||||
else if (!strcmp(newstream+4, "opaque"))
|
||||
ctx->wnd->setTransparent(true);
|
||||
else if (!strcmp(newstream+4, "stop"))
|
||||
ctx->wnd->stop();
|
||||
else if (!strcmp(newstream+4, "back"))
|
||||
ctx->wnd->goBack();
|
||||
else if (!strcmp(newstream+4, "forward"))
|
||||
ctx->wnd->goForward();
|
||||
else if (!strcmp(newstream+4, "cut"))
|
||||
ctx->wnd->cut();
|
||||
else if (!strcmp(newstream+4, "copy"))
|
||||
ctx->wnd->copy();
|
||||
else if (!strcmp(newstream+4, "paste"))
|
||||
ctx->wnd->paste();
|
||||
else if (!strcmp(newstream+4, "del"))
|
||||
ctx->wnd->del();
|
||||
else if (!strcmp(newstream+4, "selectall"))
|
||||
ctx->wnd->selectAll();
|
||||
}
|
||||
else if (!strncmp(newstream, "javascript:", 11))
|
||||
{
|
||||
newstream+=11;
|
||||
int len = mblen(newstream, MB_CUR_MAX);
|
||||
wchar_t *wchrs = (wchar_t *)malloc((len+1)*2);
|
||||
len = mbstowcs(wchrs, newstream, len);
|
||||
ctx->wnd->executeJavascript(Berkelium::WideString::point_to(wchrs, len));
|
||||
free(wchrs);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string url = newstream;
|
||||
ctx->wnd->navigateTo(Berkelium::URLString::point_to(url.data(), url.length()));
|
||||
}
|
||||
}
|
||||
|
||||
static bool Dec_Init(void)
|
||||
{
|
||||
if (!Berkelium::init(Berkelium::FileString::empty()))
|
||||
{
|
||||
Con_Printf("Couldn't initialize Berkelium.\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int Dec_Tick(int *args)
|
||||
{
|
||||
//need to keep it ticking over, if any work is to be done.
|
||||
Berkelium::update();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Dec_Shutdown(int *args)
|
||||
{
|
||||
//force-kill all.
|
||||
Berkelium::destroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static media_decoder_funcs_t decoderfuncs =
|
||||
{
|
||||
Dec_Create,
|
||||
Dec_DisplayFrame,
|
||||
NULL,//doneframe
|
||||
Dec_Destroy,
|
||||
NULL,//rewind
|
||||
|
||||
Dec_CursorMove,
|
||||
Dec_Key,
|
||||
Dec_SetSize,
|
||||
Dec_GetSize,
|
||||
Dec_ChangeStream
|
||||
};
|
||||
|
||||
int Plug_Init(int *args)
|
||||
{
|
||||
if (!Plug_Export("Tick", Dec_Tick))
|
||||
{
|
||||
Con_Printf("Berkelium plugin failed: Engine doesn't support Tick feature\n");
|
||||
return false;
|
||||
}
|
||||
if (!Plug_Export("Shutdown", Dec_Shutdown))
|
||||
{
|
||||
Con_Printf("Berkelium plugin warning: Engine doesn't support Shutdown feature\n");
|
||||
// return false;
|
||||
}
|
||||
if (!Plug_ExportNative("Media_VideoDecoder", &decoderfuncs))
|
||||
{
|
||||
Con_Printf("Berkelium plugin failed: Engine doesn't support media decoder plugins\n");
|
||||
return false;
|
||||
}
|
||||
return Dec_Init();
|
||||
}
|
52
plugins/berkelium/readme.txt
Normal file
52
plugins/berkelium/readme.txt
Normal file
|
@ -0,0 +1,52 @@
|
|||
simple media decoding plugin that decodes web pages instead of videos...
|
||||
See installation instructions at the end of this file.
|
||||
|
||||
|
||||
This means you can do: playfilm berkelium:http://google.com
|
||||
And you'll get google.com displayed as if it were a cinematic or so.
|
||||
The engine will pass mouse+keyboard events to such cinematics so you can interact with it.
|
||||
|
||||
|
||||
You can also use the 'videomap' term within a shader, if you want to put some youtube video on a wall or something. Here's an example of such a shader:
|
||||
muh_bad
|
||||
{
|
||||
{
|
||||
map $lightmap
|
||||
}
|
||||
{
|
||||
videomap berkelium:http://google.com
|
||||
blendfunc filter
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CSQC is able to interact with videos by:
|
||||
1: find the name of the shader
|
||||
string texname = getsurfacetexture(trace_ent, getsurfacenearpoint(trace_ent, trace_endpos));
|
||||
2: send a key event
|
||||
gecko_keyevent(texname, keycode, keydown);
|
||||
3: move the mouse cursor
|
||||
gecko_mousemove(texname, x, y);
|
||||
note that the x and y values should be between 0 and 1. Surface resolution is not relevent here.
|
||||
4: resize the image
|
||||
gecko_resize(texname, 1024, 1024);
|
||||
if you're using it in 2d, make sure it matches the pixel width of the screen where it'll be displayed. it'll get fuzzy otherwise.
|
||||
Size matters. Beware of sites that do not resize images for different resolutions (ie: most of them).
|
||||
5: you can query the image with:
|
||||
vector v = gecko_get_texture_extent(texname);
|
||||
6: you can purge resources with:
|
||||
gecko_destroy(texname);
|
||||
this will 'end' the video. when the shader is next displayed it'll reset from the original url.
|
||||
7: you can change the url with:
|
||||
gecko_navigate(texname, "http://fteqw.com");
|
||||
|
||||
|
||||
|
||||
Compiling the plugin for Windows:
|
||||
In the berkelium 7z file that you should have already downloaded, there'll be an includes and a lib directory. Stick the contents of both of those in the empty doubled berkelium directory on the fte svn.
|
||||
The msvc project file should now be usable. You will likely want to change the linker target path to get picked up by the engine automatically.
|
||||
|
||||
Windows Installation instructions:
|
||||
Download Berkelium from here: http://berkelium.org/ (http://github.com/sirikata/berkelium/downloads)
|
||||
You should get a 7z file that contains a bin directory. Copy the contents of that bin directory to your quake directory.
|
||||
The FTE-specific plugin 'berkeliumx86.dll' should be placed as 'quake/fte/plugins/berkeliumx86.dll'.
|
|
@ -50,6 +50,11 @@ void BadBuiltin(void);
|
|||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include "math.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//DLLs need a wrapper to add the extra parameter and call a boring function.
|
||||
#define EBUILTIN(t, n, args) extern int BUILTIN_##n; t n args
|
||||
#define TEST
|
||||
|
@ -75,8 +80,13 @@ int snprintf(char *buffer, size_t maxlen, const char *format, ...);
|
|||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
typedef enum {false, true} qboolean;
|
||||
#ifdef __cplusplus
|
||||
typedef enum {qfalse, qtrue} qboolean;
|
||||
#else
|
||||
typedef enum {qfalse, qtrue} qboolean;
|
||||
#define false qfalse
|
||||
#define true qtrue
|
||||
#endif
|
||||
typedef void *qhandle_t;
|
||||
typedef float vec3_t[3];
|
||||
typedef void* funcptr_t;
|
||||
|
@ -109,7 +119,7 @@ EBUILTIN(qboolean, Plug_ExportNative, (char *funcname, void *func)); //set up in
|
|||
EBUILTIN(void, Con_Print, (char *text)); //on to main console.
|
||||
|
||||
EBUILTIN(void, Con_SubPrint, (char *subname, char *text)); //on to sub console.
|
||||
EBUILTIN(void, Con_RenameSub, (char *old, char *new)); //rename a console.
|
||||
EBUILTIN(void, Con_RenameSub, (char *oldname, char *newname)); //rename a console.
|
||||
EBUILTIN(int, Con_IsActive, (char *conname));
|
||||
EBUILTIN(void, Con_SetActive, (char *conname));
|
||||
EBUILTIN(void, Con_Destroy, (char *conname));
|
||||
|
@ -256,4 +266,8 @@ void Info_RemoveNonStarKeys (char *start);
|
|||
void Info_SetValueForKey (char *s, char *key, char *value, int maxsize);
|
||||
void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue