Skip to content

Commit

Permalink
Windows: hack vs a weird bug in VC2015 fseek()
Browse files Browse the repository at this point in the history
By a sheer coincidence one user have stumbled on what seems like a bug in MSVC standard library. The fseek fails to correctly position a stream at particular position, possibly depending on the current position too.
The curious detail: no error is set, and ftell() reports a correct desired position after fseek, but next call to fread actually reads from a wrong place. This may or not be related to internal stream buffering by standard library.
The workaround is to rewind the stream to the beginning first, and then seek to a wanted position.

I confirmed that this problem does not happen in later versions of VC (tried VC2019), nor on Linux.

Replicating this error in real game may be nearly impossible, so for the record, here's the confirmed failing case:
  start    0x000000
  seek to  0xb19b00
  read to  0xb1bb00
  seek to  0xb1bb00    // no actual move
  read to  0xb1db00
  seek to  0xb1ca3a    // seeking back here
  read to  0xb69512
  seek to  0xb69512    // no actual move
  read to  0xb6ba3a
  seek to  0xb6ba3a    // no actual move
  read to  0xb6da3a
  seek to  0xb6ce45    // seeking back here
  --- fail location
  reading anything from here reads from (supposedly) further position.
NOTE: in order to replicate this file must be few KBs bigger.
  • Loading branch information
ivan-mogilko committed Dec 18, 2023
1 parent ec528eb commit 5120e48
Showing 1 changed file with 31 additions and 0 deletions.
31 changes: 31 additions & 0 deletions Common/util/stdio_compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,42 @@ int ags_fseek(FILE * stream, file_off_t offset, int whence)
#if defined(HAVE_FSEEKO) // Contemporary POSIX libc
return fseeko(stream, offset, whence);
#elif AGS_PLATFORM_OS_WINDOWS // MSVC
// MSVC 2015 has a weird bug in fseek, where it may fail to set certain
// position, unless stream is rewinded to 0 first. See commentary below *
#if (_MSC_VER <= 1900)
if (whence == SEEK_SET)
_fseeki64(stream, 0, whence);
#endif
return _fseeki64(stream, offset, whence);
#else // No distinct interface with off_t
return fseek(stream, offset, whence);
#endif
}
//-----------------------------------------------------------------------------
// * A commentary on the MSVC 2015 fseek bug.
// Replicating this error in real game may be nearly impossible,
// so for the record, here's the confirmed failing case. Following are
// seek-to / read-to pairs which, if executed sequentially, will produce the
// erroneous effect. The test may be run on a generated file filled with zeros,
// with only meaningful values at certain spot.
//
// start 0x000000
// seek to 0xb19b00
// read to 0xb1bb00
// seek to 0xb1bb00 // no actual move
// read to 0xb1db00
// seek to 0xb1ca3a // seeking back here
// read to 0xb69512
// seek to 0xb69512 // no actual move
// read to 0xb6ba3a
// seek to 0xb6ba3a // no actual move
// read to 0xb6da3a
// seek to 0xb6ce45 // seeking back here
// --- fail location
// reading anything from here reads from (supposedly) further position.
// NOTE: in order to replicate this file must be few KBs bigger.
//
//-----------------------------------------------------------------------------

file_off_t ags_ftell(FILE * stream)
{
Expand Down

0 comments on commit 5120e48

Please sign in to comment.