Fscanf and PHYSFS

is there any way to use fscanf and fprintf on a PHYSFS text file handle?

You would have to use some varargs and call PHYSFS_read() or PHYSFS_write()
appropriately. I just looked and found this old implementation of such:
http://icculus.org/pipermail/physfs/2003-February/000148.html

Be warned that there are some issues with that patch (C++, vsprintf vs.
vsnprintf, arg count limits, and perhaps more).

Jonny DOn Wed, Jun 25, 2014 at 11:25 PM, javierecf wrote:

is there any way to use fscanf and fprintf on a PHYSFS text file handle?


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

gonna check it out, thanks.

2014-06-26 1:16 GMT-06:00 Jonathan Dearborn :> You would have to use some varargs and call PHYSFS_read() or

PHYSFS_write() appropriately. I just looked and found this old
implementation of such:
fscanf and fprintf (fwd)

Be warned that there are some issues with that patch (C++, vsprintf vs.
vsnprintf, arg count limits, and perhaps more).

Jonny D

On Wed, Jun 25, 2014 at 11:25 PM, javierecf <@Javier_Flores> wrote:

is there any way to use fscanf and fprintf on a PHYSFS text file
handle?


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

–
Javier Flores

1 Like

If you’re only compiling with glibc, you can use fopencookie() to get a
FILE*.

? DavidOn 26/06/14 11:25, javierecf wrote:

is there any way to use fscanf and fprintf on a PHYSFS text file handle?

Hate to bump, but is there a modern solution to this? Preferably something written in C, not C++.

its being more than 10 years since I had this issue, whatever it was I fixed it already, what are you trying to do?

Basically, I’m working with a game that heavily uses fscanf, fprintf, fgetc, and ungetc, and have been wanting to use PhysFS to support more archive formats, but don’t know how to write equivalent functions for PhysFS.

without more context on what you are trying to to exactly I cannot give you more advice, but I can say for my case I ended up just using PHYSFS_openRead and PHYSFS_readBytes for every file type I have and it works fine for everything, I ended up not using anything else.

I don’t know if there’s really any more context to give. There’s just a lot of code that uses those functions that PhysFS doesn’t have analogues to, so I can’t really use it without something that mimics them, or rewriting several thousands of lines of code.

I don’t know if there’s really any more context to give. There’s just a lot of code that uses those functions that PhysFS doesn’t have analogues to, so I can’t really use it without something that mimics them, or rewriting several thousands of lines of code.

EDIT: While I’m not one to use AI normally, for a bunch of ethical reasons, desperate times call for desperate measures, and it seems to have spat these out after a bit of conversation, and so far, they seem to work: It’s a C conversion of the C++ code in the post linked by JohnnyD.

If anyone spots anything obviously wrong with any of these, let me know, but from testing it works exactly as I wish.

char* PHYSFS_gets(char* buffer, int maxlen, PHYSFS_File* file)
{
	if (buffer == NULL || maxlen <= 0 || file == NULL)
		return NULL;

	int i = 0;
	PHYSFS_sint64 rc;
	char c;

	/* Read characters until newline, EOF, or limit */
	while (i < maxlen - 1)
	{
		rc = PHYSFS_readBytes(file, &c, 1);
		if (rc <= 0)
		{
			/* EOF or read error */
			break;
		}

		buffer[i++] = c;
		if (c == '\n')
		{
			break;
		}
	}

	if (i == 0 && rc <= 0)
	{
		/* Nothing read and EOF/error */
		return NULL;
	}

	buffer[i] = '\0';
	return buffer;
}

int PHYSFS_getc(PHYSFS_File* fp)
{
	unsigned char currentCharacter;
	if (PHYSFS_readBytes(fp, &currentCharacter, 1) != 1)
		return EOF;

	return currentCharacter;
}

int PHYSFS_ungetc(int c, PHYSFS_File* fp)
{
	PHYSFS_sint64 pos;

	if (fp == NULL || c == EOF)
		return EOF;

	/* Get current position */
	pos = PHYSFS_tell(fp);
	if (pos <= 0)
		return EOF; /* can't move before start of file */

	/* Simply move back one byte. The next read will re-read it. */
	if (!PHYSFS_seek(fp, pos - 1))
		return EOF;

	return (unsigned char)c;
}

int PHYSFS_scanf(PHYSFS_File* fp, const char* formatting, ...)
{
	/////////////////////////////////////////////////
	// Find number of parameters
	int numArgs = 0;
	const char* currBuff = strchr(formatting, '%');
	if (currBuff == NULL)
		return 0;

	do
	{
		currBuff++;
		if ((*currBuff != '\0') && (*currBuff != '*'))
		{
			if (*currBuff == '%')
				currBuff++;
			else
				numArgs++;
		}
		currBuff = strchr(currBuff, '%');
	} while (currBuff != NULL);

	/////////////////////////////////////////////////
	// Determine lastLetter
	char lastLetter = formatting[strlen(formatting) - 1];

	// If last char is a format specifier, treat as EOF sentinel
	if (strchr("cdefgniouxpsXEF", lastLetter) != NULL)
		lastLetter = EOF;

	// If % appears before [ (or no [ exists), treat as string format
	if ((lastLetter == ']') &&
		(strrchr(formatting, '%') < strrchr(formatting, '[')))
		lastLetter = EOF;

	/////////////////////////////////////////////////
	// Begin variadic argument list
	va_list paramList;
	va_start(paramList, formatting);

	// Use a growable buffer
	size_t bufSize = 256;
	size_t len = 0;
	char* buffer = (char*)malloc(bufSize);
	if (!buffer)
	{
		va_end(paramList);
		return 0;
	}

	int numFound = 0;
	int letter;

	while ((letter = PHYSFS_getc(fp)) != EOF)
	{
		// Append to buffer
		if (len + 1 >= bufSize)
		{
			bufSize *= 2;
			char* newBuf = (char*)realloc(buffer, bufSize);
			if (!newBuf)
			{
				free(buffer);
				va_end(paramList);
				return numFound;
			}
			buffer = newBuf;
		}
		buffer[len++] = (char)letter;
		buffer[len] = '\0';

		// Try parsing
		numFound = vsscanf(buffer, formatting, paramList);

		if ((numFound == numArgs) &&
			((letter == EOF) ||
				((lastLetter != EOF) && (letter == lastLetter)) ||
				((lastLetter == EOF) && isspace(letter))))
		{
			break;
		}
	}

	free(buffer);
	va_end(paramList);
	return numFound;
}

int PHYSFS_fprintf(PHYSFS_File* file, const char* fmt, ...)
{
	int len;
	int size = 256;
	char* buffer;
	va_list args;

	if (file == NULL || fmt == NULL)
		return -1;

	buffer = (char*)malloc(size);
	if (buffer == NULL)
		return -1;

	for (;;)
	{
		va_start(args, fmt);
#if defined(_MSC_VER)
		len = _vsnprintf(buffer, size, fmt, args);
#else
		len = vsnprintf(buffer, size, fmt, args);
#endif
		va_end(args);

		if (len >= 0 && len < size)
			break; /* success */

		size *= 2;
		buffer = (char*)realloc(buffer, size);
		if (buffer == NULL)
			return -1;
	}

	if (PHYSFS_writeBytes(file, buffer, (PHYSFS_uint64)len) != (PHYSFS_sint64)len)
		len = -1;

	free(buffer);
	return len;
}

Did find one problem with the C’ified fscanf after a bit. It doesn’t scan through lines until it finds the substring. It stops the moment it doesn’t find a match. It also doesn’t support cases of no parameters. That’s unfortunate…