tools/gdc-psyfork/gdc.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Sun May 27 11:36:49 2012 -0400 (2012-05-27)
changeset 3513 50b5b98b3400
child 3574 b51285ca203f
permissions -rw-r--r--
Added "psyfork" of gdc and example scripts to tools/ (NPOTB).
skamonkey@3513
     1
#include <stdio.h>
skamonkey@3513
     2
#include <unistd.h>
skamonkey@3513
     3
#include <dlfcn.h>
skamonkey@3513
     4
#include <fcntl.h>
skamonkey@3513
     5
#include <math.h>
skamonkey@3513
     6
#include "gdc.h"
skamonkey@3513
     7
#include "GameConfigs.h"
skamonkey@3513
     8
#include "MemoryUtils.h"
skamonkey@3513
     9
skamonkey@3513
    10
MemoryUtils mu;
skamonkey@3513
    11
skamonkey@3513
    12
char *game = NULL;
skamonkey@3513
    13
char *engine = NULL;
skamonkey@3513
    14
char *game_binary = NULL;
skamonkey@3513
    15
char *engine_binary = NULL;
skamonkey@3513
    16
char *wgame_binary = NULL;
skamonkey@3513
    17
char *wengine_binary = NULL;
skamonkey@3513
    18
skamonkey@3513
    19
int main(int argc, char **argv)
skamonkey@3513
    20
{
skamonkey@3513
    21
	char *gamedata = NULL;
skamonkey@3513
    22
	bool debug = false;
skamonkey@3513
    23
skamonkey@3513
    24
	opterr = 0;
skamonkey@3513
    25
	int opt;
skamonkey@3513
    26
	while ((opt = getopt(argc, argv, "b:de:f:g:x:w:y:")) != -1)
skamonkey@3513
    27
		switch (opt)
skamonkey@3513
    28
		{
skamonkey@3513
    29
			case 'd':
skamonkey@3513
    30
				debug = true;
skamonkey@3513
    31
				break;
skamonkey@3513
    32
			case 'g':
skamonkey@3513
    33
				game = optarg;
skamonkey@3513
    34
				break;
skamonkey@3513
    35
skamonkey@3513
    36
			case 'e':
skamonkey@3513
    37
				engine = optarg;
skamonkey@3513
    38
				break;
skamonkey@3513
    39
skamonkey@3513
    40
			case 'f':
skamonkey@3513
    41
				gamedata = optarg;
skamonkey@3513
    42
				break;
skamonkey@3513
    43
skamonkey@3513
    44
			case 'b':
skamonkey@3513
    45
				game_binary = optarg;
skamonkey@3513
    46
				break;
skamonkey@3513
    47
skamonkey@3513
    48
			case 'x':
skamonkey@3513
    49
				engine_binary = optarg;
skamonkey@3513
    50
				break;
skamonkey@3513
    51
skamonkey@3513
    52
			case 'w':
skamonkey@3513
    53
				wgame_binary = optarg;
skamonkey@3513
    54
				break;
skamonkey@3513
    55
skamonkey@3513
    56
			case 'y':
skamonkey@3513
    57
				wengine_binary = optarg;
skamonkey@3513
    58
				break;
skamonkey@3513
    59
skamonkey@3513
    60
			case '?':
skamonkey@3513
    61
				printf("Usage: ./gdc -g <game> -e <engine name> -f <gamedata file> -b <game binary> -x <engine binary> -w <win game binary> -y <win engine binary>\n");
skamonkey@3513
    62
				return 0;
skamonkey@3513
    63
skamonkey@3513
    64
			default:
skamonkey@3513
    65
				printf("WHAT\n");
skamonkey@3513
    66
				return 0;
skamonkey@3513
    67
		}
skamonkey@3513
    68
skamonkey@3513
    69
	if (!game || !engine || !gamedata || !game_binary || !engine_binary || !wgame_binary || !wengine_binary)
skamonkey@3513
    70
	{
skamonkey@3513
    71
		printf("Usage: ./gdc -g <game> -e <engine name> -f <gamedata file> -b <game binary> -x <engine binary> -w <win game binary> -y <win engine binary>\n");
skamonkey@3513
    72
		return 0;
skamonkey@3513
    73
	}
skamonkey@3513
    74
skamonkey@3513
    75
	printf("Game: %s\n", game);
skamonkey@3513
    76
	if (debug)
skamonkey@3513
    77
	{
skamonkey@3513
    78
		printf("Engine: %s\nGame binary: %s\nEngine binary: %s\nWin game binary: %s\nWin engine binary: %s\n",
skamonkey@3513
    79
			engine, game_binary, engine_binary, wgame_binary, wengine_binary
skamonkey@3513
    80
			);
skamonkey@3513
    81
	}
skamonkey@3513
    82
	printf("Gamedata: %s\n\n", gamedata);
skamonkey@3513
    83
skamonkey@3513
    84
	void *ghandle;
skamonkey@3513
    85
	ghandle = dlopen(game_binary, RTLD_LAZY);
skamonkey@3513
    86
	if (!ghandle)
skamonkey@3513
    87
	{
skamonkey@3513
    88
		printf("Couldn't open %s (%s)\n", game_binary, dlerror());
skamonkey@3513
    89
			return 0;
skamonkey@3513
    90
	}
skamonkey@3513
    91
	void *ehandle;
skamonkey@3513
    92
	ehandle = dlopen(engine_binary, RTLD_LAZY);
skamonkey@3513
    93
	if (!ehandle)
skamonkey@3513
    94
	{
skamonkey@3513
    95
		printf("Couldn't open %s (%s)\n", engine_binary, dlerror());
skamonkey@3513
    96
			return 0;
skamonkey@3513
    97
	}
skamonkey@3513
    98
skamonkey@3513
    99
	int wgfile = open(wgame_binary, O_RDONLY);
skamonkey@3513
   100
	if (wgfile == -1)
skamonkey@3513
   101
	{
skamonkey@3513
   102
		printf("Couldn't open %s\n", wgame_binary);
skamonkey@3513
   103
		return 0;
skamonkey@3513
   104
	}
skamonkey@3513
   105
skamonkey@3513
   106
	int wefile = open(wengine_binary, O_RDONLY);
skamonkey@3513
   107
	if (wefile == -1)
skamonkey@3513
   108
	{
skamonkey@3513
   109
		printf("Couldn't open %s\n", wengine_binary);
skamonkey@3513
   110
		return 0;
skamonkey@3513
   111
	}
skamonkey@3513
   112
skamonkey@3513
   113
	CGameConfig gc;
skamonkey@3513
   114
	char err[512];
skamonkey@3513
   115
	if (!gc.EnterFile(gamedata, err, sizeof(err)))
skamonkey@3513
   116
	{
skamonkey@3513
   117
		printf("%s: %s\n", gamedata, err);
skamonkey@3513
   118
		return 0;
skamonkey@3513
   119
	}
skamonkey@3513
   120
skamonkey@3513
   121
	CGameConfig symbols;
skamonkey@3513
   122
	if (!symbols.EnterFile("symbols.txt", err, sizeof(err)))
skamonkey@3513
   123
	{
skamonkey@3513
   124
		printf("symbols.txt: %s\n", err);
skamonkey@3513
   125
		return 0;
skamonkey@3513
   126
	}
skamonkey@3513
   127
skamonkey@3513
   128
	const char *vtsym = symbols.GetKeyValue("vtsym");
skamonkey@3513
   129
	if (!vtsym)
skamonkey@3513
   130
	{
skamonkey@3513
   131
		printf("Couldn't find vtsym\n");
skamonkey@3513
   132
		return 0;
skamonkey@3513
   133
	}
skamonkey@3513
   134
skamonkey@3513
   135
	void **vt = (void**) mu.ResolveSymbol(ghandle, vtsym);
skamonkey@3513
   136
	if (!vt)
skamonkey@3513
   137
	{
skamonkey@3513
   138
		printf("Couldn't find vtable %s\n", vtsym);
skamonkey@3513
   139
		dlclose(ghandle);
skamonkey@3513
   140
		dlclose(ehandle);
skamonkey@3513
   141
		close(wgfile);
skamonkey@3513
   142
		close(wefile);
skamonkey@3513
   143
skamonkey@3513
   144
		return 0;
skamonkey@3513
   145
	}
skamonkey@3513
   146
skamonkey@3513
   147
skamonkey@3513
   148
	for (list<Offset>::iterator it = gc.m_Offsets.begin(); it != gc.m_Offsets.end(); it++)
skamonkey@3513
   149
	{
skamonkey@3513
   150
		if (debug)
skamonkey@3513
   151
		{
skamonkey@3513
   152
			printf("DEBUG %s - l: %d w: %d\n", it->name, it->lin, it->win);
skamonkey@3513
   153
		}
skamonkey@3513
   154
skamonkey@3513
   155
		const char *symbol = symbols.GetKeyValue(it->name);
skamonkey@3513
   156
		if (symbol)
skamonkey@3513
   157
		{
skamonkey@3513
   158
			int newOffset;
skamonkey@3513
   159
			char symvt[128];
skamonkey@3513
   160
			strcpy(symvt, it->name);
skamonkey@3513
   161
			strcat(symvt, "_vt");
skamonkey@3513
   162
			const char *newvtsym = symbols.GetKeyValue(symvt);
skamonkey@3513
   163
			if (newvtsym && newvtsym[0])
skamonkey@3513
   164
			{
skamonkey@3513
   165
				void **newvt = (void**) mu.ResolveSymbol(ghandle, newvtsym);
skamonkey@3513
   166
			        if (!newvt)
skamonkey@3513
   167
				{
skamonkey@3513
   168
					printf("O: %-22s - can't find, skipping\n", symvt);
skamonkey@3513
   169
					continue;
skamonkey@3513
   170
				}
skamonkey@3513
   171
skamonkey@3513
   172
				newOffset = findVFunc(ghandle, newvt, symbol);
skamonkey@3513
   173
			}
skamonkey@3513
   174
			else
skamonkey@3513
   175
			{
skamonkey@3513
   176
				newOffset = findVFunc(ghandle, vt, symbol);
skamonkey@3513
   177
			}
skamonkey@3513
   178
skamonkey@3513
   179
			if (newOffset == it->lin)
skamonkey@3513
   180
			{
skamonkey@3513
   181
				printf("O: %-22s - GOOD.    current [ w: %3d, l: %3d ].\n", it->name, it->win, it->lin);
skamonkey@3513
   182
			}
skamonkey@3513
   183
			else
skamonkey@3513
   184
			{
skamonkey@3513
   185
				printf("O: %-22s - CHANGED. old [ w: %3d, l: %3d ]. new [ w: %3d, l: %3d ].\n",
skamonkey@3513
   186
					it->name,
skamonkey@3513
   187
					it->win, it->lin,
skamonkey@3513
   188
					newOffset - (it->lin - it->win), newOffset
skamonkey@3513
   189
					);
skamonkey@3513
   190
			}
skamonkey@3513
   191
		}
skamonkey@3513
   192
		else // !symbol
skamonkey@3513
   193
		{
skamonkey@3513
   194
			printf("O: %-22s - no Linux symbol, skipping\n", it->name);
skamonkey@3513
   195
		}
skamonkey@3513
   196
	}
skamonkey@3513
   197
skamonkey@3513
   198
	printf("\nWindows offsets are (semi-)wild guesses!\n\n");
skamonkey@3513
   199
skamonkey@3513
   200
	for (list<Sig>::iterator it = gc.m_Sigs.begin(); it != gc.m_Sigs.end(); it++)
skamonkey@3513
   201
	{
skamonkey@3513
   202
		if (debug)
skamonkey@3513
   203
		{
skamonkey@3513
   204
			printf("DEBUG %s - %s - l: %s\n", it->name, it->lib == Server ? "server" : "engine", it->lin);
skamonkey@3513
   205
		}
skamonkey@3513
   206
skamonkey@3513
   207
		const char *linSymbol = it->lin;
skamonkey@3513
   208
		const char *winSymbol = it->win;
skamonkey@3513
   209
		bool hasLinux   = (linSymbol && linSymbol[0]);
skamonkey@3513
   210
		bool hasWindows = (winSymbol && winSymbol[0]);
skamonkey@3513
   211
		if (!hasLinux && !hasWindows)
skamonkey@3513
   212
		{
skamonkey@3513
   213
			printf("S: %-22s - hasn't linux nor windows data, skipping\n", it->name);
skamonkey@3513
   214
			continue;
skamonkey@3513
   215
		}
skamonkey@3513
   216
		
skamonkey@3513
   217
		int  winFile;
skamonkey@3513
   218
		void *linHandle;
skamonkey@3513
   219
		if (it->lib == Server)
skamonkey@3513
   220
		{
skamonkey@3513
   221
			winFile   = wgfile;
skamonkey@3513
   222
			linHandle = ghandle;
skamonkey@3513
   223
		}
skamonkey@3513
   224
		else if (it->lib == Engine)
skamonkey@3513
   225
		{
skamonkey@3513
   226
			winFile   = wefile;
skamonkey@3513
   227
			linHandle = ehandle;
skamonkey@3513
   228
		}
skamonkey@3513
   229
		else
skamonkey@3513
   230
		{
skamonkey@3513
   231
			printf("S: %-22s - isn't from server nor engine, skipping\n", it->name);
skamonkey@3513
   232
			continue;
skamonkey@3513
   233
		}
skamonkey@3513
   234
		
skamonkey@3513
   235
		bool foundLinux   = (!hasLinux   || checkSigStringL(linHandle, linSymbol));
skamonkey@3513
   236
		bool foundWindows = (!hasWindows || checkSigStringW(winFile,   winSymbol));
skamonkey@3513
   237
			
skamonkey@3513
   238
		// Prepare for ternery ABUSE
skamonkey@3513
   239
		
skamonkey@3513
   240
		if (foundLinux && foundWindows)
skamonkey@3513
   241
		{
skamonkey@3513
   242
			// too much clutter to print current data if it matches anyway, unlike with offsets
skamonkey@3513
   243
			/*
skamonkey@3513
   244
			printf("%-22s (%s) - GOOD. current:\n\t[w:%s]\n\t[l:%s]\n",
skamonkey@3513
   245
				it->name,
skamonkey@3513
   246
				(it->lib == Server) ? "server" : "engine",
skamonkey@3513
   247
				winSymbol ? winSymbol : "",
skamonkey@3513
   248
				linSymbol ? linSymbol : ""
skamonkey@3513
   249
				);
skamonkey@3513
   250
			*/
skamonkey@3513
   251
			printf("S: %-22s (%s) - GOOD.\n",
skamonkey@3513
   252
				it->name,
skamonkey@3513
   253
				(it->lib == Server) ? "server" : "engine"
skamonkey@3513
   254
				);
skamonkey@3513
   255
		}
skamonkey@3513
   256
		else
skamonkey@3513
   257
		{
skamonkey@3513
   258
			// extra \n at end is intentional to add buffer after possibly long sigs
skamonkey@3513
   259
			printf("S: %-22s (%s) - w: %s - l: %s\n%s%s%s%s%s%s%s%s\n",
skamonkey@3513
   260
					it->name,
skamonkey@3513
   261
					(it->lib == Server) ? "server" : "engine",
skamonkey@3513
   262
					hasWindows ? (foundWindows ? "GOOD" : "CHANGED") : "UNKNOWN",
skamonkey@3513
   263
					hasLinux   ? (foundLinux   ? "GOOD" : "CHANGED") : "UNKNOWN",
skamonkey@3513
   264
					
skamonkey@3513
   265
					hasWindows ? "\tcurrent - w: " : "",
skamonkey@3513
   266
					(hasWindows && foundWindows) ? "    (found) \"" : (hasWindows ? "(NOT found) \"" : ""),
skamonkey@3513
   267
					(hasWindows && winSymbol)  ? winSymbol : "",
skamonkey@3513
   268
					hasWindows ? "\"\n" : "",
skamonkey@3513
   269
					
skamonkey@3513
   270
					hasLinux ? "\tcurrent - l: " : "",
skamonkey@3513
   271
					(hasLinux && foundLinux) ? "    (found) \"" : (hasLinux ? "(NOT found) \"" : ""),
skamonkey@3513
   272
					(hasLinux && linSymbol)  ? linSymbol : "",
skamonkey@3513
   273
					hasLinux ? "\"\n" : ""
skamonkey@3513
   274
					);
skamonkey@3513
   275
			
skamonkey@3513
   276
		}
skamonkey@3513
   277
		
skamonkey@3513
   278
		//  j/k, no way that anyone could have been prepared for that
skamonkey@3513
   279
	}
skamonkey@3513
   280
skamonkey@3513
   281
	return 0;
skamonkey@3513
   282
}
skamonkey@3513
   283
skamonkey@3513
   284
bool checkSigStringW(int file, const char* symbol)
skamonkey@3513
   285
{
skamonkey@3513
   286
	void *addr = NULL;
skamonkey@3513
   287
	bool isAt = (symbol[0] == '@');
skamonkey@3513
   288
	// we can't support this on windows from here
skamonkey@3513
   289
	if (isAt)
skamonkey@3513
   290
		return false;
skamonkey@3513
   291
	
skamonkey@3513
   292
	unsigned char real_sig[511];
skamonkey@3513
   293
	size_t real_bytes = UTIL_DecodeHexString(real_sig, sizeof(real_sig), symbol);
skamonkey@3513
   294
skamonkey@3513
   295
	if (real_bytes >= 1)
skamonkey@3513
   296
	{
skamonkey@3513
   297
		addr = mu.FindPatternInFile(file, (char*)real_sig, real_bytes);
skamonkey@3513
   298
	}
skamonkey@3513
   299
skamonkey@3513
   300
	if (addr)
skamonkey@3513
   301
		return true;
skamonkey@3513
   302
	
skamonkey@3513
   303
	return false;
skamonkey@3513
   304
}
skamonkey@3513
   305
skamonkey@3513
   306
bool checkSigStringL(void *handle, const char* symbol)
skamonkey@3513
   307
{
skamonkey@3513
   308
	void *addr = NULL;
skamonkey@3513
   309
	bool isAt = (symbol[0] == '@');
skamonkey@3513
   310
	
skamonkey@3513
   311
	if (isAt)
skamonkey@3513
   312
	{
skamonkey@3513
   313
		addr = mu.ResolveSymbol(handle, symbol + 1);
skamonkey@3513
   314
	}
skamonkey@3513
   315
	else
skamonkey@3513
   316
	{
skamonkey@3513
   317
		unsigned char real_sig[511];
skamonkey@3513
   318
		size_t real_bytes = UTIL_DecodeHexString(real_sig, sizeof(real_sig), symbol);
skamonkey@3513
   319
skamonkey@3513
   320
		if (real_bytes >= 1)
skamonkey@3513
   321
		{
skamonkey@3513
   322
			addr = mu.FindPattern(handle, (char*)real_sig, real_bytes);
skamonkey@3513
   323
		}
skamonkey@3513
   324
	}
skamonkey@3513
   325
skamonkey@3513
   326
	if (addr)
skamonkey@3513
   327
		return true;
skamonkey@3513
   328
	
skamonkey@3513
   329
	return false;
skamonkey@3513
   330
}
skamonkey@3513
   331
skamonkey@3513
   332
/* 
skamonkey@3513
   333
   takes a mangled member function symbol and returns the position where the function name and parameters start
skamonkey@3513
   334
   01234567890123456789012345678
skamonkey@3513
   335
   _ZN9CTFPlayer12ForceRespawnEv
skamonkey@3513
   336
                ^13            ^28
skamonkey@3513
   337
*/
skamonkey@3513
   338
void findFuncPos(const char *sym, int &func, int &params)
skamonkey@3513
   339
{
skamonkey@3513
   340
	int i = 0;
skamonkey@3513
   341
	while ((sym[i] < '0') || (sym[i] > '9')) i++;
skamonkey@3513
   342
	int classLen = atoi(sym + i);
skamonkey@3513
   343
	func = i + (int)ceil(log10(classLen)) + classLen;
skamonkey@3513
   344
	int funcLen = atoi(sym + func);
skamonkey@3513
   345
	params = func + (int)ceil(log10(funcLen)) + funcLen;
skamonkey@3513
   346
}
skamonkey@3513
   347
skamonkey@3513
   348
int findVFunc(void *handle, void **vt, const char *symbol)
skamonkey@3513
   349
{
skamonkey@3513
   350
	int offset = 1;
skamonkey@3513
   351
	int funcPos, paramPos;
skamonkey@3513
   352
	findFuncPos(symbol, funcPos, paramPos);
skamonkey@3513
   353
skamonkey@3513
   354
skamonkey@3513
   355
skamonkey@3513
   356
	for (int i = 0; i < 1000; i++)
skamonkey@3513
   357
	{
skamonkey@3513
   358
		void *pFunc = vt[i];
skamonkey@3513
   359
		const char *s;
skamonkey@3513
   360
skamonkey@3513
   361
		if (!(s = mu.ResolveAddr(handle, pFunc)))
skamonkey@3513
   362
			continue;
skamonkey@3513
   363
skamonkey@3513
   364
		if ((i > 1) && (strncmp(s, "_ZTI", 4) == 0))
skamonkey@3513
   365
			break;
skamonkey@3513
   366
skamonkey@3513
   367
skamonkey@3513
   368
		int tempFuncPos, tempParamPos;
skamonkey@3513
   369
		findFuncPos(s, tempFuncPos, tempParamPos);
skamonkey@3513
   370
skamonkey@3513
   371
		if (strcmp(s + tempFuncPos, symbol + funcPos) == 0)
skamonkey@3513
   372
		{
skamonkey@3513
   373
			offset = i;
skamonkey@3513
   374
			break;
skamonkey@3513
   375
		}
skamonkey@3513
   376
	}
skamonkey@3513
   377
skamonkey@3513
   378
	return offset - 2;
skamonkey@3513
   379
}
skamonkey@3513
   380
skamonkey@3513
   381
unsigned int strncopy(char *dest, const char *src, size_t count)
skamonkey@3513
   382
{
skamonkey@3513
   383
	if (!count)
skamonkey@3513
   384
	{
skamonkey@3513
   385
		return 0;
skamonkey@3513
   386
	}
skamonkey@3513
   387
skamonkey@3513
   388
	char *start = dest;
skamonkey@3513
   389
	while ((*src) && (--count))
skamonkey@3513
   390
	{
skamonkey@3513
   391
		*dest++ = *src++;
skamonkey@3513
   392
	}
skamonkey@3513
   393
	*dest = '\0';
skamonkey@3513
   394
skamonkey@3513
   395
	return (dest - start);
skamonkey@3513
   396
}
skamonkey@3513
   397
skamonkey@3513
   398
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
skamonkey@3513
   399
{
skamonkey@3513
   400
	va_list ap;
skamonkey@3513
   401
	va_start(ap, fmt);
skamonkey@3513
   402
	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
skamonkey@3513
   403
	va_end(ap);
skamonkey@3513
   404
skamonkey@3513
   405
	if (len >= maxlength)
skamonkey@3513
   406
	{
skamonkey@3513
   407
		buffer[maxlength - 1] = '\0';
skamonkey@3513
   408
		return (maxlength - 1);
skamonkey@3513
   409
	}
skamonkey@3513
   410
	else
skamonkey@3513
   411
	{
skamonkey@3513
   412
		return len;
skamonkey@3513
   413
	}
skamonkey@3513
   414
}
skamonkey@3513
   415
skamonkey@3513
   416
size_t UTIL_DecodeHexString(unsigned char *buffer, size_t maxlength, const char *hexstr)
skamonkey@3513
   417
{
skamonkey@3513
   418
	size_t written = 0;
skamonkey@3513
   419
	size_t length = strlen(hexstr);
skamonkey@3513
   420
skamonkey@3513
   421
	for (size_t i = 0; i < length; i++)
skamonkey@3513
   422
	{
skamonkey@3513
   423
		if (written >= maxlength)
skamonkey@3513
   424
			break;
skamonkey@3513
   425
		
skamonkey@3513
   426
		buffer[written++] = hexstr[i];
skamonkey@3513
   427
		if (hexstr[i] == '\\' && hexstr[i + 1] == 'x')
skamonkey@3513
   428
		{
skamonkey@3513
   429
			if (i + 3 >= length)
skamonkey@3513
   430
				continue;
skamonkey@3513
   431
			/* Get the hex part. */
skamonkey@3513
   432
			char s_byte[3];
skamonkey@3513
   433
			int r_byte;
skamonkey@3513
   434
			s_byte[0] = hexstr[i + 2];
skamonkey@3513
   435
			s_byte[1] = hexstr[i + 3];
skamonkey@3513
   436
			s_byte[2] = '\0';
skamonkey@3513
   437
			/* Read it as an integer */
skamonkey@3513
   438
			sscanf(s_byte, "%x", &r_byte);
skamonkey@3513
   439
			/* Save the value */
skamonkey@3513
   440
			buffer[written - 1] = r_byte;
skamonkey@3513
   441
			/* Adjust index */
skamonkey@3513
   442
			i += 3;
skamonkey@3513
   443
		}
skamonkey@3513
   444
	}
skamonkey@3513
   445
skamonkey@3513
   446
	return written;
skamonkey@3513
   447
}
skamonkey@3513
   448