/*
 * Copyright (C) 2007 Alexandre Julliard
 * Copyright (C) 2015-2016 Iván Matellanes
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"

#include <errno.h>
#include <fcntl.h>
#include <float.h>
#include <io.h>
#include <limits.h>
#include <share.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/stat.h>

#include "msvcirt.h"
#include "windef.h"
#include "winbase.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(msvcirt);

#define RESERVE_SIZE 512
#define STATEBUF_SIZE 8

/* ?sh_none@filebuf@@2HB */
const int filebuf_sh_none = 0x800;
/* ?sh_read@filebuf@@2HB */
const int filebuf_sh_read = 0xa00;
/* ?sh_write@filebuf@@2HB */
const int filebuf_sh_write = 0xc00;
/* ?openprot@filebuf@@2HB */
const int filebuf_openprot = 420;
/* ?binary@filebuf@@2HB */
const int filebuf_binary = _O_BINARY;
/* ?text@filebuf@@2HB */
const int filebuf_text = _O_TEXT;

/* ?adjustfield@ios@@2JB */
const LONG ios_adjustfield = FLAGS_left | FLAGS_right | FLAGS_internal;
/* ?basefield@ios@@2JB */
const LONG ios_basefield = FLAGS_dec | FLAGS_oct | FLAGS_hex;
/* ?floatfield@ios@@2JB */
const LONG ios_floatfield = FLAGS_scientific | FLAGS_fixed;
/* ?fLockcInit@ios@@0HA */
int ios_fLockcInit = 0;
/* ?sunk_with_stdio@ios@@0HA */
int ios_sunk_with_stdio = 0;
/* ?x_lockc@ios@@0U_CRT_CRITICAL_SECTION@@A */
extern CRITICAL_SECTION ios_static_lock;
CRITICAL_SECTION_DEBUG ios_static_lock_debug =
{
    0, 0, &ios_static_lock,
    { &ios_static_lock_debug.ProcessLocksList, &ios_static_lock_debug.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": ios_static_lock") }
};
CRITICAL_SECTION ios_static_lock = { &ios_static_lock_debug, -1, 0, 0, 0, 0 };
/* ?x_maxbit@ios@@0JA */
LONG ios_maxbit = 0x8000;
/* ?x_curindex@ios@@0HA */
int ios_curindex = -1;
/* ?x_statebuf@ios@@0PAJA */
LONG ios_statebuf[STATEBUF_SIZE] = {0};

/* class streambuf */
typedef struct {
    const vtable_ptr *vtable;
    int allocated;
    int unbuffered;
    int stored_char;
    char *base;
    char *ebuf;
    char *pbase;
    char *pptr;
    char *epptr;
    char *eback;
    char *gptr;
    char *egptr;
    int do_lock;
    CRITICAL_SECTION lock;
} streambuf;

void __thiscall streambuf_setb(streambuf*, char*, char*, int);
streambuf* __thiscall streambuf_setbuf(streambuf*, char*, int);
void __thiscall streambuf_setg(streambuf*, char*, char*, char*);
void __thiscall streambuf_setp(streambuf*, char*, char*);

/* class filebuf */
typedef struct {
    streambuf base;
    filedesc fd;
    int close;
} filebuf;

filebuf* __thiscall filebuf_close(filebuf*);

/* class strstreambuf */
typedef struct {
    streambuf base;
    int dynamic;
    int increase;
    int unknown;
    int constant;
    allocFunction f_alloc;
    freeFunction f_free;
} strstreambuf;

/* class stdiobuf */
typedef struct {
    streambuf base;
    FILE *file;
} stdiobuf;

/* class ios */
struct _ostream;
typedef struct {
    const vtable_ptr *vtable;
    streambuf *sb;
    ios_io_state state;
    int special[4];
    int delbuf;
    struct _ostream *tie;
    ios_flags flags;
    int precision;
    char fill;
    int width;
    int do_lock;
    CRITICAL_SECTION lock;
} ios;

ios* __thiscall ios_assign(ios*, const ios*);
int __thiscall ios_fail(const ios*);
void __cdecl ios_lock(ios*);
void __cdecl ios_lockc(void);
LONG __thiscall ios_setf_mask(ios*, LONG, LONG);
void __cdecl ios_unlock(ios*);
void __cdecl ios_unlockc(void);

/* class ostream */
typedef struct _ostream {
    const int *vbtable;
    int unknown;
} ostream;

/* class istream */
typedef struct {
    const int *vbtable;
    int extract_delim;
    int count;
} istream;

/* class iostream */
typedef struct {
    istream base1;
    ostream base2;
} iostream;

/* ??_7streambuf@@6B@ */
extern const vtable_ptr MSVCP_streambuf_vtable;
/* ??_7filebuf@@6B@ */
extern const vtable_ptr MSVCP_filebuf_vtable;
/* ??_7strstreambuf@@6B@ */
extern const vtable_ptr MSVCP_strstreambuf_vtable;
/* ??_7stdiobuf@@6B@ */
extern const vtable_ptr MSVCP_stdiobuf_vtable;
/* ??_7ios@@6B@ */
extern const vtable_ptr MSVCP_ios_vtable;
/* ??_7ostream@@6B@ */
extern const vtable_ptr MSVCP_ostream_vtable;
/* ??_7ostream_withassign@@6B@ */
extern const vtable_ptr MSVCP_ostream_withassign_vtable;
/* ??_7ostrstream@@6B@ */
extern const vtable_ptr MSVCP_ostrstream_vtable;
/* ??_7istream@@6B@ */
extern const vtable_ptr MSVCP_istream_vtable;
/* ??_7istream_withassign@@6B@ */
extern const vtable_ptr MSVCP_istream_withassign_vtable;
/* ??_7istrstream@@6B@ */
extern const vtable_ptr MSVCP_istrstream_vtable;
/* ??_7iostream@@6B@ */
extern const vtable_ptr MSVCP_iostream_vtable;
/* ??_7strstream@@6B@ */
extern const vtable_ptr MSVCP_strstream_vtable;
/* ??_7stdiostream@@6B@ */
extern const vtable_ptr MSVCP_stdiostream_vtable;

#ifndef __GNUC__
void __asm_dummy_vtables(void) {
#endif
    __ASM_VTABLE(streambuf,
            VTABLE_ADD_FUNC(streambuf_vector_dtor)
            VTABLE_ADD_FUNC(streambuf_sync)
            VTABLE_ADD_FUNC(streambuf_setbuf)
            VTABLE_ADD_FUNC(streambuf_seekoff)
            VTABLE_ADD_FUNC(streambuf_seekpos)
            VTABLE_ADD_FUNC(streambuf_xsputn)
            VTABLE_ADD_FUNC(streambuf_xsgetn)
            VTABLE_ADD_FUNC(streambuf_overflow)
            VTABLE_ADD_FUNC(streambuf_underflow)
            VTABLE_ADD_FUNC(streambuf_pbackfail)
            VTABLE_ADD_FUNC(streambuf_doallocate));
    __ASM_VTABLE(filebuf,
            VTABLE_ADD_FUNC(filebuf_vector_dtor)
            VTABLE_ADD_FUNC(filebuf_sync)
            VTABLE_ADD_FUNC(filebuf_setbuf)
            VTABLE_ADD_FUNC(filebuf_seekoff)
            VTABLE_ADD_FUNC(streambuf_seekpos)
            VTABLE_ADD_FUNC(streambuf_xsputn)
            VTABLE_ADD_FUNC(streambuf_xsgetn)
            VTABLE_ADD_FUNC(filebuf_overflow)
            VTABLE_ADD_FUNC(filebuf_underflow)
            VTABLE_ADD_FUNC(streambuf_pbackfail)
            VTABLE_ADD_FUNC(streambuf_doallocate));
    __ASM_VTABLE(strstreambuf,
            VTABLE_ADD_FUNC(strstreambuf_vector_dtor)
            VTABLE_ADD_FUNC(strstreambuf_sync)
            VTABLE_ADD_FUNC(strstreambuf_setbuf)
            VTABLE_ADD_FUNC(strstreambuf_seekoff)
            VTABLE_ADD_FUNC(streambuf_seekpos)
            VTABLE_ADD_FUNC(streambuf_xsputn)
            VTABLE_ADD_FUNC(streambuf_xsgetn)
            VTABLE_ADD_FUNC(strstreambuf_overflow)
            VTABLE_ADD_FUNC(strstreambuf_underflow)
            VTABLE_ADD_FUNC(streambuf_pbackfail)
            VTABLE_ADD_FUNC(strstreambuf_doallocate));
    __ASM_VTABLE(stdiobuf,
            VTABLE_ADD_FUNC(stdiobuf_vector_dtor)
            VTABLE_ADD_FUNC(stdiobuf_sync)
            VTABLE_ADD_FUNC(streambuf_setbuf)
            VTABLE_ADD_FUNC(stdiobuf_seekoff)
            VTABLE_ADD_FUNC(streambuf_seekpos)
            VTABLE_ADD_FUNC(streambuf_xsputn)
            VTABLE_ADD_FUNC(streambuf_xsgetn)
            VTABLE_ADD_FUNC(stdiobuf_overflow)
            VTABLE_ADD_FUNC(stdiobuf_underflow)
            VTABLE_ADD_FUNC(stdiobuf_pbackfail)
            VTABLE_ADD_FUNC(streambuf_doallocate));
    __ASM_VTABLE(ios,
            VTABLE_ADD_FUNC(ios_vector_dtor));
    __ASM_VTABLE(ostream,
            VTABLE_ADD_FUNC(ostream_vector_dtor));
    __ASM_VTABLE(ostream_withassign,
            VTABLE_ADD_FUNC(ostream_vector_dtor));
    __ASM_VTABLE(ostrstream,
            VTABLE_ADD_FUNC(ostream_vector_dtor));
    __ASM_VTABLE(istream,
            VTABLE_ADD_FUNC(istream_vector_dtor));
    __ASM_VTABLE(istream_withassign,
            VTABLE_ADD_FUNC(istream_vector_dtor));
    __ASM_VTABLE(istrstream,
            VTABLE_ADD_FUNC(istream_vector_dtor));
    __ASM_VTABLE(iostream,
            VTABLE_ADD_FUNC(iostream_vector_dtor));
    __ASM_VTABLE(strstream,
            VTABLE_ADD_FUNC(iostream_vector_dtor));
    __ASM_VTABLE(stdiostream,
            VTABLE_ADD_FUNC(iostream_vector_dtor));
#ifndef __GNUC__
}
#endif

#define ALIGNED_SIZE(size, alignment) (((size)+((alignment)-1))/(alignment)*(alignment))
#define VBTABLE_ENTRY(class, offset, vbase) ALIGNED_SIZE(sizeof(class), TYPE_ALIGNMENT(vbase))-offset

/* ??_8ostream@@7B@ */
/* ??_8ostream_withassign@@7B@ */
/* ??_8ostrstream@@7B@ */
const int ostream_vbtable[] = {0, VBTABLE_ENTRY(ostream, FIELD_OFFSET(ostream, vbtable), ios)};
/* ??_8istream@@7B@ */
/* ??_8istream_withassign@@7B@ */
/* ??_8istrstream@@7B@ */
const int istream_vbtable[] = {0, VBTABLE_ENTRY(istream, FIELD_OFFSET(istream, vbtable), ios)};
/* ??_8iostream@@7Bistream@@@ */
/* ??_8stdiostream@@7Bistream@@@ */
/* ??_8strstream@@7Bistream@@@ */
const int iostream_vbtable_istream[] = {0, VBTABLE_ENTRY(iostream, FIELD_OFFSET(iostream, base1), ios)};
/* ??_8iostream@@7Bostream@@@ */
/* ??_8stdiostream@@7Bostream@@@ */
/* ??_8strstream@@7Bostream@@@ */
const int iostream_vbtable_ostream[] = {0, VBTABLE_ENTRY(iostream, FIELD_OFFSET(iostream, base2), ios)};

DEFINE_RTTI_DATA0(streambuf, 0, ".?AVstreambuf@@")
DEFINE_RTTI_DATA1(filebuf, 0, &streambuf_rtti_base_descriptor, ".?AVfilebuf@@")
DEFINE_RTTI_DATA1(strstreambuf, 0, &streambuf_rtti_base_descriptor, ".?AVstrstreambuf@@")
DEFINE_RTTI_DATA1(stdiobuf, 0, &streambuf_rtti_base_descriptor, ".?AVstdiobuf@@")
DEFINE_RTTI_DATA0(ios, 0, ".?AVios@@")
DEFINE_RTTI_DATA1(ostream, sizeof(ostream), &ios_rtti_base_descriptor, ".?AVostream@@")
DEFINE_RTTI_DATA2(ostream_withassign, sizeof(ostream),
    &ostream_rtti_base_descriptor, &ios_rtti_base_descriptor, ".?AVostream_withassign@@")
DEFINE_RTTI_DATA2(ostrstream, sizeof(ostream),
    &ostream_rtti_base_descriptor, &ios_rtti_base_descriptor, ".?AVostrstream@@")
DEFINE_RTTI_DATA1(istream, sizeof(istream), &ios_rtti_base_descriptor, ".?AVistream@@")
DEFINE_RTTI_DATA2(istream_withassign, sizeof(istream),
    &istream_rtti_base_descriptor, &ios_rtti_base_descriptor, ".?AVistream_withassign@@")
DEFINE_RTTI_DATA2(istrstream, sizeof(istream),
    &istream_rtti_base_descriptor, &ios_rtti_base_descriptor, ".?AVistrstream@@")
DEFINE_RTTI_DATA4(iostream, sizeof(iostream),
    &istream_rtti_base_descriptor, &ios_rtti_base_descriptor,
    &ostream_rtti_base_descriptor, &ios_rtti_base_descriptor, ".?AViostream@@")
DEFINE_RTTI_DATA4(strstream, sizeof(iostream),
    &istream_rtti_base_descriptor, &ios_rtti_base_descriptor,
    &ostream_rtti_base_descriptor, &ios_rtti_base_descriptor, ".?AVstrstream@@")
DEFINE_RTTI_DATA4(stdiostream, sizeof(iostream),
    &istream_rtti_base_descriptor, &ios_rtti_base_descriptor,
    &ostream_rtti_base_descriptor, &ios_rtti_base_descriptor, ".?AVstdiostream@@")

/* ?cin@@3Vistream_withassign@@A */
struct {
    istream is;
    ios vbase;
} cin = { { 0 } };

/* ?cout@@3Vostream_withassign@@A */
/* ?cerr@@3Vostream_withassign@@A */
/* ?clog@@3Vostream_withassign@@A */
struct {
    ostream os;
    ios vbase;
} cout = { { 0 } }, cerr = { { 0 } }, clog = { { 0 } };


/* ??0streambuf@@IAE@PADH@Z */
/* ??0streambuf@@IEAA@PEADH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_reserve_ctor, 12)
streambuf* __thiscall streambuf_reserve_ctor(streambuf *this, char *buffer, int length)
{
    TRACE("(%p %p %d)\n", this, buffer, length);
    this->vtable = &MSVCP_streambuf_vtable;
    this->allocated = 0;
    this->stored_char = EOF;
    this->do_lock = -1;
    this->base = NULL;
    streambuf_setbuf(this, buffer, length);
    streambuf_setg(this, NULL, NULL, NULL);
    streambuf_setp(this, NULL, NULL);
    InitializeCriticalSection(&this->lock);
    return this;
}

/* ??0streambuf@@IAE@XZ */
/* ??0streambuf@@IEAA@XZ */
DEFINE_THISCALL_WRAPPER(streambuf_ctor, 4)
streambuf* __thiscall streambuf_ctor(streambuf *this)
{
    streambuf_reserve_ctor(this, NULL, 0);
    this->unbuffered = 0;
    return this;
}

/* ??0streambuf@@QAE@ABV0@@Z */
/* ??0streambuf@@QEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(streambuf_copy_ctor, 8)
streambuf* __thiscall streambuf_copy_ctor(streambuf *this, const streambuf *copy)
{
    TRACE("(%p %p)\n", this, copy);
    *this = *copy;
    this->vtable = &MSVCP_streambuf_vtable;
    return this;
}

/* ??1streambuf@@UAE@XZ */
/* ??1streambuf@@UEAA@XZ */
DEFINE_THISCALL_WRAPPER(streambuf_dtor, 4)
void __thiscall streambuf_dtor(streambuf *this)
{
    TRACE("(%p)\n", this);
    if (this->allocated)
        MSVCRT_operator_delete(this->base);
    DeleteCriticalSection(&this->lock);
}

/* ??4streambuf@@QAEAAV0@ABV0@@Z */
/* ??4streambuf@@QEAAAEAV0@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(streambuf_assign, 8)
streambuf* __thiscall streambuf_assign(streambuf *this, const streambuf *rhs)
{
    streambuf_dtor(this);
    return streambuf_copy_ctor(this, rhs);
}

/* ??_Estreambuf@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(streambuf_vector_dtor, 8)
#define call_streambuf_vector_dtor(this, flags) CALL_VTBL_FUNC(this, 0,\
        streambuf*, (streambuf*, unsigned int), (this, flags))
streambuf* __thiscall streambuf_vector_dtor(streambuf *this, unsigned int flags)
{
    TRACE("(%p %x)\n", this, flags);
    if (flags & 2) {
        /* we have an array, with the number of elements stored before the first object */
        INT_PTR i, *ptr = (INT_PTR *)this-1;

        for (i = *ptr-1; i >= 0; i--)
            streambuf_dtor(this+i);
        MSVCRT_operator_delete(ptr);
    } else {
        streambuf_dtor(this);
        if (flags & 1)
            MSVCRT_operator_delete(this);
    }
    return this;
}

/* ??_Gstreambuf@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(streambuf_scalar_dtor, 8)
streambuf* __thiscall streambuf_scalar_dtor(streambuf *this, unsigned int flags)
{
    TRACE("(%p %x)\n", this, flags);
    streambuf_dtor(this);
    if (flags & 1) MSVCRT_operator_delete(this);
    return this;
}

/* ?doallocate@streambuf@@MAEHXZ */
/* ?doallocate@streambuf@@MEAAHXZ */
DEFINE_THISCALL_WRAPPER(streambuf_doallocate, 4)
#define call_streambuf_doallocate(this) CALL_VTBL_FUNC(this, 40, int, (streambuf*), (this))
int __thiscall streambuf_doallocate(streambuf *this)
{
    char *reserve;

    TRACE("(%p)\n", this);
    reserve = MSVCRT_operator_new(RESERVE_SIZE);
    if (!reserve)
        return EOF;

    streambuf_setb(this, reserve, reserve + RESERVE_SIZE, 1);
    return 1;
}

/* ?allocate@streambuf@@IAEHXZ */
/* ?allocate@streambuf@@IEAAHXZ */
DEFINE_THISCALL_WRAPPER(streambuf_allocate, 4)
int __thiscall streambuf_allocate(streambuf *this)
{
    TRACE("(%p)\n", this);
    if (this->base != NULL || this->unbuffered)
        return 0;
    return call_streambuf_doallocate(this);
}

/* ?base@streambuf@@IBEPADXZ */
/* ?base@streambuf@@IEBAPEADXZ */
DEFINE_THISCALL_WRAPPER(streambuf_base, 4)
char* __thiscall streambuf_base(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return this->base;
}

/* ?blen@streambuf@@IBEHXZ */
/* ?blen@streambuf@@IEBAHXZ */
DEFINE_THISCALL_WRAPPER(streambuf_blen, 4)
int __thiscall streambuf_blen(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return this->ebuf - this->base;
}

/* ?eback@streambuf@@IBEPADXZ */
/* ?eback@streambuf@@IEBAPEADXZ */
DEFINE_THISCALL_WRAPPER(streambuf_eback, 4)
char* __thiscall streambuf_eback(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return this->eback;
}

/* ?ebuf@streambuf@@IBEPADXZ */
/* ?ebuf@streambuf@@IEBAPEADXZ */
DEFINE_THISCALL_WRAPPER(streambuf_ebuf, 4)
char* __thiscall streambuf_ebuf(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return this->ebuf;
}

/* ?egptr@streambuf@@IBEPADXZ */
/* ?egptr@streambuf@@IEBAPEADXZ */
DEFINE_THISCALL_WRAPPER(streambuf_egptr, 4)
char* __thiscall streambuf_egptr(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return this->egptr;
}

/* ?epptr@streambuf@@IBEPADXZ */
/* ?epptr@streambuf@@IEBAPEADXZ */
DEFINE_THISCALL_WRAPPER(streambuf_epptr, 4)
char* __thiscall streambuf_epptr(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return this->epptr;
}

/* ?gptr@streambuf@@IBEPADXZ */
/* ?gptr@streambuf@@IEBAPEADXZ */
DEFINE_THISCALL_WRAPPER(streambuf_gptr, 4)
char* __thiscall streambuf_gptr(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return this->gptr;
}

/* ?pbase@streambuf@@IBEPADXZ */
/* ?pbase@streambuf@@IEBAPEADXZ */
DEFINE_THISCALL_WRAPPER(streambuf_pbase, 4)
char* __thiscall streambuf_pbase(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return this->pbase;
}

/* ?pptr@streambuf@@IBEPADXZ */
/* ?pptr@streambuf@@IEBAPEADXZ */
DEFINE_THISCALL_WRAPPER(streambuf_pptr, 4)
char* __thiscall streambuf_pptr(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return this->pptr;
}

/* ?clrlock@streambuf@@QAEXXZ */
/* ?clrlock@streambuf@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(streambuf_clrlock, 4)
void __thiscall streambuf_clrlock(streambuf *this)
{
    TRACE("(%p)\n", this);
    if (this->do_lock <= 0)
        this->do_lock++;
}

/* ?lock@streambuf@@QAEXXZ */
/* ?lock@streambuf@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(streambuf_lock, 4)
void __thiscall streambuf_lock(streambuf *this)
{
    TRACE("(%p)\n", this);
    if (this->do_lock < 0)
        EnterCriticalSection(&this->lock);
}

/* ?lockptr@streambuf@@IAEPAU_CRT_CRITICAL_SECTION@@XZ */
/* ?lockptr@streambuf@@IEAAPEAU_CRT_CRITICAL_SECTION@@XZ */
DEFINE_THISCALL_WRAPPER(streambuf_lockptr, 4)
CRITICAL_SECTION* __thiscall streambuf_lockptr(streambuf *this)
{
    TRACE("(%p)\n", this);
    return &this->lock;
}

/* ?gbump@streambuf@@IAEXH@Z */
/* ?gbump@streambuf@@IEAAXH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_gbump, 8)
void __thiscall streambuf_gbump(streambuf *this, int count)
{
    TRACE("(%p %d)\n", this, count);
    this->gptr += count;
}

/* ?pbump@streambuf@@IAEXH@Z */
/* ?pbump@streambuf@@IEAAXH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_pbump, 8)
void __thiscall streambuf_pbump(streambuf *this, int count)
{
    TRACE("(%p %d)\n", this, count);
    this->pptr += count;
}

/* ?in_avail@streambuf@@QBEHXZ */
/* ?in_avail@streambuf@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(streambuf_in_avail, 4)
int __thiscall streambuf_in_avail(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return (this->egptr - this->gptr > 0) ? this->egptr - this->gptr : 0;
}

/* ?out_waiting@streambuf@@QBEHXZ */
/* ?out_waiting@streambuf@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(streambuf_out_waiting, 4)
int __thiscall streambuf_out_waiting(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return (this->pptr - this->pbase > 0) ? this->pptr - this->pbase : 0;
}

/* Unexported */
DEFINE_THISCALL_WRAPPER(streambuf_overflow, 8)
#define call_streambuf_overflow(this, c) CALL_VTBL_FUNC(this, 28, int, (streambuf*, int), (this, c))
int __thiscall streambuf_overflow(streambuf *this, int c)
{
    ERR("overflow is not implemented in streambuf\n");
    return EOF;
}

/* ?seekoff@streambuf@@UAEJJW4seek_dir@ios@@H@Z */
/* ?seekoff@streambuf@@UEAAJJW4seek_dir@ios@@H@Z */
DEFINE_THISCALL_WRAPPER(streambuf_seekoff, 16)
#define call_streambuf_seekoff(this, off, dir, mode) CALL_VTBL_FUNC(this, 12, streampos, (streambuf*, streamoff, ios_seek_dir, int), (this, off, dir, mode))
streampos __thiscall streambuf_seekoff(streambuf *this, streamoff offset, ios_seek_dir dir, int mode)
{
    TRACE("(%p %d %d %d)\n", this, offset, dir, mode);
    return EOF;
}

/* ?seekpos@streambuf@@UAEJJH@Z */
/* ?seekpos@streambuf@@UEAAJJH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_seekpos, 12)
streampos __thiscall streambuf_seekpos(streambuf *this, streampos pos, int mode)
{
    TRACE("(%p %d %d)\n", this, pos, mode);
    return call_streambuf_seekoff(this, pos, SEEKDIR_beg, mode);
}

/* ?pbackfail@streambuf@@UAEHH@Z */
/* ?pbackfail@streambuf@@UEAAHH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_pbackfail, 8)
#define call_streambuf_pbackfail(this, c) CALL_VTBL_FUNC(this, 36, int, (streambuf*, int), (this, c))
int __thiscall streambuf_pbackfail(streambuf *this, int c)
{
    TRACE("(%p %d)\n", this, c);
    if (this->gptr > this->eback)
        return *--this->gptr = c;
    if (call_streambuf_seekoff(this, -1, SEEKDIR_cur, OPENMODE_in) == EOF)
        return EOF;
    if (!this->unbuffered && this->egptr) {
        /* 'c' should be the next character read */
        memmove(this->gptr + 1, this->gptr, this->egptr - this->gptr - 1);
        *this->gptr = c;
    }
    return c;
}

/* ?setb@streambuf@@IAEXPAD0H@Z */
/* ?setb@streambuf@@IEAAXPEAD0H@Z */
DEFINE_THISCALL_WRAPPER(streambuf_setb, 16)
void __thiscall streambuf_setb(streambuf *this, char *ba, char *eb, int delete)
{
    TRACE("(%p %p %p %d)\n", this, ba, eb, delete);
    if (this->allocated)
        MSVCRT_operator_delete(this->base);
    this->allocated = delete;
    this->base = ba;
    this->ebuf = eb;
}

/* ?setbuf@streambuf@@UAEPAV1@PADH@Z */
/* ?setbuf@streambuf@@UEAAPEAV1@PEADH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_setbuf, 12)
streambuf* __thiscall streambuf_setbuf(streambuf *this, char *buffer, int length)
{
    TRACE("(%p %p %d)\n", this, buffer, length);
    if (this->base != NULL)
        return NULL;

    if (buffer == NULL || !length) {
        this->unbuffered = 1;
        this->base = this->ebuf = NULL;
    } else {
        this->unbuffered = 0;
        this->base = buffer;
        this->ebuf = buffer + length;
    }
    return this;
}

/* ?setg@streambuf@@IAEXPAD00@Z */
/* ?setg@streambuf@@IEAAXPEAD00@Z */
DEFINE_THISCALL_WRAPPER(streambuf_setg, 16)
void __thiscall streambuf_setg(streambuf *this, char *ek, char *gp, char *eg)
{
    TRACE("(%p %p %p %p)\n", this, ek, gp, eg);
    this->eback = ek;
    this->gptr = gp;
    this->egptr = eg;
}

/* ?setlock@streambuf@@QAEXXZ */
/* ?setlock@streambuf@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(streambuf_setlock, 4)
void __thiscall streambuf_setlock(streambuf *this)
{
    TRACE("(%p)\n", this);
    this->do_lock--;
}

/* ?setp@streambuf@@IAEXPAD0@Z */
/* ?setp@streambuf@@IEAAXPEAD0@Z */
DEFINE_THISCALL_WRAPPER(streambuf_setp, 12)
void __thiscall streambuf_setp(streambuf *this, char *pb, char *ep)
{
    TRACE("(%p %p %p)\n", this, pb, ep);
    this->pbase = this->pptr = pb;
    this->epptr = ep;
}

/* ?sync@streambuf@@UAEHXZ */
/* ?sync@streambuf@@UEAAHXZ */
DEFINE_THISCALL_WRAPPER(streambuf_sync, 4)
#define call_streambuf_sync(this) CALL_VTBL_FUNC(this, 4, int, (streambuf*), (this))
int __thiscall streambuf_sync(streambuf *this)
{
    TRACE("(%p)\n", this);
    return (this->gptr >= this->egptr && this->pbase >= this->pptr) ? 0 : EOF;
}

/* ?unbuffered@streambuf@@IAEXH@Z */
/* ?unbuffered@streambuf@@IEAAXH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_unbuffered_set, 8)
void __thiscall streambuf_unbuffered_set(streambuf *this, int buf)
{
    TRACE("(%p %d)\n", this, buf);
    this->unbuffered = buf;
}

/* ?unbuffered@streambuf@@IBEHXZ */
/* ?unbuffered@streambuf@@IEBAHXZ */
DEFINE_THISCALL_WRAPPER(streambuf_unbuffered_get, 4)
int __thiscall streambuf_unbuffered_get(const streambuf *this)
{
    TRACE("(%p)\n", this);
    return this->unbuffered;
}

/* Unexported */
DEFINE_THISCALL_WRAPPER(streambuf_underflow, 4)
#define call_streambuf_underflow(this) CALL_VTBL_FUNC(this, 32, int, (streambuf*), (this))
int __thiscall streambuf_underflow(streambuf *this)
{
    ERR("underflow is not implemented in streambuf\n");
    return EOF;
}

/* ?unlock@streambuf@@QAEXXZ */
/* ?unlock@streambuf@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(streambuf_unlock, 4)
void __thiscall streambuf_unlock(streambuf *this)
{
    TRACE("(%p)\n", this);
    if (this->do_lock < 0)
        LeaveCriticalSection(&this->lock);
}

/* ?xsgetn@streambuf@@UAEHPADH@Z */
/* ?xsgetn@streambuf@@UEAAHPEADH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_xsgetn, 12)
#define call_streambuf_xsgetn(this, buffer, count) CALL_VTBL_FUNC(this, 24, int, (streambuf*, char*, int), (this, buffer, count))
int __thiscall streambuf_xsgetn(streambuf *this, char *buffer, int count)
{
    int copied = 0, chunk;

    TRACE("(%p %p %d)\n", this, buffer, count);

    if (this->unbuffered) {
        if (this->stored_char == EOF)
            this->stored_char = call_streambuf_underflow(this);
        while (copied < count && this->stored_char != EOF) {
            buffer[copied++] = this->stored_char;
            this->stored_char = call_streambuf_underflow(this);
        }
    } else {
        while (copied < count) {
            if (call_streambuf_underflow(this) == EOF)
                break;
            chunk = this->egptr - this->gptr;
            if (chunk > count - copied)
                chunk = count - copied;
            memcpy(buffer+copied, this->gptr, chunk);
            this->gptr += chunk;
            copied += chunk;
        }
    }
    return copied;
}

/* ?xsputn@streambuf@@UAEHPBDH@Z */
/* ?xsputn@streambuf@@UEAAHPEBDH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_xsputn, 12)
#define call_streambuf_xsputn(this, data, length) CALL_VTBL_FUNC(this, 20, int, (streambuf*, const char*, int), (this, data, length))
int __thiscall streambuf_xsputn(streambuf *this, const char *data, int length)
{
    int copied = 0, chunk;

    TRACE("(%p %p %d)\n", this, data, length);

    while (copied < length) {
        if (this->unbuffered || this->pptr == this->epptr) {
            if (call_streambuf_overflow(this, data[copied]) == EOF)
                break;
            copied++;
        } else {
            chunk = this->epptr - this->pptr;
            if (chunk > length - copied)
                chunk = length - copied;
            memcpy(this->pptr, data+copied, chunk);
            this->pptr += chunk;
            copied += chunk;
        }
    }
    return copied;
}

/* ?sgetc@streambuf@@QAEHXZ */
/* ?sgetc@streambuf@@QEAAHXZ */
DEFINE_THISCALL_WRAPPER(streambuf_sgetc, 4)
int __thiscall streambuf_sgetc(streambuf *this)
{
    TRACE("(%p)\n", this);
    if (this->unbuffered) {
        if (this->stored_char == EOF)
            this->stored_char = call_streambuf_underflow(this);
        return this->stored_char;
    } else
        return call_streambuf_underflow(this);
}

/* ?sputc@streambuf@@QAEHH@Z */
/* ?sputc@streambuf@@QEAAHH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_sputc, 8)
int __thiscall streambuf_sputc(streambuf *this, int ch)
{
    TRACE("(%p %d)\n", this, ch);
    return (this->pptr < this->epptr) ? (unsigned char)(*this->pptr++ = ch) : call_streambuf_overflow(this, ch);
}

/* ?sgetn@streambuf@@QAEHPADH@Z */
/* ?sgetn@streambuf@@QEAAHPEADH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_sgetn, 12)
int __thiscall streambuf_sgetn(streambuf *this, char *buffer, int count)
{
    return call_streambuf_xsgetn(this, buffer, count);
}

/* ?sputn@streambuf@@QAEHPBDH@Z */
/* ?sputn@streambuf@@QEAAHPEBDH@Z */
DEFINE_THISCALL_WRAPPER(streambuf_sputn, 12)
int __thiscall streambuf_sputn(streambuf *this, const char *data, int length)
{
    return call_streambuf_xsputn(this, data, length);
}

/* ?snextc@streambuf@@QAEHXZ */
/* ?snextc@streambuf@@QEAAHXZ */
DEFINE_THISCALL_WRAPPER(streambuf_snextc, 4)
int __thiscall streambuf_snextc(streambuf *this)
{
    TRACE("(%p)\n", this);
    if (this->unbuffered) {
        if (this->stored_char == EOF)
            call_streambuf_underflow(this);
        return this->stored_char = call_streambuf_underflow(this);
    } else {
        if (this->gptr >= this->egptr)
            call_streambuf_underflow(this);
        this->gptr++;
        return (this->gptr < this->egptr) ? (unsigned char)(*this->gptr) : call_streambuf_underflow(this);
    }
}

/* ?sbumpc@streambuf@@QAEHXZ */
/* ?sbumpc@streambuf@@QEAAHXZ */
DEFINE_THISCALL_WRAPPER(streambuf_sbumpc, 4)
int __thiscall streambuf_sbumpc(streambuf *this)
{
    int ret;

    TRACE("(%p)\n", this);

    if (this->unbuffered) {
        ret = this->stored_char;
        this->stored_char = EOF;
        if (ret == EOF)
            ret = call_streambuf_underflow(this);
    } else {
        ret = (this->gptr < this->egptr) ? (unsigned char)(*this->gptr) : call_streambuf_underflow(this);
        this->gptr++;
    }
    return ret;
}

/* ?stossc@streambuf@@QAEXXZ */
/* ?stossc@streambuf@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(streambuf_stossc, 4)
void __thiscall streambuf_stossc(streambuf *this)
{
    TRACE("(%p)\n", this);
    if (this->unbuffered) {
        if (this->stored_char == EOF)
            call_streambuf_underflow(this);
        else
            this->stored_char = EOF;
    } else {
        if (this->gptr >= this->egptr)
            call_streambuf_underflow(this);
        if (this->gptr < this->egptr)
            this->gptr++;
    }
}

/* ?sputbackc@streambuf@@QAEHD@Z */
/* ?sputbackc@streambuf@@QEAAHD@Z */
DEFINE_THISCALL_WRAPPER(streambuf_sputbackc, 8)
int __thiscall streambuf_sputbackc(streambuf *this, char ch)
{
    TRACE("(%p %d)\n", this, ch);
    return call_streambuf_pbackfail(this, ch);
}

/* ?dbp@streambuf@@QAEXXZ */
/* ?dbp@streambuf@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(streambuf_dbp, 4)
void __thiscall streambuf_dbp(streambuf *this)
{
    printf("\nSTREAMBUF DEBUG INFO: this=%p, ", this);
    if (this->unbuffered) {
        printf("unbuffered\n");
    } else {
        printf("_fAlloc=%d\n", this->allocated);
        printf(" base()=%p, ebuf()=%p,  blen()=%d\n", this->base, this->ebuf, streambuf_blen(this));
        printf("pbase()=%p, pptr()=%p, epptr()=%p\n", this->pbase, this->pptr, this->epptr);
        printf("eback()=%p, gptr()=%p, egptr()=%p\n", this->eback, this->gptr, this->egptr);
    }
}

/* ??0filebuf@@QAE@ABV0@@Z */
/* ??0filebuf@@QEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(filebuf_copy_ctor, 8)
filebuf* __thiscall filebuf_copy_ctor(filebuf* this, const filebuf *copy)
{
    TRACE("(%p %p)\n", this, copy);
    *this = *copy;
    this->base.vtable = &MSVCP_filebuf_vtable;
    return this;
}

/* ??0filebuf@@QAE@HPADH@Z */
/* ??0filebuf@@QEAA@HPEADH@Z */
DEFINE_THISCALL_WRAPPER(filebuf_fd_reserve_ctor, 16)
filebuf* __thiscall filebuf_fd_reserve_ctor(filebuf* this, filedesc fd, char *buffer, int length)
{
    TRACE("(%p %d %p %d)\n", this, fd, buffer, length);
    streambuf_reserve_ctor(&this->base, buffer, length);
    this->base.vtable = &MSVCP_filebuf_vtable;
    this->fd = fd;
    this->close = 0;
    return this;
}

/* ??0filebuf@@QAE@H@Z */
/* ??0filebuf@@QEAA@H@Z */
DEFINE_THISCALL_WRAPPER(filebuf_fd_ctor, 8)
filebuf* __thiscall filebuf_fd_ctor(filebuf* this, filedesc fd)
{
    filebuf_fd_reserve_ctor(this, fd, NULL, 0);
    this->base.unbuffered = 0;
    return this;
}

/* ??0filebuf@@QAE@XZ */
/* ??0filebuf@@QEAA@XZ */
DEFINE_THISCALL_WRAPPER(filebuf_ctor, 4)
filebuf* __thiscall filebuf_ctor(filebuf* this)
{
    return filebuf_fd_ctor(this, -1);
}

/* ??1filebuf@@UAE@XZ */
/* ??1filebuf@@UEAA@XZ */
DEFINE_THISCALL_WRAPPER(filebuf_dtor, 4)
void __thiscall filebuf_dtor(filebuf* this)
{
    TRACE("(%p)\n", this);
    if (this->close)
        filebuf_close(this);
    streambuf_dtor(&this->base);
}

/* ??4filebuf@@QAEAAV0@ABV0@@Z */
/* ??4filebuf@@QEAAAEAV0@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(filebuf_assign, 8)
filebuf* __thiscall filebuf_assign(filebuf* this, const filebuf *rhs)
{
    filebuf_dtor(this);
    return filebuf_copy_ctor(this, rhs);
}

/* ??_Efilebuf@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(filebuf_vector_dtor, 8)
filebuf* __thiscall filebuf_vector_dtor(filebuf *this, unsigned int flags)
{
    TRACE("(%p %x)\n", this, flags);
    if (flags & 2) {
        /* we have an array, with the number of elements stored before the first object */
        INT_PTR i, *ptr = (INT_PTR *)this-1;

        for (i = *ptr-1; i >= 0; i--)
            filebuf_dtor(this+i);
        MSVCRT_operator_delete(ptr);
    } else {
        filebuf_dtor(this);
        if (flags & 1)
            MSVCRT_operator_delete(this);
    }
    return this;
}

/* ??_Gfilebuf@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(filebuf_scalar_dtor, 8)
filebuf* __thiscall filebuf_scalar_dtor(filebuf *this, unsigned int flags)
{
    TRACE("(%p %x)\n", this, flags);
    filebuf_dtor(this);
    if (flags & 1) MSVCRT_operator_delete(this);
    return this;
}

/* ?attach@filebuf@@QAEPAV1@H@Z */
/* ?attach@filebuf@@QEAAPEAV1@H@Z */
DEFINE_THISCALL_WRAPPER(filebuf_attach, 8)
filebuf* __thiscall filebuf_attach(filebuf *this, filedesc fd)
{
    TRACE("(%p %d)\n", this, fd);
    if (this->fd != -1)
        return NULL;

    streambuf_lock(&this->base);
    this->fd = fd;
    streambuf_allocate(&this->base);
    streambuf_unlock(&this->base);
    return this;
}

/* ?close@filebuf@@QAEPAV1@XZ */
/* ?close@filebuf@@QEAAPEAV1@XZ */
DEFINE_THISCALL_WRAPPER(filebuf_close, 4)
filebuf* __thiscall filebuf_close(filebuf *this)
{
    filebuf *ret;

    TRACE("(%p)\n", this);
    if (this->fd == -1)
        return NULL;

    streambuf_lock(&this->base);
    if (call_streambuf_sync(&this->base) == EOF || _close(this->fd) < 0) {
        ret = NULL;
    } else {
        this->fd = -1;
        ret = this;
    }
    streambuf_unlock(&this->base);
    return ret;
}

/* ?fd@filebuf@@QBEHXZ */
/* ?fd@filebuf@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(filebuf_fd, 4)
filedesc __thiscall filebuf_fd(const filebuf *this)
{
    TRACE("(%p)\n", this);
    return this->fd;
}

/* ?is_open@filebuf@@QBEHXZ */
/* ?is_open@filebuf@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(filebuf_is_open, 4)
int __thiscall filebuf_is_open(const filebuf *this)
{
    TRACE("(%p)\n", this);
    return this->fd != -1;
}

/* ?open@filebuf@@QAEPAV1@PBDHH@Z */
/* ?open@filebuf@@QEAAPEAV1@PEBDHH@Z */
DEFINE_THISCALL_WRAPPER(filebuf_open, 16)
filebuf* __thiscall filebuf_open(filebuf *this, const char *name, ios_open_mode mode, int protection)
{
    const int inout_mode[4] = {-1, _O_RDONLY, _O_WRONLY, _O_RDWR};
    const int share_mode[4] = {_SH_DENYRW, _SH_DENYWR, _SH_DENYRD, _SH_DENYNO};
    int op_flags, sh_flags, fd;

    TRACE("(%p %s %x %x)\n", this, name, mode, protection);
    if (this->fd != -1)
        return NULL;

    /* mode */
    if (mode & (OPENMODE_app|OPENMODE_trunc))
        mode |= OPENMODE_out;
    op_flags = inout_mode[mode & (OPENMODE_in|OPENMODE_out)];
    if (op_flags < 0)
        return NULL;
    if (mode & OPENMODE_app)
        op_flags |= _O_APPEND;
    if ((mode & OPENMODE_trunc) ||
            ((mode & OPENMODE_out) && !(mode & (OPENMODE_in|OPENMODE_app|OPENMODE_ate))))
        op_flags |= _O_TRUNC;
    if (!(mode & OPENMODE_nocreate))
        op_flags |= _O_CREAT;
    if (mode & OPENMODE_noreplace)
        op_flags |= _O_EXCL;
    op_flags |= (mode & OPENMODE_binary) ? _O_BINARY : _O_TEXT;

    /* share protection */
    sh_flags = (protection & filebuf_sh_none) ? share_mode[(protection >> 9) & 3] : _SH_DENYNO;

    TRACE("op_flags %x, sh_flags %x\n", op_flags, sh_flags);
    fd = _sopen(name, op_flags, sh_flags, _S_IREAD|_S_IWRITE);
    if (fd < 0)
        return NULL;

    streambuf_lock(&this->base);
    this->close = 1;
    this->fd = fd;
    if ((mode & OPENMODE_ate) &&
            call_streambuf_seekoff(&this->base, 0, SEEKDIR_end, mode & (OPENMODE_in|OPENMODE_out)) == EOF) {
        _close(fd);
        this->fd = -1;
    }
    streambuf_allocate(&this->base);
    streambuf_unlock(&this->base);
    return (this->fd == -1) ? NULL : this;
}

/* ?overflow@filebuf@@UAEHH@Z */
/* ?overflow@filebuf@@UEAAHH@Z */
DEFINE_THISCALL_WRAPPER(filebuf_overflow, 8)
int __thiscall filebuf_overflow(filebuf *this, int c)
{
    TRACE("(%p %d)\n", this, c);
    if (call_streambuf_sync(&this->base) == EOF)
        return EOF;
    if (this->base.unbuffered)
        return (c == EOF) ? 1 : _write(this->fd, &c, 1);
    if (streambuf_allocate(&this->base) == EOF)
        return EOF;

    this->base.pbase = this->base.pptr = this->base.base;
    this->base.epptr = this->base.ebuf;
    if (c != EOF)
        *this->base.pptr++ = c;
    return 1;
}

/* ?seekoff@filebuf@@UAEJJW4seek_dir@ios@@H@Z */
/* ?seekoff@filebuf@@UEAAJJW4seek_dir@ios@@H@Z */
DEFINE_THISCALL_WRAPPER(filebuf_seekoff, 16)
streampos __thiscall filebuf_seekoff(filebuf *this, streamoff offset, ios_seek_dir dir, int mode)
{
    TRACE("(%p %d %d %d)\n", this, offset, dir, mode);
    if (call_streambuf_sync(&this->base) == EOF)
        return EOF;
    return _lseek(this->fd, offset, dir);
}

/* ?setbuf@filebuf@@UAEPAVstreambuf@@PADH@Z */
/* ?setbuf@filebuf@@UEAAPEAVstreambuf@@PEADH@Z */
DEFINE_THISCALL_WRAPPER(filebuf_setbuf, 12)
streambuf* __thiscall filebuf_setbuf(filebuf *this, char *buffer, int length)
{
    streambuf *ret;

    TRACE("(%p %p %d)\n", this, buffer, length);
    if (this->base.base != NULL)
        return NULL;

    streambuf_lock(&this->base);
    ret = streambuf_setbuf(&this->base, buffer, length);
    streambuf_unlock(&this->base);
    return ret;
}

/* ?setmode@filebuf@@QAEHH@Z */
/* ?setmode@filebuf@@QEAAHH@Z */
DEFINE_THISCALL_WRAPPER(filebuf_setmode, 8)
int __thiscall filebuf_setmode(filebuf *this, int mode)
{
    int ret;

    TRACE("(%p %d)\n", this, mode);
    if (mode != filebuf_text && mode != filebuf_binary)
        return -1;

    streambuf_lock(&this->base);
    ret = (call_streambuf_sync(&this->base) == EOF) ? -1 : _setmode(this->fd, mode);
    streambuf_unlock(&this->base);
    return ret;
}

/* ?sync@filebuf@@UAEHXZ */
/* ?sync@filebuf@@UEAAHXZ */
DEFINE_THISCALL_WRAPPER(filebuf_sync, 4)
int __thiscall filebuf_sync(filebuf *this)
{
    int count, mode;
    char *ptr;
    LONG offset;

    TRACE("(%p)\n", this);
    if (this->fd == -1)
        return EOF;
    if (this->base.unbuffered)
        return 0;

    /* flush output buffer */
    if (this->base.pptr != NULL) {
        count = this->base.pptr - this->base.pbase;
        if (count > 0 && _write(this->fd, this->base.pbase, count) != count)
            return EOF;
    }
    this->base.pbase = this->base.pptr = this->base.epptr = NULL;
    /* flush input buffer */
    if (this->base.egptr != NULL) {
        offset = this->base.egptr - this->base.gptr;
        if (offset > 0) {
            mode = _setmode(this->fd, _O_TEXT);
            _setmode(this->fd, mode);
            if (mode & _O_TEXT) {
                /* in text mode, '\n' in the buffer means '\r\n' in the file */
                for (ptr = this->base.gptr; ptr < this->base.egptr; ptr++)
                    if (*ptr == '\n')
                        offset++;
            }
            if (_lseek(this->fd, -offset, SEEK_CUR) < 0)
                return EOF;
        }
    }
    this->base.eback = this->base.gptr = this->base.egptr = NULL;
    return 0;
}

/* ?underflow@filebuf@@UAEHXZ */
/* ?underflow@filebuf@@UEAAHXZ */
DEFINE_THISCALL_WRAPPER(filebuf_underflow, 4)
int __thiscall filebuf_underflow(filebuf *this)
{
    int buffer_size, read_bytes;
    char c;

    TRACE("(%p)\n", this);

    if (this->base.unbuffered)
        return (_read(this->fd, &c, 1) < 1) ? EOF : (unsigned char) c;

    if (this->base.gptr >= this->base.egptr) {
        if (call_streambuf_sync(&this->base) == EOF)
            return EOF;
        buffer_size = this->base.ebuf - this->base.base;
        read_bytes = _read(this->fd, this->base.base, buffer_size);
        if (read_bytes <= 0)
            return EOF;
        this->base.eback = this->base.gptr = this->base.base;
        this->base.egptr = this->base.base + read_bytes;
    }
    return (unsigned char) *this->base.gptr;
}

/* ??0strstreambuf@@QAE@ABV0@@Z */
/* ??0strstreambuf@@QEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_copy_ctor, 8)
strstreambuf* __thiscall strstreambuf_copy_ctor(strstreambuf *this, const strstreambuf *copy)
{
    TRACE("(%p %p)\n", this, copy);
    *this = *copy;
    this->base.vtable = &MSVCP_strstreambuf_vtable;
    return this;
}

/* ??0strstreambuf@@QAE@H@Z */
/* ??0strstreambuf@@QEAA@H@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_dynamic_ctor, 8)
strstreambuf* __thiscall strstreambuf_dynamic_ctor(strstreambuf* this, int length)
{
    TRACE("(%p %d)\n", this, length);
    streambuf_ctor(&this->base);
    this->base.vtable = &MSVCP_strstreambuf_vtable;
    this->dynamic = 1;
    this->increase = length;
    this->constant = 0;
    this->f_alloc = NULL;
    this->f_free = NULL;
    return this;
}

/* ??0strstreambuf@@QAE@P6APAXJ@ZP6AXPAX@Z@Z */
/* ??0strstreambuf@@QEAA@P6APEAXJ@ZP6AXPEAX@Z@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_funcs_ctor, 12)
strstreambuf* __thiscall strstreambuf_funcs_ctor(strstreambuf* this, allocFunction falloc, freeFunction ffree)
{
    TRACE("(%p %p %p)\n", this, falloc, ffree);
    strstreambuf_dynamic_ctor(this, 1);
    this->f_alloc = falloc;
    this->f_free = ffree;
    return this;
}

/* ??0strstreambuf@@QAE@PADH0@Z */
/* ??0strstreambuf@@QEAA@PEADH0@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_buffer_ctor, 16)
strstreambuf* __thiscall strstreambuf_buffer_ctor(strstreambuf *this, char *buffer, int length, char *put)
{
    char *end_buffer;

    TRACE("(%p %p %d %p)\n", this, buffer, length, put);

    if (length > 0)
        end_buffer = buffer + length;
    else if (length == 0)
        end_buffer = buffer + strlen(buffer);
    else
        end_buffer = (char*) -1;

    streambuf_ctor(&this->base);
    streambuf_setb(&this->base, buffer, end_buffer, 0);
    if (put == NULL) {
        streambuf_setg(&this->base, buffer, buffer, end_buffer);
    } else {
        streambuf_setg(&this->base, buffer, buffer, put);
        streambuf_setp(&this->base, put, end_buffer);
    }
    this->base.vtable = &MSVCP_strstreambuf_vtable;
    this->dynamic = 0;
    this->constant = 1;
    return this;
}

/* ??0strstreambuf@@QAE@PAEH0@Z */
/* ??0strstreambuf@@QEAA@PEAEH0@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_ubuffer_ctor, 16)
strstreambuf* __thiscall strstreambuf_ubuffer_ctor(strstreambuf *this, unsigned char *buffer, int length, unsigned char *put)
{
    TRACE("(%p %p %d %p)\n", this, buffer, length, put);
    return strstreambuf_buffer_ctor(this, (char*)buffer, length, (char*)put);
}

/* ??0strstreambuf@@QAE@XZ */
/* ??0strstreambuf@@QEAA@XZ */
DEFINE_THISCALL_WRAPPER(strstreambuf_ctor, 4)
strstreambuf* __thiscall strstreambuf_ctor(strstreambuf *this)
{
    TRACE("(%p)\n", this);
    return strstreambuf_dynamic_ctor(this, 1);
}

/* ??1strstreambuf@@UAE@XZ */
/* ??1strstreambuf@@UEAA@XZ */
DEFINE_THISCALL_WRAPPER(strstreambuf_dtor, 4)
void __thiscall strstreambuf_dtor(strstreambuf *this)
{
    TRACE("(%p)\n", this);
    if (this->dynamic && this->base.base) {
        if (this->f_free)
            this->f_free(this->base.base);
        else
            MSVCRT_operator_delete(this->base.base);
    }
    streambuf_dtor(&this->base);
}

/* ??4strstreambuf@@QAEAAV0@ABV0@@Z */
/* ??4strstreambuf@@QEAAAEAV0@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_assign, 8)
strstreambuf* __thiscall strstreambuf_assign(strstreambuf *this, const strstreambuf *rhs)
{
    strstreambuf_dtor(this);
    return strstreambuf_copy_ctor(this, rhs);
}

/* ??_Estrstreambuf@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_vector_dtor, 8)
strstreambuf* __thiscall strstreambuf_vector_dtor(strstreambuf *this, unsigned int flags)
{
    TRACE("(%p %x)\n", this, flags);
    if (flags & 2) {
        /* we have an array, with the number of elements stored before the first object */
        INT_PTR i, *ptr = (INT_PTR *)this-1;

        for (i = *ptr-1; i >= 0; i--)
            strstreambuf_dtor(this+i);
        MSVCRT_operator_delete(ptr);
    } else {
        strstreambuf_dtor(this);
        if (flags & 1)
            MSVCRT_operator_delete(this);
    }
    return this;
}

/* ??_Gstrstreambuf@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_scalar_dtor, 8)
strstreambuf* __thiscall strstreambuf_scalar_dtor(strstreambuf *this, unsigned int flags)
{
    TRACE("(%p %x)\n", this, flags);
    strstreambuf_dtor(this);
    if (flags & 1) MSVCRT_operator_delete(this);
    return this;
}

/* ?doallocate@strstreambuf@@MAEHXZ */
/* ?doallocate@strstreambuf@@MEAAHXZ */
DEFINE_THISCALL_WRAPPER(strstreambuf_doallocate, 4)
int __thiscall strstreambuf_doallocate(strstreambuf *this)
{
    char *prev_buffer = this->base.base, *new_buffer;
    LONG prev_size = this->base.ebuf - this->base.base, new_size;

    TRACE("(%p)\n", this);

    /* calculate the size of the new buffer */
    new_size = (prev_size > 0 ? prev_size : 0) + (this->increase > 0 ? this->increase : 1);
    /* get a new buffer */
    if (this->f_alloc)
        new_buffer = this->f_alloc(new_size);
    else
        new_buffer = MSVCRT_operator_new(new_size);
    if (!new_buffer)
        return EOF;
    if (this->base.ebuf) {
        /* copy the contents and adjust the pointers */
        memcpy(new_buffer, this->base.base, prev_size);
        if (this->base.egptr) {
            this->base.eback += new_buffer - prev_buffer;
            this->base.gptr += new_buffer - prev_buffer;
            this->base.egptr += new_buffer - prev_buffer;
        }
        if (this->base.epptr) {
            this->base.pbase += new_buffer - prev_buffer;
            this->base.pptr += new_buffer - prev_buffer;
            this->base.epptr += new_buffer - prev_buffer;
        }
        /* free the old buffer */
        if (this->f_free)
            this->f_free(this->base.base);
        else
            MSVCRT_operator_delete(this->base.base);
    }
    streambuf_setb(&this->base, new_buffer, new_buffer + new_size, 0);
    return 1;
}

/* ?freeze@strstreambuf@@QAEXH@Z */
/* ?freeze@strstreambuf@@QEAAXH@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_freeze, 8)
void __thiscall strstreambuf_freeze(strstreambuf *this, int frozen)
{
    TRACE("(%p %d)\n", this, frozen);
    if (!this->constant)
        this->dynamic = !frozen;
}

/* ?overflow@strstreambuf@@UAEHH@Z */
/* ?overflow@strstreambuf@@UEAAHH@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_overflow, 8)
int __thiscall strstreambuf_overflow(strstreambuf *this, int c)
{
    TRACE("(%p %d)\n", this, c);
    if (this->base.pptr >= this->base.epptr) {
        /* increase the buffer size if it's dynamic */
        if (!this->dynamic || call_streambuf_doallocate(&this->base) == EOF)
            return EOF;
        if (!this->base.epptr)
            this->base.pbase = this->base.pptr = this->base.egptr ? this->base.egptr : this->base.base;
        this->base.epptr = this->base.ebuf;
    }
    if (c != EOF)
        *this->base.pptr++ = c;
    return 1;
}

/* ?seekoff@strstreambuf@@UAEJJW4seek_dir@ios@@H@Z */
/* ?seekoff@strstreambuf@@UEAAJJW4seek_dir@ios@@H@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_seekoff, 16)
streampos __thiscall strstreambuf_seekoff(strstreambuf *this, streamoff offset, ios_seek_dir dir, int mode)
{
    char *base[3];

    TRACE("(%p %d %d %d)\n", this, offset, dir, mode);

    if ((unsigned int)dir > SEEKDIR_end || !(mode & (OPENMODE_in|OPENMODE_out)))
        return EOF;
    /* read buffer */
    if (mode & OPENMODE_in) {
        call_streambuf_underflow(&this->base);
        base[SEEKDIR_beg] = this->base.eback;
        base[SEEKDIR_cur] = this->base.gptr;
        base[SEEKDIR_end] = this->base.egptr;
        if (base[dir] + offset < this->base.eback || base[dir] + offset > this->base.egptr)
            return EOF;
        this->base.gptr = base[dir] + offset;
    }
    /* write buffer */
    if (mode & OPENMODE_out) {
        if (!this->base.epptr && call_streambuf_overflow(&this->base, EOF) == EOF)
            return EOF;
        base[SEEKDIR_beg] = this->base.pbase;
        base[SEEKDIR_cur] = this->base.pptr;
        base[SEEKDIR_end] = this->base.epptr;
        if (base[dir] + offset < this->base.pbase)
            return EOF;
        if (base[dir] + offset > this->base.epptr) {
            /* make room if the buffer is dynamic */
            if (!this->dynamic)
                return EOF;
            this->increase = offset;
            if (call_streambuf_doallocate(&this->base) == EOF)
                return EOF;
        }
        this->base.pptr = base[dir] + offset;
        return this->base.pptr - base[SEEKDIR_beg];
    }
    return this->base.gptr - base[SEEKDIR_beg];
}

/* ?setbuf@strstreambuf@@UAEPAVstreambuf@@PADH@Z */
/* ?setbuf@strstreambuf@@UEAAPEAVstreambuf@@PEADH@Z */
DEFINE_THISCALL_WRAPPER(strstreambuf_setbuf, 12)
streambuf* __thiscall strstreambuf_setbuf(strstreambuf *this, char *buffer, int length)
{
    TRACE("(%p %p %d)\n", this, buffer, length);
    if (length)
        this->increase = length;
    return &this->base;
}

/* ?str@strstreambuf@@QAEPADXZ */
/* ?str@strstreambuf@@QEAAPEADXZ */
DEFINE_THISCALL_WRAPPER(strstreambuf_str, 4)
char* __thiscall strstreambuf_str(strstreambuf *this)
{
    TRACE("(%p)\n", this);
    strstreambuf_freeze(this, 1);
    return this->base.base;
}

/* ?sync@strstreambuf@@UAEHXZ */
/* ?sync@strstreambuf@@UEAAHXZ */
DEFINE_THISCALL_WRAPPER(strstreambuf_sync, 4)
int __thiscall strstreambuf_sync(strstreambuf *this)
{
    TRACE("(%p)\n", this);
    return 0;
}

/* ?underflow@strstreambuf@@UAEHXZ */
/* ?underflow@strstreambuf@@UEAAHXZ */
DEFINE_THISCALL_WRAPPER(strstreambuf_underflow, 4)
int __thiscall strstreambuf_underflow(strstreambuf *this)
{
    TRACE("(%p)\n", this);
    if (this->base.gptr < this->base.egptr)
        return (unsigned char) *this->base.gptr;
    /* extend the get area to include the characters written */
    if (this->base.egptr < this->base.pptr) {
        this->base.gptr = this->base.base + (this->base.gptr - this->base.eback);
        this->base.eback = this->base.base;
        this->base.egptr = this->base.pptr;
    }
    return (this->base.gptr < this->base.egptr) ? (unsigned char)(*this->base.gptr) : EOF;
}

/* ??0stdiobuf@@QAE@ABV0@@Z */
/* ??0stdiobuf@@QEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(stdiobuf_copy_ctor, 8)
stdiobuf* __thiscall stdiobuf_copy_ctor(stdiobuf *this, const stdiobuf *copy)
{
    TRACE("(%p %p)\n", this, copy);
    *this = *copy;
    this->base.vtable = &MSVCP_stdiobuf_vtable;
    return this;
}

/* ??0stdiobuf@@QAE@PAU_iobuf@@@Z */
/* ??0stdiobuf@@QEAA@PEAU_iobuf@@@Z */
DEFINE_THISCALL_WRAPPER(stdiobuf_file_ctor, 8)
stdiobuf* __thiscall stdiobuf_file_ctor(stdiobuf *this, FILE *file)
{
    TRACE("(%p %p)\n", this, file);
    streambuf_reserve_ctor(&this->base, NULL, 0);
    this->base.vtable = &MSVCP_stdiobuf_vtable;
    this->file = file;
    return this;
}

/* ??1stdiobuf@@UAE@XZ */
/* ??1stdiobuf@@UEAA@XZ */
DEFINE_THISCALL_WRAPPER(stdiobuf_dtor, 4)
void __thiscall stdiobuf_dtor(stdiobuf *this)
{
    TRACE("(%p)\n", this);
    call_streambuf_sync(&this->base);
    streambuf_dtor(&this->base);
}

/* ??4stdiobuf@@QAEAAV0@ABV0@@Z */
/* ??4stdiobuf@@QEAAAEAV0@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(stdiobuf_assign, 8)
stdiobuf* __thiscall stdiobuf_assign(stdiobuf *this, const stdiobuf *rhs)
{
    stdiobuf_dtor(this);
    return stdiobuf_copy_ctor(this, rhs);
}

/* ??_Estdiobuf@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(stdiobuf_vector_dtor, 8)
stdiobuf* __thiscall stdiobuf_vector_dtor(stdiobuf *this, unsigned int flags)
{
    TRACE("(%p %x)\n", this, flags);
    if (flags & 2) {
        /* we have an array, with the number of elements stored before the first object */
        INT_PTR i, *ptr = (INT_PTR *)this-1;

        for (i = *ptr-1; i >= 0; i--)
            stdiobuf_dtor(this+i);
        MSVCRT_operator_delete(ptr);
    } else {
        stdiobuf_dtor(this);
        if (flags & 1)
            MSVCRT_operator_delete(this);
    }
    return this;
}

/* ??_Gstdiobuf@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(stdiobuf_scalar_dtor, 8)
stdiobuf* __thiscall stdiobuf_scalar_dtor(stdiobuf *this, unsigned int flags)
{
    TRACE("(%p %x)\n", this, flags);
    stdiobuf_dtor(this);
    if (flags & 1) MSVCRT_operator_delete(this);
    return this;
}

/* ?overflow@stdiobuf@@UAEHH@Z */
/* ?overflow@stdiobuf@@UEAAHH@Z */
DEFINE_THISCALL_WRAPPER(stdiobuf_overflow, 8)
int __thiscall stdiobuf_overflow(stdiobuf *this, int c)
{
    TRACE("(%p %d)\n", this, c);
    if (this->base.unbuffered)
        return (c == EOF) ? 1 : fputc(c, this->file);
    if (streambuf_allocate(&this->base) == EOF)
        return EOF;

    if (!this->base.epptr) {
        /* set the put area to the second half of the buffer */
        streambuf_setp(&this->base,
            this->base.base + (this->base.ebuf - this->base.base) / 2, this->base.ebuf);
    } else if (this->base.pptr > this->base.pbase) {
        /* flush the put area */
        int count = this->base.pptr - this->base.pbase;
        if (fwrite(this->base.pbase, sizeof(char), count, this->file) != count)
            return EOF;
        this->base.pptr = this->base.pbase;
    }
    if (c != EOF) {
        if (this->base.pbase >= this->base.epptr)
            return fputc(c, this->file);
        *this->base.pptr++ = c;
    }
    return 1;
}

/* ?pbackfail@stdiobuf@@UAEHH@Z */
/* ?pbackfail@stdiobuf@@UEAAHH@Z */
DEFINE_THISCALL_WRAPPER(stdiobuf_pbackfail, 8)
int __thiscall stdiobuf_pbackfail(stdiobuf *this, int c)
{
    TRACE("(%p %d)\n", this, c);
    return streambuf_pbackfail(&this->base, c);
}

/* ?seekoff@stdiobuf@@UAEJJW4seek_dir@ios@@H@Z */
/* ?seekoff@stdiobuf@@UEAAJJW4seek_dir@ios@@H@Z */
DEFINE_THISCALL_WRAPPER(stdiobuf_seekoff, 16)
streampos __thiscall stdiobuf_seekoff(stdiobuf *this, streamoff offset, ios_seek_dir dir, int mode)
{
    TRACE("(%p %d %d %d)\n", this, offset, dir, mode);
    call_streambuf_overflow(&this->base, EOF);
    if (fseek(this->file, offset, dir))
        return EOF;
    return ftell(this->file);
}

/* ?setrwbuf@stdiobuf@@QAEHHH@Z */
/* ?setrwbuf@stdiobuf@@QEAAHHH@Z */
DEFINE_THISCALL_WRAPPER(stdiobuf_setrwbuf, 12)
int __thiscall stdiobuf_setrwbuf(stdiobuf *this, int read_size, int write_size)
{
    char *reserve;
    int buffer_size = read_size + write_size;

    TRACE("(%p %d %d)\n", this, read_size, write_size);
    if (read_size < 0 || write_size < 0)
        return 0;
    if (!buffer_size) {
        this->base.unbuffered = 1;
        return 0;
    }
    /* get a new buffer */
    reserve = MSVCRT_operator_new(buffer_size);
    if (!reserve)
        return 0;
    streambuf_setb(&this->base, reserve, reserve + buffer_size, 1);
    this->base.unbuffered = 0;
    /* set the get/put areas */
    if (read_size > 0)
        streambuf_setg(&this->base, reserve, reserve + read_size, reserve + read_size);
    else
        streambuf_setg(&this->base, NULL, NULL, NULL);
    if (write_size > 0)
        streambuf_setp(&this->base, reserve + read_size, reserve + buffer_size);
    else
        streambuf_setp(&this->base, NULL, NULL);
    return 1;
}

/* ?stdiofile@stdiobuf@@QAEPAU_iobuf@@XZ */
/* ?stdiofile@stdiobuf@@QEAAPEAU_iobuf@@XZ */
DEFINE_THISCALL_WRAPPER(stdiobuf_stdiofile, 4)
FILE* __thiscall stdiobuf_stdiofile(stdiobuf *this)
{
    TRACE("(%p)\n", this);
    return this->file;
}

/* ?sync@stdiobuf@@UAEHXZ */
/* ?sync@stdiobuf@@UEAAHXZ */
DEFINE_THISCALL_WRAPPER(stdiobuf_sync, 4)
int __thiscall stdiobuf_sync(stdiobuf *this)
{
    TRACE("(%p)\n", this);
    if (this->base.unbuffered)
        return 0;
    /* flush the put area */
    if (call_streambuf_overflow(&this->base, EOF) == EOF)
        return EOF;
    /* flush the get area */
    if (this->base.gptr < this->base.egptr) {
        char *ptr;
        int fd, mode, offset = this->base.egptr - this->base.gptr;
        if ((fd = fileno(this->file)) < 0)
            return EOF;
        mode = _setmode(fd, _O_TEXT);
        _setmode(fd, mode);
        if (mode & _O_TEXT) {
            /* in text mode, '\n' in the buffer means '\r\n' in the file */
            for (ptr = this->base.gptr; ptr < this->base.egptr; ptr++)
                if (*ptr == '\n')
                    offset++;
        }
        if (fseek(this->file, -offset, SEEK_CUR))
            return EOF;
        this->base.gptr = this->base.egptr;
    }
    return 0;
}

/* ?underflow@stdiobuf@@UAEHXZ */
/* ?underflow@stdiobuf@@UEAAHXZ */
DEFINE_THISCALL_WRAPPER(stdiobuf_underflow, 4)
int __thiscall stdiobuf_underflow(stdiobuf *this)
{
    TRACE("(%p)\n", this);
    if (!this->file)
        return EOF;
    if (this->base.unbuffered)
        return fgetc(this->file);
    if (streambuf_allocate(&this->base) == EOF)
        return EOF;

    if (!this->base.egptr) {
        /* set the get area to the first half of the buffer */
        char *middle = this->base.base + (this->base.ebuf - this->base.base) / 2;
        streambuf_setg(&this->base, this->base.base, middle, middle);
    }
    if (this->base.gptr >= this->base.egptr) {
        /* read characters from the file */
        int buffer_size = this->base.egptr - this->base.eback, read_bytes;
        if (!this->base.eback ||
            (read_bytes = fread(this->base.eback, sizeof(char), buffer_size, this->file)) <= 0)
            return EOF;
        memmove(this->base.egptr - read_bytes, this->base.eback, read_bytes);
        this->base.gptr = this->base.egptr - read_bytes;
    }
    return (unsigned char) *this->base.gptr++;
}

/* ??0ios@@IAE@ABV0@@Z */
/* ??0ios@@IEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(ios_copy_ctor, 8)
ios* __thiscall ios_copy_ctor(ios *this, const ios *copy)
{
    TRACE("(%p %p)\n", this, copy);
    ios_fLockcInit++;
    this->vtable = &MSVCP_ios_vtable;
    this->sb = NULL;
    this->delbuf = 0;
    this->do_lock = -1;
    InitializeCriticalSection(&this->lock);
    return ios_assign(this, copy);
}

/* ??0ios@@QAE@PAVstreambuf@@@Z */
/* ??0ios@@QEAA@PEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(ios_sb_ctor, 8)
ios* __thiscall ios_sb_ctor(ios *this, streambuf *sb)
{
    TRACE("(%p %p)\n", this, sb);
    ios_fLockcInit++;
    this->vtable = &MSVCP_ios_vtable;
    this->sb = sb;
    this->state = sb ? IOSTATE_goodbit : IOSTATE_badbit;
    this->special[0] = this->special[1] = 0;
    this->delbuf = 0;
    this->tie = NULL;
    this->flags = 0;
    this->precision = 6;
    this->fill = ' ';
    this->width = 0;
    this->do_lock = -1;
    InitializeCriticalSection(&this->lock);
    return this;
}

/* ??0ios@@IAE@XZ */
/* ??0ios@@IEAA@XZ */
DEFINE_THISCALL_WRAPPER(ios_ctor, 4)
ios* __thiscall ios_ctor(ios *this)
{
    return ios_sb_ctor(this, NULL);
}

/* ??1ios@@UAE@XZ */
/* ??1ios@@UEAA@XZ */
DEFINE_THISCALL_WRAPPER(ios_dtor, 4)
void __thiscall ios_dtor(ios *this)
{
    TRACE("(%p)\n", this);
    ios_fLockcInit--;
    if (this->delbuf && this->sb)
        call_streambuf_vector_dtor(this->sb, 1);
    this->sb = NULL;
    this->state = IOSTATE_badbit;
    DeleteCriticalSection(&this->lock);
}

/* ??4ios@@IAEAAV0@ABV0@@Z */
/* ??4ios@@IEAAAEAV0@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(ios_assign, 8)
ios* __thiscall ios_assign(ios *this, const ios *rhs)
{
    TRACE("(%p %p)\n", this, rhs);
    this->state = rhs->state;
    if (!this->sb)
        this->state |= IOSTATE_badbit;
    this->tie = rhs->tie;
    this->flags = rhs->flags;
    this->precision = (char) rhs->precision;
    this->fill = rhs->fill;
    this->width = (char) rhs->width;
    return this;
}

/* ??7ios@@QBEHXZ */
/* ??7ios@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(ios_op_not, 4)
int __thiscall ios_op_not(const ios *this)
{
    TRACE("(%p)\n", this);
    return ios_fail(this);
}

/* ??Bios@@QBEPAXXZ */
/* ??Bios@@QEBAPEAXXZ */
DEFINE_THISCALL_WRAPPER(ios_op_void, 4)
void* __thiscall ios_op_void(const ios *this)
{
    TRACE("(%p)\n", this);
    return ios_fail(this) ? NULL : (void*)this;
}

/* ??_Eios@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(ios_vector_dtor, 8)
ios* __thiscall ios_vector_dtor(ios *this, unsigned int flags)
{
    TRACE("(%p %x)\n", this, flags);
    if (flags & 2) {
        /* we have an array, with the number of elements stored before the first object */
        INT_PTR i, *ptr = (INT_PTR *)this-1;

        for (i = *ptr-1; i >= 0; i--)
            ios_dtor(this+i);
        MSVCRT_operator_delete(ptr);
    } else {
        ios_dtor(this);
        if (flags & 1)
            MSVCRT_operator_delete(this);
    }
    return this;
}

/* ??_Gios@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(ios_scalar_dtor, 8)
ios* __thiscall ios_scalar_dtor(ios *this, unsigned int flags)
{
    TRACE("(%p %x)\n", this, flags);
    ios_dtor(this);
    if (flags & 1) MSVCRT_operator_delete(this);
    return this;
}

/* ?bad@ios@@QBEHXZ */
/* ?bad@ios@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(ios_bad, 4)
int __thiscall ios_bad(const ios *this)
{
    TRACE("(%p)\n", this);
    return (this->state & IOSTATE_badbit);
}

/* ?bitalloc@ios@@SAJXZ */
LONG __cdecl ios_bitalloc(void)
{
    TRACE("()\n");
    ios_lockc();
    ios_maxbit <<= 1;
    ios_unlockc();
    return ios_maxbit;
}

/* ?clear@ios@@QAEXH@Z */
/* ?clear@ios@@QEAAXH@Z */
DEFINE_THISCALL_WRAPPER(ios_clear, 8)
void __thiscall ios_clear(ios *this, int state)
{
    TRACE("(%p %d)\n", this, state);
    ios_lock(this);
    this->state = state;
    ios_unlock(this);
}

/* ?clrlock@ios@@QAAXXZ */
/* ?clrlock@ios@@QEAAXXZ */
void __cdecl ios_clrlock(ios *this)
{
    TRACE("(%p)\n", this);
    if (this->do_lock <= 0)
        this->do_lock++;
    if (this->sb)
        streambuf_clrlock(this->sb);
}

/* ?delbuf@ios@@QAEXH@Z */
/* ?delbuf@ios@@QEAAXH@Z */
DEFINE_THISCALL_WRAPPER(ios_delbuf_set, 8)
void __thiscall ios_delbuf_set(ios *this, int delete)
{
    TRACE("(%p %d)\n", this, delete);
    this->delbuf = delete;
}

/* ?delbuf@ios@@QBEHXZ */
/* ?delbuf@ios@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(ios_delbuf_get, 4)
int __thiscall ios_delbuf_get(const ios *this)
{
    TRACE("(%p)\n", this);
    return this->delbuf;
}

/* ?dec@@YAAAVios@@AAV1@@Z */
/* ?dec@@YAAEAVios@@AEAV1@@Z */
ios* __cdecl ios_dec(ios *this)
{
    TRACE("(%p)\n", this);
    ios_setf_mask(this, FLAGS_dec, ios_basefield);
    return this;
}

/* ?eof@ios@@QBEHXZ */
/* ?eof@ios@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(ios_eof, 4)
int __thiscall ios_eof(const ios *this)
{
    TRACE("(%p)\n", this);
    return (this->state & IOSTATE_eofbit);
}

/* ?fail@ios@@QBEHXZ */
/* ?fail@ios@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(ios_fail, 4)
int __thiscall ios_fail(const ios *this)
{
    TRACE("(%p)\n", this);
    return (this->state & (IOSTATE_failbit|IOSTATE_badbit));
}

/* ?fill@ios@@QAEDD@Z */
/* ?fill@ios@@QEAADD@Z */
DEFINE_THISCALL_WRAPPER(ios_fill_set, 8)
char __thiscall ios_fill_set(ios *this, char fill)
{
    char prev = this->fill;

    TRACE("(%p %d)\n", this, fill);

    this->fill = fill;
    return prev;
}

/* ?fill@ios@@QBEDXZ */
/* ?fill@ios@@QEBADXZ */
DEFINE_THISCALL_WRAPPER(ios_fill_get, 4)
char __thiscall ios_fill_get(const ios *this)
{
    TRACE("(%p)\n", this);
    return this->fill;
}

/* ?flags@ios@@QAEJJ@Z */
/* ?flags@ios@@QEAAJJ@Z */
DEFINE_THISCALL_WRAPPER(ios_flags_set, 8)
LONG __thiscall ios_flags_set(ios *this, LONG flags)
{
    LONG prev = this->flags;

    TRACE("(%p %x)\n", this, flags);

    this->flags = flags;
    return prev;
}

/* ?flags@ios@@QBEJXZ */
/* ?flags@ios@@QEBAJXZ */
DEFINE_THISCALL_WRAPPER(ios_flags_get, 4)
LONG __thiscall ios_flags_get(const ios *this)
{
    TRACE("(%p)\n", this);
    return this->flags;
}

/* ?good@ios@@QBEHXZ */
/* ?good@ios@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(ios_good, 4)
int __thiscall ios_good(const ios *this)
{
    TRACE("(%p)\n", this);
    return this->state == IOSTATE_goodbit;
}

/* ?hex@@YAAAVios@@AAV1@@Z */
/* ?hex@@YAAEAVios@@AEAV1@@Z */
ios* __cdecl ios_hex(ios *this)
{
    TRACE("(%p)\n", this);
    ios_setf_mask(this, FLAGS_hex, ios_basefield);
    return this;
}

/* ?init@ios@@IAEXPAVstreambuf@@@Z */
/* ?init@ios@@IEAAXPEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(ios_init, 8)
void __thiscall ios_init(ios *this, streambuf *sb)
{
    TRACE("(%p %p)\n", this, sb);
    if (this->delbuf && this->sb)
        call_streambuf_vector_dtor(this->sb, 1);
    this->sb = sb;
    if (sb == NULL)
        this->state |= IOSTATE_badbit;
    else
        this->state &= ~IOSTATE_badbit;
}

/* ?iword@ios@@QBEAAJH@Z */
/* ?iword@ios@@QEBAAEAJH@Z */
DEFINE_THISCALL_WRAPPER(ios_iword, 8)
LONG* __thiscall ios_iword(const ios *this, int index)
{
    TRACE("(%p %d)\n", this, index);
    return &ios_statebuf[index];
}

/* ?lock@ios@@QAAXXZ */
/* ?lock@ios@@QEAAXXZ */
void __cdecl ios_lock(ios *this)
{
    TRACE("(%p)\n", this);
    if (this->do_lock < 0)
        EnterCriticalSection(&this->lock);
}

/* ?lockbuf@ios@@QAAXXZ */
/* ?lockbuf@ios@@QEAAXXZ */
void __cdecl ios_lockbuf(ios *this)
{
    TRACE("(%p)\n", this);
    streambuf_lock(this->sb);
}

/* ?lockc@ios@@KAXXZ */
void __cdecl ios_lockc(void)
{
    TRACE("()\n");
    EnterCriticalSection(&ios_static_lock);
}

/* ?lockptr@ios@@IAEPAU_CRT_CRITICAL_SECTION@@XZ */
/* ?lockptr@ios@@IEAAPEAU_CRT_CRITICAL_SECTION@@XZ */
DEFINE_THISCALL_WRAPPER(ios_lockptr, 4)
CRITICAL_SECTION* __thiscall ios_lockptr(ios *this)
{
    TRACE("(%p)\n", this);
    return &this->lock;
}

/* ?oct@@YAAAVios@@AAV1@@Z */
/* ?oct@@YAAEAVios@@AEAV1@@Z */
ios* __cdecl ios_oct(ios *this)
{
    TRACE("(%p)\n", this);
    ios_setf_mask(this, FLAGS_oct, ios_basefield);
    return this;
}

/* ?precision@ios@@QAEHH@Z */
/* ?precision@ios@@QEAAHH@Z */
DEFINE_THISCALL_WRAPPER(ios_precision_set, 8)
int __thiscall ios_precision_set(ios *this, int prec)
{
    int prev = this->precision;

    TRACE("(%p %d)\n", this, prec);

    this->precision = prec;
    return prev;
}

/* ?precision@ios@@QBEHXZ */
/* ?precision@ios@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(ios_precision_get, 4)
int __thiscall ios_precision_get(const ios *this)
{
    TRACE("(%p)\n", this);
    return this->precision;
}

/* ?pword@ios@@QBEAAPAXH@Z */
/* ?pword@ios@@QEBAAEAPEAXH@Z */
DEFINE_THISCALL_WRAPPER(ios_pword, 8)
void** __thiscall ios_pword(const ios *this, int index)
{
    TRACE("(%p %d)\n", this, index);
    return (void**)&ios_statebuf[index];
}

/* ?rdbuf@ios@@QBEPAVstreambuf@@XZ */
/* ?rdbuf@ios@@QEBAPEAVstreambuf@@XZ */
DEFINE_THISCALL_WRAPPER(ios_rdbuf, 4)
streambuf* __thiscall ios_rdbuf(const ios *this)
{
    TRACE("(%p)\n", this);
    return this->sb;
}

/* ?rdstate@ios@@QBEHXZ */
/* ?rdstate@ios@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(ios_rdstate, 4)
int __thiscall ios_rdstate(const ios *this)
{
    TRACE("(%p)\n", this);
    return this->state;
}

/* ?setf@ios@@QAEJJ@Z */
/* ?setf@ios@@QEAAJJ@Z */
DEFINE_THISCALL_WRAPPER(ios_setf, 8)
LONG __thiscall ios_setf(ios *this, LONG flags)
{
    LONG prev = this->flags;

    TRACE("(%p %x)\n", this, flags);

    ios_lock(this);
    this->flags |= flags;
    ios_unlock(this);
    return prev;
}

/* ?setf@ios@@QAEJJJ@Z */
/* ?setf@ios@@QEAAJJJ@Z */
DEFINE_THISCALL_WRAPPER(ios_setf_mask, 12)
LONG __thiscall ios_setf_mask(ios *this, LONG flags, LONG mask)
{
    LONG prev = this->flags;

    TRACE("(%p %x %x)\n", this, flags, mask);

    ios_lock(this);
    this->flags = (this->flags & (~mask)) | (flags & mask);
    ios_unlock(this);
    return prev;
}

/* ?setlock@ios@@QAAXXZ */
/* ?setlock@ios@@QEAAXXZ */
void __cdecl ios_setlock(ios *this)
{
    TRACE("(%p)\n", this);
    this->do_lock--;
    if (this->sb)
        streambuf_setlock(this->sb);
}

/* ?tie@ios@@QAEPAVostream@@PAV2@@Z */
/* ?tie@ios@@QEAAPEAVostream@@PEAV2@@Z */
DEFINE_THISCALL_WRAPPER(ios_tie_set, 8)
ostream* __thiscall ios_tie_set(ios *this, ostream *ostr)
{
    ostream *prev = this->tie;

    TRACE("(%p %p)\n", this, ostr);

    this->tie = ostr;
    return prev;
}

/* ?tie@ios@@QBEPAVostream@@XZ */
/* ?tie@ios@@QEBAPEAVostream@@XZ */
DEFINE_THISCALL_WRAPPER(ios_tie_get, 4)
ostream* __thiscall ios_tie_get(const ios *this)
{
    TRACE("(%p)\n", this);
    return this->tie;
}

/* ?unlock@ios@@QAAXXZ */
/* ?unlock@ios@@QEAAXXZ */
void __cdecl ios_unlock(ios *this)
{
    TRACE("(%p)\n", this);
    if (this->do_lock < 0)
        LeaveCriticalSection(&this->lock);
}

/* ?unlockbuf@ios@@QAAXXZ */
/* ?unlockbuf@ios@@QEAAXXZ */
void __cdecl ios_unlockbuf(ios *this)
{
    TRACE("(%p)\n", this);
    streambuf_unlock(this->sb);
}

/* ?unlockc@ios@@KAXXZ */
void __cdecl ios_unlockc(void)
{
    TRACE("()\n");
    LeaveCriticalSection(&ios_static_lock);
}

/* ?unsetf@ios@@QAEJJ@Z */
/* ?unsetf@ios@@QEAAJJ@Z */
DEFINE_THISCALL_WRAPPER(ios_unsetf, 8)
LONG __thiscall ios_unsetf(ios *this, LONG flags)
{
    LONG prev = this->flags;

    TRACE("(%p %x)\n", this, flags);

    ios_lock(this);
    this->flags &= ~flags;
    ios_unlock(this);
    return prev;
}

/* ?width@ios@@QAEHH@Z */
/* ?width@ios@@QEAAHH@Z */
DEFINE_THISCALL_WRAPPER(ios_width_set, 8)
int __thiscall ios_width_set(ios *this, int width)
{
    int prev = this->width;

    TRACE("(%p %d)\n", this, width);

    this->width = width;
    return prev;
}

/* ?width@ios@@QBEHXZ */
/* ?width@ios@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(ios_width_get, 4)
int __thiscall ios_width_get(const ios *this)
{
    TRACE("(%p)\n", this);
    return this->width;
}

/* ?xalloc@ios@@SAHXZ */
int __cdecl ios_xalloc(void)
{
    int ret;

    TRACE("()\n");

    ios_lockc();
    ret = (ios_curindex < STATEBUF_SIZE-1) ? ++ios_curindex : -1;
    ios_unlockc();
    return ret;
}

static inline ios* ostream_get_ios(const ostream *this)
{
    return (ios*)((char*)this + this->vbtable[1]);
}

static inline ios* ostream_to_ios(const ostream *this)
{
    return (ios*)((char*)this + ostream_vbtable[1]);
}

static inline ostream* ios_to_ostream(const ios *base)
{
    return (ostream*)((char*)base - ostream_vbtable[1]);
}

/* ??0ostream@@IAE@XZ */
/* ??0ostream@@IEAA@XZ */
DEFINE_THISCALL_WRAPPER(ostream_ctor, 8)
ostream* __thiscall ostream_ctor(ostream *this, BOOL virt_init)
{
    ios *base;

    TRACE("(%p %d)\n", this, virt_init);

    if (virt_init) {
        this->vbtable = ostream_vbtable;
        base = ostream_get_ios(this);
        ios_ctor(base);
    } else
        base = ostream_get_ios(this);
    base->vtable = &MSVCP_ostream_vtable;
    this->unknown = 0;
    return this;
}

/* ??0ostream@@QAE@PAVstreambuf@@@Z */
/* ??0ostream@@QEAA@PEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(ostream_sb_ctor, 12)
ostream* __thiscall ostream_sb_ctor(ostream *this, streambuf *sb, BOOL virt_init)
{
    TRACE("(%p %p %d)\n", this, sb, virt_init);
    ostream_ctor(this, virt_init);
    ios_init(ostream_get_ios(this), sb);
    return this;
}

/* ??0ostream@@IAE@ABV0@@Z */
/* ??0ostream@@IEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(ostream_copy_ctor, 12)
ostream* __thiscall ostream_copy_ctor(ostream *this, const ostream *copy, BOOL virt_init)
{
    return ostream_sb_ctor(this, ostream_get_ios(copy)->sb, virt_init);
}

/* ??1ostream@@UAE@XZ */
/* ??1ostream@@UEAA@XZ */
/* ??1ostream_withassign@@UAE@XZ */
/* ??1ostream_withassign@@UEAA@XZ */
/* ??1ostrstream@@UAE@XZ */
/* ??1ostrstream@@UEAA@XZ */
DEFINE_THISCALL_WRAPPER(ostream_dtor, 4)
void __thiscall ostream_dtor(ios *base)
{
    ostream *this = ios_to_ostream(base);

    TRACE("(%p)\n", this);
}

/* ??4ostream@@IAEAAV0@PAVstreambuf@@@Z */
/* ??4ostream@@IEAAAEAV0@PEAVstreambuf@@@Z */
/* ??4ostream_withassign@@QAEAAVostream@@PAVstreambuf@@@Z */
/* ??4ostream_withassign@@QEAAAEAVostream@@PEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(ostream_assign_sb, 8)
ostream* __thiscall ostream_assign_sb(ostream *this, streambuf *sb)
{
    ios *base = ostream_get_ios(this);

    TRACE("(%p %p)\n", this, sb);

    ios_init(base, sb);
    base->state &= IOSTATE_badbit;
    base->delbuf = 0;
    base->tie = NULL;
    base->flags = 0;
    base->precision = 6;
    base->fill = ' ';
    base->width = 0;
    return this;
}

/* ??4ostream@@IAEAAV0@ABV0@@Z */
/* ??4ostream@@IEAAAEAV0@AEBV0@@Z */
/* ??4ostream_withassign@@QAEAAV0@ABV0@@Z */
/* ??4ostream_withassign@@QEAAAEAV0@AEBV0@@Z */
/* ??4ostream_withassign@@QAEAAVostream@@ABV1@@Z */
/* ??4ostream_withassign@@QEAAAEAVostream@@AEBV1@@Z */
/* ??4ostrstream@@QAEAAV0@ABV0@@Z */
/* ??4ostrstream@@QEAAAEAV0@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(ostream_assign, 8)
ostream* __thiscall ostream_assign(ostream *this, const ostream *rhs)
{
    return ostream_assign_sb(this, ostream_get_ios(rhs)->sb);
}

/* ??_Dostream@@QAEXXZ */
/* ??_Dostream@@QEAAXXZ */
/* ??_Dostream_withassign@@QAEXXZ */
/* ??_Dostream_withassign@@QEAAXXZ */
/* ??_Dostrstream@@QAEXXZ */
/* ??_Dostrstream@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(ostream_vbase_dtor, 4)
void __thiscall ostream_vbase_dtor(ostream *this)
{
    ios *base = ostream_to_ios(this);

    TRACE("(%p)\n", this);

    ostream_dtor(base);
    ios_dtor(base);
}

/* ??_Eostream@@UAEPAXI@Z */
/* ??_Eostream_withassign@@UAEPAXI@Z */
/* ??_Eostrstream@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(ostream_vector_dtor, 8)
ostream* __thiscall ostream_vector_dtor(ios *base, unsigned int flags)
{
    ostream *this = ios_to_ostream(base);

    TRACE("(%p %x)\n", this, flags);

    if (flags & 2) {
        /* we have an array, with the number of elements stored before the first object */
        INT_PTR i, *ptr = (INT_PTR *)this-1;

        for (i = *ptr-1; i >= 0; i--)
            ostream_vbase_dtor(this+i);
        MSVCRT_operator_delete(ptr);
    } else {
        ostream_vbase_dtor(this);
        if (flags & 1)
            MSVCRT_operator_delete(this);
    }
    return this;
}

/* ??_Gostream@@UAEPAXI@Z */
/* ??_Gostream_withassign@@UAEPAXI@Z */
/* ??_Gostrstream@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(ostream_scalar_dtor, 8)
ostream* __thiscall ostream_scalar_dtor(ios *base, unsigned int flags)
{
    ostream *this = ios_to_ostream(base);

    TRACE("(%p %x)\n", this, flags);

    ostream_vbase_dtor(this);
    if (flags & 1) MSVCRT_operator_delete(this);
    return this;
}

/* ?flush@ostream@@QAEAAV1@XZ */
/* ?flush@ostream@@QEAAAEAV1@XZ */
DEFINE_THISCALL_WRAPPER(ostream_flush, 4)
ostream* __thiscall ostream_flush(ostream *this)
{
    ios *base = ostream_get_ios(this);

    TRACE("(%p)\n", this);

    ios_lockbuf(base);
    if (call_streambuf_sync(base->sb) == EOF)
        ios_clear(base, base->state | IOSTATE_failbit);
    ios_unlockbuf(base);
    return this;
}

/* ?opfx@ostream@@QAEHXZ */
/* ?opfx@ostream@@QEAAHXZ */
DEFINE_THISCALL_WRAPPER(ostream_opfx, 4)
int __thiscall ostream_opfx(ostream *this)
{
    ios *base = ostream_get_ios(this);

    TRACE("(%p)\n", this);

    if (!ios_good(base)) {
        ios_clear(base, base->state | IOSTATE_failbit);
        return 0;
    }
    ios_lock(base);
    ios_lockbuf(base);
    if (base->tie)
        ostream_flush(base->tie);
    return 1;
}

/* ?osfx@ostream@@QAEXXZ */
/* ?osfx@ostream@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(ostream_osfx, 4)
void __thiscall ostream_osfx(ostream *this)
{
    ios *base = ostream_get_ios(this);

    TRACE("(%p)\n", this);

    ios_unlockbuf(base);
    ios_width_set(base, 0);
    if (base->flags & FLAGS_unitbuf)
        ostream_flush(this);
    if (base->flags & FLAGS_stdio) {
        fflush(stdout);
        fflush(stderr);
    }
    ios_unlock(base);
}

/* ?put@ostream@@QAEAAV1@C@Z */
/* ?put@ostream@@QEAAAEAV1@C@Z */
/* ?put@ostream@@QAEAAV1@D@Z */
/* ?put@ostream@@QEAAAEAV1@D@Z */
/* ?put@ostream@@QAEAAV1@E@Z */
/* ?put@ostream@@QEAAAEAV1@E@Z */
DEFINE_THISCALL_WRAPPER(ostream_put, 8)
ostream* __thiscall ostream_put(ostream *this, char c)
{
    ios *base = ostream_get_ios(this);

    TRACE("(%p %c)\n", this, c);

    if (ostream_opfx(this)) {
        if (streambuf_sputc(base->sb, c) == EOF)
            base->state = IOSTATE_badbit | IOSTATE_failbit;
        ostream_osfx(this);
    }
    return this;
}

/* ?seekp@ostream@@QAEAAV1@J@Z */
/* ?seekp@ostream@@QEAAAEAV1@J@Z */
DEFINE_THISCALL_WRAPPER(ostream_seekp, 8)
ostream* __thiscall ostream_seekp(ostream *this, streampos pos)
{
    ios *base = ostream_get_ios(this);

    TRACE("(%p %d)\n", this, pos);

    ios_lockbuf(base);
    if (streambuf_seekpos(base->sb, pos, OPENMODE_out) == EOF)
        ios_clear(base, base->state | IOSTATE_failbit);
    ios_unlockbuf(base);
    return this;
}

/* ?seekp@ostream@@QAEAAV1@JW4seek_dir@ios@@@Z */
/* ?seekp@ostream@@QEAAAEAV1@JW4seek_dir@ios@@@Z */
DEFINE_THISCALL_WRAPPER(ostream_seekp_offset, 12)
ostream* __thiscall ostream_seekp_offset(ostream *this, streamoff off, ios_seek_dir dir)
{
    ios *base = ostream_get_ios(this);

    TRACE("(%p %d %d)\n", this, off, dir);

    ios_lockbuf(base);
    if (call_streambuf_seekoff(base->sb, off, dir, OPENMODE_out) == EOF)
        ios_clear(base, base->state | IOSTATE_failbit);
    ios_unlockbuf(base);
    return this;
}

/* ?tellp@ostream@@QAEJXZ */
/* ?tellp@ostream@@QEAAJXZ */
DEFINE_THISCALL_WRAPPER(ostream_tellp, 4)
streampos __thiscall ostream_tellp(ostream *this)
{
    ios *base = ostream_get_ios(this);
    streampos pos;

    TRACE("(%p)\n", this);

    ios_lockbuf(base);
    if ((pos = call_streambuf_seekoff(base->sb, 0, SEEKDIR_cur, OPENMODE_out)) == EOF)
        ios_clear(base, base->state | IOSTATE_failbit);
    ios_unlockbuf(base);
    return pos;
}

/* ?write@ostream@@QAEAAV1@PBCH@Z */
/* ?write@ostream@@QEAAAEAV1@PEBCH@Z */
/* ?write@ostream@@QAEAAV1@PBDH@Z */
/* ?write@ostream@@QEAAAEAV1@PEBDH@Z */
/* ?write@ostream@@QAEAAV1@PBEH@Z */
/* ?write@ostream@@QEAAAEAV1@PEBEH@Z */
DEFINE_THISCALL_WRAPPER(ostream_write, 12)
ostream* __thiscall ostream_write(ostream *this, const char *str, int count)
{
    ios *base = ostream_get_ios(this);

    TRACE("(%p %p %d)\n", this, str, count);

    if (ostream_opfx(this)) {
        if (streambuf_sputn(base->sb, str, count) != count)
            base->state = IOSTATE_badbit | IOSTATE_failbit;
        ostream_osfx(this);
    }
    return this;
}

/* ?writepad@ostream@@AAEAAV1@PBD0@Z */
/* ?writepad@ostream@@AEAAAEAV1@PEBD0@Z */
DEFINE_THISCALL_WRAPPER(ostream_writepad, 12)
ostream* __thiscall ostream_writepad(ostream *this, const char *str1, const char *str2)
{
    ios *base = ostream_get_ios(this);
    int len1 = strlen(str1), len2 = strlen(str2), i;

    TRACE("(%p %p %p)\n", this, str1, str2);

    /* left of the padding */
    if (base->flags & (FLAGS_left|FLAGS_internal)) {
        if (streambuf_sputn(base->sb, str1, len1) != len1)
            base->state |= IOSTATE_failbit | IOSTATE_badbit;
        if (!(base->flags & FLAGS_internal))
            if (streambuf_sputn(base->sb, str2, len2) != len2)
                base->state |= IOSTATE_failbit | IOSTATE_badbit;
    }
    /* add padding to fill the width */
    for (i = len1 + len2; i < base->width; i++)
        if (streambuf_sputc(base->sb, base->fill) == EOF)
            base->state |= IOSTATE_failbit | IOSTATE_badbit;
    /* right of the padding */
    if ((base->flags & (FLAGS_left|FLAGS_internal)) != FLAGS_left) {
        if (!(base->flags & (FLAGS_left|FLAGS_internal)))
            if (streambuf_sputn(base->sb, str1, len1) != len1)
                base->state |= IOSTATE_failbit | IOSTATE_badbit;
        if (streambuf_sputn(base->sb, str2, len2) != len2)
            base->state |= IOSTATE_failbit | IOSTATE_badbit;
    }
    return this;
}

static ostream* ostream_internal_print_integer(ostream *ostr, int n, BOOL unsig, BOOL shrt)
{
    ios *base = ostream_get_ios(ostr);
    char prefix_str[3] = {0}, number_str[12], sprintf_fmt[4] = {'%','d',0};

    TRACE("(%p %d %d %d)\n", ostr, n, unsig, shrt);

    if (ostream_opfx(ostr)) {
        if (base->flags & FLAGS_hex) {
            sprintf_fmt[1] = (base->flags & FLAGS_uppercase) ? 'X' : 'x';
            if (base->flags & FLAGS_showbase) {
                prefix_str[0] = '0';
                prefix_str[1] = (base->flags & FLAGS_uppercase) ? 'X' : 'x';
            }
        } else if (base->flags & FLAGS_oct) {
            sprintf_fmt[1]  = 'o';
            if (base->flags & FLAGS_showbase)
                prefix_str[0] = '0';
        } else { /* FLAGS_dec */
            if (unsig)
                sprintf_fmt[1] = 'u';
            if ((base->flags & FLAGS_showpos) && n != 0 && (unsig || n > 0))
                prefix_str[0] = '+';
        }

        if (shrt) {
            sprintf_fmt[2] = sprintf_fmt[1];
            sprintf_fmt[1] = 'h';
        }

        if (sprintf(number_str, sprintf_fmt, n) > 0)
            ostream_writepad(ostr, prefix_str, number_str);
        else
            base->state |= IOSTATE_failbit;
        ostream_osfx(ostr);
    }
    return ostr;
}

static ostream* ostream_internal_print_float(ostream *ostr, double d, BOOL dbl)
{
    ios *base = ostream_get_ios(ostr);
    char prefix_str[2] = {0}, number_str[24], sprintf_fmt[6] = {'%','.','*','f',0};
    int prec, max_prec = dbl ? 15 : 6;
    int str_length = 1; /* null end char */

    TRACE("(%p %lf %d)\n", ostr, d, dbl);

    if (ostream_opfx(ostr)) {
        if ((base->flags & FLAGS_showpos) && d > 0) {
            prefix_str[0] = '+';
            str_length++; /* plus sign */
        }
        if ((base->flags & (FLAGS_scientific|FLAGS_fixed)) == FLAGS_scientific)
            sprintf_fmt[3] = (base->flags & FLAGS_uppercase) ? 'E' : 'e';
        else if ((base->flags & (FLAGS_scientific|FLAGS_fixed)) != FLAGS_fixed)
            sprintf_fmt[3] = (base->flags & FLAGS_uppercase) ? 'G' : 'g';
        if (base->flags & FLAGS_showpoint) {
            sprintf_fmt[4] = sprintf_fmt[3];
            sprintf_fmt[3] = sprintf_fmt[2];
            sprintf_fmt[2] = sprintf_fmt[1];
            sprintf_fmt[1] = '#';
        }

        prec = (base->precision >= 0 && base->precision <= max_prec) ? base->precision : max_prec;
        str_length += _scprintf(sprintf_fmt, prec, d); /* number representation */
        if (str_length > 24) {
            /* when the output length exceeds 24 characters, Windows prints an empty string with padding */
            ostream_writepad(ostr, "", "");
        } else {
            if (sprintf(number_str, sprintf_fmt, prec, d) > 0)
                ostream_writepad(ostr, prefix_str, number_str);
            else
                base->state |= IOSTATE_failbit;
        }
        ostream_osfx(ostr);
    }
    return ostr;
}

/* ??6ostream@@QAEAAV0@C@Z */
/* ??6ostream@@QEAAAEAV0@C@Z */
/* ??6ostream@@QAEAAV0@D@Z */
/* ??6ostream@@QEAAAEAV0@D@Z */
/* ??6ostream@@QAEAAV0@E@Z */
/* ??6ostream@@QEAAAEAV0@E@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_char, 8)
ostream* __thiscall ostream_print_char(ostream *this, char c)
{
    const char c_str[2] = {c, 0};

    TRACE("(%p %c)\n", this, c);

    if (ostream_opfx(this)) {
        ostream_writepad(this, "", c_str);
        ostream_osfx(this);
    }
    return this;
}

/* ??6ostream@@QAEAAV0@PBC@Z */
/* ??6ostream@@QEAAAEAV0@PEBC@Z */
/* ??6ostream@@QAEAAV0@PBD@Z */
/* ??6ostream@@QEAAAEAV0@PEBD@Z */
/* ??6ostream@@QAEAAV0@PBE@Z */
/* ??6ostream@@QEAAAEAV0@PEBE@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_str, 8)
ostream* __thiscall ostream_print_str(ostream *this, const char *str)
{
    TRACE("(%p %s)\n", this, str);
    if (ostream_opfx(this)) {
        ostream_writepad(this, "", str);
        ostream_osfx(this);
    }
    return this;
}

/* ??6ostream@@QAEAAV0@F@Z */
/* ??6ostream@@QEAAAEAV0@F@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_short, 8)
ostream* __thiscall ostream_print_short(ostream *this, short n)
{
    return ostream_internal_print_integer(this, n, FALSE, TRUE);
}

/* ??6ostream@@QAEAAV0@G@Z */
/* ??6ostream@@QEAAAEAV0@G@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_unsigned_short, 8)
ostream* __thiscall ostream_print_unsigned_short(ostream *this, unsigned short n)
{
    return ostream_internal_print_integer(this, n, TRUE, TRUE);
}

/* ??6ostream@@QAEAAV0@H@Z */
/* ??6ostream@@QEAAAEAV0@H@Z */
/* ??6ostream@@QAEAAV0@J@Z */
/* ??6ostream@@QEAAAEAV0@J@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_int, 8)
ostream* __thiscall ostream_print_int(ostream *this, int n)
{
    return ostream_internal_print_integer(this, n, FALSE, FALSE);
}

/* ??6ostream@@QAEAAV0@I@Z */
/* ??6ostream@@QEAAAEAV0@I@Z */
/* ??6ostream@@QAEAAV0@K@Z */
/* ??6ostream@@QEAAAEAV0@K@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_unsigned_int, 8)
ostream* __thiscall ostream_print_unsigned_int(ostream *this, unsigned int n)
{
    return ostream_internal_print_integer(this, n, TRUE, FALSE);
}

/* ??6ostream@@QAEAAV0@M@Z */
/* ??6ostream@@QEAAAEAV0@M@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_float, 8)
ostream* __thiscall ostream_print_float(ostream *this, float f)
{
    return ostream_internal_print_float(this, f, FALSE);
}

/* ??6ostream@@QAEAAV0@N@Z */
/* ??6ostream@@QEAAAEAV0@N@Z */
/* ??6ostream@@QAEAAV0@O@Z */
/* ??6ostream@@QEAAAEAV0@O@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_double, 12)
ostream* __thiscall ostream_print_double(ostream *this, double d)
{
    return ostream_internal_print_float(this, d, TRUE);
}

/* ??6ostream@@QAEAAV0@PBX@Z */
/* ??6ostream@@QEAAAEAV0@PEBX@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_ptr, 8)
ostream* __thiscall ostream_print_ptr(ostream *this, const void *ptr)
{
    ios *base = ostream_get_ios(this);
    char prefix_str[3] = {'0','x',0}, pointer_str[17];

    TRACE("(%p %p)\n", this, ptr);

    if (ostream_opfx(this)) {
        if (ptr && base->flags & FLAGS_uppercase)
            prefix_str[1] = 'X';

        if (sprintf(pointer_str, "%p", ptr) > 0)
            ostream_writepad(this, prefix_str, pointer_str);
        else
            base->state |= IOSTATE_failbit;
        ostream_osfx(this);
    }
    return this;
}

/* ??6ostream@@QAEAAV0@PAVstreambuf@@@Z */
/* ??6ostream@@QEAAAEAV0@PEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_streambuf, 8)
ostream* __thiscall ostream_print_streambuf(ostream *this, streambuf *sb)
{
    ios *base = ostream_get_ios(this);
    int c;

    TRACE("(%p %p)\n", this, sb);

    if (ostream_opfx(this)) {
        while ((c = streambuf_sbumpc(sb)) != EOF) {
            if (streambuf_sputc(base->sb, c) == EOF) {
                base->state |= IOSTATE_failbit;
                break;
            }
        }
        ostream_osfx(this);
    }
    return this;
}

/* ??6ostream@@QAEAAV0@P6AAAV0@AAV0@@Z@Z */
/* ??6ostream@@QEAAAEAV0@P6AAEAV0@AEAV0@@Z@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_manip, 8)
ostream* __thiscall ostream_print_manip(ostream *this, ostream* (__cdecl *func)(ostream*))
{
    TRACE("(%p %p)\n", this, func);
    return func(this);
}

/* ??6ostream@@QAEAAV0@P6AAAVios@@AAV1@@Z@Z */
/* ??6ostream@@QEAAAEAV0@P6AAEAVios@@AEAV1@@Z@Z */
DEFINE_THISCALL_WRAPPER(ostream_print_ios_manip, 8)
ostream* __thiscall ostream_print_ios_manip(ostream *this, ios* (__cdecl *func)(ios*))
{
    TRACE("(%p %p)\n", this, func);
    func(ostream_get_ios(this));
    return this;
}

/* ?endl@@YAAAVostream@@AAV1@@Z */
/* ?endl@@YAAEAVostream@@AEAV1@@Z */
ostream* __cdecl ostream_endl(ostream *this)
{
   TRACE("(%p)\n", this);
   ostream_put(this, '\n');
   return ostream_flush(this);
}

/* ?ends@@YAAAVostream@@AAV1@@Z */
/* ?ends@@YAAEAVostream@@AEAV1@@Z */
ostream* __cdecl ostream_ends(ostream *this)
{
   TRACE("(%p)\n", this);
   return ostream_put(this, 0);
}

/* ?flush@@YAAAVostream@@AAV1@@Z */
/* ?flush@@YAAEAVostream@@AEAV1@@Z */
ostream* __cdecl ostream_flush_manip(ostream *this)
{
   TRACE("(%p)\n", this);
   return ostream_flush(this);
}

/* ??0ostream_withassign@@QAE@ABV0@@Z */
/* ??0ostream_withassign@@QEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(ostream_withassign_copy_ctor, 12)
ostream* __thiscall ostream_withassign_copy_ctor(ostream *this, const ostream *copy, BOOL virt_init)
{
    ios *base, *base_copy;

    TRACE("(%p %p %d)\n", this, copy, virt_init);

    base_copy = ostream_get_ios(copy);
    if (virt_init) {
        this->vbtable = ostream_vbtable;
        base = ostream_get_ios(this);
        ios_copy_ctor(base, base_copy);
    } else
        base = ostream_get_ios(this);
    ios_init(base, base_copy->sb);
    base->vtable = &MSVCP_ostream_withassign_vtable;
    this->unknown = 0;
    return this;
}

/* ??0ostream_withassign@@QAE@PAVstreambuf@@@Z */
/* ??0ostream_withassign@@QEAA@PEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(ostream_withassign_sb_ctor, 12)
ostream* __thiscall ostream_withassign_sb_ctor(ostream *this, streambuf *sb, BOOL virt_init)
{
    ios *base;

    TRACE("(%p %p %d)\n", this, sb, virt_init);

    ostream_sb_ctor(this, sb, virt_init);
    base = ostream_get_ios(this);
    base->vtable = &MSVCP_ostream_withassign_vtable;
    return this;
}

/* ??0ostream_withassign@@QAE@XZ */
/* ??0ostream_withassign@@QEAA@XZ */
DEFINE_THISCALL_WRAPPER(ostream_withassign_ctor, 8)
ostream* __thiscall ostream_withassign_ctor(ostream *this, BOOL virt_init)
{
    ios *base;

    TRACE("(%p %d)\n", this, virt_init);

    ostream_ctor(this, virt_init);
    base = ostream_get_ios(this);
    base->vtable = &MSVCP_ostream_withassign_vtable;
    return this;
}

static ostream* ostrstream_internal_sb_ctor(ostream *this, strstreambuf *ssb, BOOL virt_init)
{
    ios *base;

    if (ssb)
        ostream_sb_ctor(this, &ssb->base, virt_init);
    else
        ostream_ctor(this, virt_init);
    base = ostream_get_ios(this);
    base->vtable = &MSVCP_ostrstream_vtable;
    base->delbuf = 1;
    return this;
}

/* ??0ostrstream@@QAE@ABV0@@Z */
/* ??0ostrstream@@QEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(ostrstream_copy_ctor, 12)
ostream* __thiscall ostrstream_copy_ctor(ostream *this, const ostream *copy, BOOL virt_init)
{
    TRACE("(%p %p %d)\n", this, copy, virt_init);
    ostream_withassign_copy_ctor(this, copy, virt_init);
    ostream_get_ios(this)->vtable = &MSVCP_ostrstream_vtable;
    return this;
}

/* ??0ostrstream@@QAE@PADHH@Z */
/* ??0ostrstream@@QEAA@PEADHH@Z */
DEFINE_THISCALL_WRAPPER(ostrstream_buffer_ctor, 20)
ostream* __thiscall ostrstream_buffer_ctor(ostream *this, char *buffer, int length, int mode, BOOL virt_init)
{
    strstreambuf *ssb = MSVCRT_operator_new(sizeof(strstreambuf));

    TRACE("(%p %p %d %d %d)\n", this, buffer, length, mode, virt_init);

    if (ssb) {
        strstreambuf_buffer_ctor(ssb, buffer, length, buffer);
        if (mode & (OPENMODE_app|OPENMODE_ate))
            ssb->base.pptr = buffer + strlen(buffer);
    }
    return ostrstream_internal_sb_ctor(this, ssb, virt_init);
}

/* ??0ostrstream@@QAE@XZ */
/* ??0ostrstream@@QEAA@XZ */
DEFINE_THISCALL_WRAPPER(ostrstream_ctor, 8)
ostream* __thiscall ostrstream_ctor(ostream *this, BOOL virt_init)
{
    strstreambuf *ssb = MSVCRT_operator_new(sizeof(strstreambuf));

    TRACE("(%p %d)\n", this, virt_init);

    if (ssb)
        strstreambuf_ctor(ssb);
    return ostrstream_internal_sb_ctor(this, ssb, virt_init);
}

/* ?pcount@ostrstream@@QBEHXZ */
/* ?pcount@ostrstream@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(ostrstream_pcount, 4)
int __thiscall ostrstream_pcount(const ostream *this)
{
    return streambuf_out_waiting(ostream_get_ios(this)->sb);
}

/* ?rdbuf@ostrstream@@QBEPAVstrstreambuf@@XZ */
/* ?rdbuf@ostrstream@@QEBAPEAVstrstreambuf@@XZ */
DEFINE_THISCALL_WRAPPER(ostrstream_rdbuf, 4)
strstreambuf* __thiscall ostrstream_rdbuf(const ostream *this)
{
    return (strstreambuf*) ostream_get_ios(this)->sb;
}

/* ?str@ostrstream@@QAEPADXZ */
/* ?str@ostrstream@@QEAAPEADXZ */
DEFINE_THISCALL_WRAPPER(ostrstream_str, 4)
char* __thiscall ostrstream_str(ostream *this)
{
    return strstreambuf_str(ostrstream_rdbuf(this));
}

static inline ios* istream_get_ios(const istream *this)
{
    return (ios*)((char*)this + this->vbtable[1]);
}

static inline ios* istream_to_ios(const istream *this)
{
    return (ios*)((char*)this + istream_vbtable[1]);
}

static inline istream* ios_to_istream(const ios *base)
{
    return (istream*)((char*)base - istream_vbtable[1]);
}

/* ??0istream@@IAE@XZ */
/* ??0istream@@IEAA@XZ */
DEFINE_THISCALL_WRAPPER(istream_ctor, 8)
istream* __thiscall istream_ctor(istream *this, BOOL virt_init)
{
    ios *base;

    TRACE("(%p %d)\n", this, virt_init);

    if (virt_init) {
        this->vbtable = istream_vbtable;
        base = istream_get_ios(this);
        ios_ctor(base);
    } else
        base = istream_get_ios(this);
    base->vtable = &MSVCP_istream_vtable;
    base->flags |= FLAGS_skipws;
    this->extract_delim = 0;
    this->count = 0;
    return this;
}

/* ??0istream@@QAE@PAVstreambuf@@@Z */
/* ??0istream@@QEAA@PEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(istream_sb_ctor, 12)
istream* __thiscall istream_sb_ctor(istream *this, streambuf *sb, BOOL virt_init)
{
    TRACE("(%p %p %d)\n", this, sb, virt_init);
    istream_ctor(this, virt_init);
    ios_init(istream_get_ios(this), sb);
    return this;
}

/* ??0istream@@IAE@ABV0@@Z */
/* ??0istream@@IEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(istream_copy_ctor, 12)
istream* __thiscall istream_copy_ctor(istream *this, const istream *copy, BOOL virt_init)
{
    return istream_sb_ctor(this, istream_get_ios(copy)->sb, virt_init);
}

/* ??1istream@@UAE@XZ */
/* ??1istream@@UEAA@XZ */
/* ??1istream_withassign@@UAE@XZ */
/* ??1istream_withassign@@UEAA@XZ */
/* ??1istrstream@@UAE@XZ */
/* ??1istrstream@@UEAA@XZ */
DEFINE_THISCALL_WRAPPER(istream_dtor, 4)
void __thiscall istream_dtor(ios *base)
{
    istream *this = ios_to_istream(base);

    TRACE("(%p)\n", this);
}

/* ??4istream@@IAEAAV0@PAVstreambuf@@@Z */
/* ??4istream@@IEAAAEAV0@PEAVstreambuf@@@Z */
/* ??4istream_withassign@@QAEAAVistream@@PAVstreambuf@@@Z */
/* ??4istream_withassign@@QEAAAEAVistream@@PEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(istream_assign_sb, 8)
istream* __thiscall istream_assign_sb(istream *this, streambuf *sb)
{
    ios *base = istream_get_ios(this);

    TRACE("(%p %p)\n", this, sb);

    ios_init(base, sb);
    base->state &= IOSTATE_badbit;
    base->delbuf = 0;
    base->tie = NULL;
    base->flags = FLAGS_skipws;
    base->precision = 6;
    base->fill = ' ';
    base->width = 0;
    this->count = 0;
    return this;
}

/* ??4istream@@IAEAAV0@ABV0@@Z */
/* ??4istream@@IEAAAEAV0@AEBV0@@Z */
/* ??4istream_withassign@@QAEAAV0@ABV0@@Z */
/* ??4istream_withassign@@QEAAAEAV0@AEBV0@@Z */
/* ??4istream_withassign@@QAEAAVistream@@ABV1@@Z */
/* ??4istream_withassign@@QEAAAEAVistream@@AEBV1@@Z */
/* ??4istrstream@@QAEAAV0@ABV0@@Z */
/* ??4istrstream@@QEAAAEAV0@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(istream_assign, 8)
istream* __thiscall istream_assign(istream *this, const istream *rhs)
{
    return istream_assign_sb(this, istream_get_ios(rhs)->sb);
}

/* ??_Distream@@QAEXXZ */
/* ??_Distream@@QEAAXXZ */
/* ??_Distream_withassign@@QAEXXZ */
/* ??_Distream_withassign@@QEAAXXZ */
/* ??_Distrstream@@QAEXXZ */
/* ??_Distrstream@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(istream_vbase_dtor, 4)
void __thiscall istream_vbase_dtor(istream *this)
{
    ios *base = istream_to_ios(this);

    TRACE("(%p)\n", this);

    istream_dtor(base);
    ios_dtor(base);
}

/* ??_Eistream@@UAEPAXI@Z */
/* ??_Eistream_withassign@@UAEPAXI@Z */
/* ??_Eistrstream@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(istream_vector_dtor, 8)
istream* __thiscall istream_vector_dtor(ios *base, unsigned int flags)
{
    istream *this = ios_to_istream(base);

    TRACE("(%p %x)\n", this, flags);

    if (flags & 2) {
        /* we have an array, with the number of elements stored before the first object */
        INT_PTR i, *ptr = (INT_PTR *)this-1;

        for (i = *ptr-1; i >= 0; i--)
            istream_vbase_dtor(this+i);
        MSVCRT_operator_delete(ptr);
    } else {
        istream_vbase_dtor(this);
        if (flags & 1)
            MSVCRT_operator_delete(this);
    }
    return this;
}

/* ??_Gistream@@UAEPAXI@Z */
/* ??_Gistream_withassign@@UAEPAXI@Z */
/* ??_Gistrstream@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(istream_scalar_dtor, 8)
istream* __thiscall istream_scalar_dtor(ios *base, unsigned int flags)
{
    istream *this = ios_to_istream(base);

    TRACE("(%p %x)\n", this, flags);

    istream_vbase_dtor(this);
    if (flags & 1) MSVCRT_operator_delete(this);
    return this;
}

/* ?eatwhite@istream@@QAEXXZ */
/* ?eatwhite@istream@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(istream_eatwhite, 4)
void __thiscall istream_eatwhite(istream *this)
{
    ios *base = istream_get_ios(this);
    int c;

    TRACE("(%p)\n", this);

    ios_lockbuf(base);
    for (c = streambuf_sgetc(base->sb); isspace(c); c = streambuf_snextc(base->sb));
    ios_unlockbuf(base);
    if (c == EOF)
        ios_clear(base, base->state | IOSTATE_eofbit);
}

/* ?gcount@istream@@QBEHXZ */
/* ?gcount@istream@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(istream_gcount, 4)
int __thiscall istream_gcount(const istream *this)
{
    TRACE("(%p)\n", this);
    return this->count;
}

/* ?ipfx@istream@@QAEHH@Z */
/* ?ipfx@istream@@QEAAHH@Z */
DEFINE_THISCALL_WRAPPER(istream_ipfx, 8)
int __thiscall istream_ipfx(istream *this, int need)
{
    ios *base = istream_get_ios(this);

    TRACE("(%p %d)\n", this, need);

    if (need)
        this->count = 0;
    if (!ios_good(base)) {
        ios_clear(base, base->state | IOSTATE_failbit);
        return 0;
    }
    ios_lock(base);
    ios_lockbuf(base);
    if (base->tie && (!need || streambuf_in_avail(base->sb) < need))
        ostream_flush(base->tie);
    if ((base->flags & FLAGS_skipws) && !need) {
        istream_eatwhite(this);
        if (base->state & IOSTATE_eofbit) {
            base->state |= IOSTATE_failbit;
            ios_unlockbuf(base);
            ios_unlock(base);
            return 0;
        }
    }
    return 1;
}

/* ?isfx@istream@@QAEXXZ */
/* ?isfx@istream@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(istream_isfx, 4)
void __thiscall istream_isfx(istream *this)
{
    ios *base = istream_get_ios(this);

    TRACE("(%p)\n", this);

    ios_unlockbuf(base);
    ios_unlock(base);
}

/* ?get@istream@@IAEAAV1@PADHH@Z */
/* ?get@istream@@IEAAAEAV1@PEADHH@Z */
DEFINE_THISCALL_WRAPPER(istream_get_str_delim, 16)
istream* __thiscall istream_get_str_delim(istream *this, char *str, int count, int delim)
{
    ios *base = istream_get_ios(this);
    int ch, i = 0;

    TRACE("(%p %p %d %d)\n", this, str, count, delim);

    if (istream_ipfx(this, 1)) {
        while (i < count - 1) {
            if ((ch = streambuf_sgetc(base->sb)) == EOF) {
                base->state |= IOSTATE_eofbit;
                if (!i) /* tried to read, but not a single character was obtained */
                    base->state |= IOSTATE_failbit;
                break;
            }
            if (ch == delim) {
                if (this->extract_delim) { /* discard the delimiter */
                    streambuf_stossc(base->sb);
                    this->count++;
                }
                break;
            }
            if (str)
                str[i] = ch;
            streambuf_stossc(base->sb);
            i++;
        }
        this->count += i;
        istream_isfx(this);
    }
    if (str && count) /* append a null terminator, unless a string of 0 characters was requested */
        str[i] = 0;
    this->extract_delim = 0;
    return this;
}

/* ?get@istream@@QAEAAV1@PACHD@Z */
/* ?get@istream@@QEAAAEAV1@PEACHD@Z */
/* ?get@istream@@QAEAAV1@PADHD@Z */
/* ?get@istream@@QEAAAEAV1@PEADHD@Z */
/* ?get@istream@@QAEAAV1@PAEHD@Z */
/* ?get@istream@@QEAAAEAV1@PEAEHD@Z */
DEFINE_THISCALL_WRAPPER(istream_get_str, 16)
istream* __thiscall istream_get_str(istream *this, char *str, int count, char delim)
{
    return istream_get_str_delim(this, str, count, (unsigned char) delim);
}

static int istream_internal_get_char(istream *this, char *ch)
{
    ios *base = istream_get_ios(this);
    int ret = EOF;

    TRACE("(%p %p)\n", this, ch);

    if (istream_ipfx(this, 1)) {
        if ((ret = streambuf_sbumpc(base->sb)) != EOF) {
            this->count = 1;
        } else {
            base->state |= IOSTATE_eofbit;
            if (ch)
                base->state |= IOSTATE_failbit;
        }
        if (ch)
            *ch = ret;
        istream_isfx(this);
    }
    return ret;
}

/* ?get@istream@@QAEAAV1@AAC@Z */
/* ?get@istream@@QEAAAEAV1@AEAC@Z */
/* ?get@istream@@QAEAAV1@AAD@Z */
/* ?get@istream@@QEAAAEAV1@AEAD@Z */
/* ?get@istream@@QAEAAV1@AAE@Z */
/* ?get@istream@@QEAAAEAV1@AEAE@Z */
DEFINE_THISCALL_WRAPPER(istream_get_char, 8)
istream* __thiscall istream_get_char(istream *this, char *ch)
{
    istream_internal_get_char(this, ch);
    return this;
}

/* ?get@istream@@QAEHXZ */
/* ?get@istream@@QEAAHXZ */
DEFINE_THISCALL_WRAPPER(istream_get, 4)
int __thiscall istream_get(istream *this)
{
    return istream_internal_get_char(this, NULL);
}

/* ?get@istream@@QAEAAV1@AAVstreambuf@@D@Z */
/* ?get@istream@@QEAAAEAV1@AEAVstreambuf@@D@Z */
DEFINE_THISCALL_WRAPPER(istream_get_sb, 12)
istream* __thiscall istream_get_sb(istream *this, streambuf *sb, char delim)
{
    ios *base = istream_get_ios(this);
    int ch;

    TRACE("(%p %p %c)\n", this, sb, delim);

    if (istream_ipfx(this, 1)) {
        for (ch = streambuf_sgetc(base->sb); ch != delim; ch = streambuf_snextc(base->sb)) {
            if (ch == EOF) {
                base->state |= IOSTATE_eofbit;
                break;
            }
            if (streambuf_sputc(sb, ch) == EOF)
                base->state |= IOSTATE_failbit;
            this->count++;
        }
        istream_isfx(this);
    }
    return this;
}

/* ?getline@istream@@QAEAAV1@PACHD@Z */
/* ?getline@istream@@QEAAAEAV1@PEACHD@Z */
/* ?getline@istream@@QAEAAV1@PADHD@Z */
/* ?getline@istream@@QEAAAEAV1@PEADHD@Z */
/* ?getline@istream@@QAEAAV1@PAEHD@Z */
/* ?getline@istream@@QEAAAEAV1@PEAEHD@Z */
DEFINE_THISCALL_WRAPPER(istream_getline, 16)
istream* __thiscall istream_getline(istream *this, char *str, int count, char delim)
{
    ios *base = istream_get_ios(this);

    TRACE("(%p %p %d %c)\n", this, str, count, delim);

    ios_lock(base);
    this->extract_delim++;
    istream_get_str_delim(this, str, count, (unsigned char) delim);
    ios_unlock(base);
    return this;
}

/* ?ignore@istream@@QAEAAV1@HH@Z */
/* ?ignore@istream@@QEAAAEAV1@HH@Z */
DEFINE_THISCALL_WRAPPER(istream_ignore, 12)
istream* __thiscall istream_ignore(istream *this, int count, int delim)
{
    ios *base = istream_get_ios(this);

    TRACE("(%p %d %d)\n", this, count, delim);

    ios_lock(base);
    this->extract_delim++;
    istream_get_str_delim(this, NULL, count + 1, delim);
    ios_unlock(base);
    return this;
}

/* ?peek@istream@@QAEHXZ */
/* ?peek@istream@@QEAAHXZ */
DEFINE_THISCALL_WRAPPER(istream_peek, 4)
int __thiscall istream_peek(istream *this)
{
    ios *base = istream_get_ios(this);
    int ret = EOF;

    TRACE("(%p)\n", this);

    if (istream_ipfx(this, 1)) {
        ret = streambuf_sgetc(base->sb);
        istream_isfx(this);
    }
    return ret;
}

/* ?putback@istream@@QAEAAV1@D@Z */
/* ?putback@istream@@QEAAAEAV1@D@Z */
DEFINE_THISCALL_WRAPPER(istream_putback, 8)
istream* __thiscall istream_putback(istream *this, char ch)
{
    ios *base = istream_get_ios(this);

    TRACE("(%p %c)\n", this, ch);

    if (ios_good(base)) {
        ios_lockbuf(base);
        if (streambuf_sputbackc(base->sb, ch) == EOF)
            ios_clear(base, base->state | IOSTATE_failbit);
        ios_unlockbuf(base);
    }
    return this;
}

/* ?read@istream@@QAEAAV1@PACH@Z */
/* ?read@istream@@QEAAAEAV1@PEACH@Z */
/* ?read@istream@@QAEAAV1@PADH@Z */
/* ?read@istream@@QEAAAEAV1@PEADH@Z */
/* ?read@istream@@QAEAAV1@PAEH@Z */
/* ?read@istream@@QEAAAEAV1@PEAEH@Z */
DEFINE_THISCALL_WRAPPER(istream_read, 12)
istream* __thiscall istream_read(istream *this, char *str, int count)
{
    ios *base = istream_get_ios(this);

    TRACE("(%p %p %d)\n", this, str, count);

    if (istream_ipfx(this, 1)) {
        if ((this->count = streambuf_sgetn(base->sb, str, count)) != count)
            base->state = IOSTATE_eofbit | IOSTATE_failbit;
        istream_isfx(this);
    }
    return this;
}

/* ?seekg@istream@@QAEAAV1@J@Z */
/* ?seekg@istream@@QEAAAEAV1@J@Z */
DEFINE_THISCALL_WRAPPER(istream_seekg, 8)
istream* __thiscall istream_seekg(istream *this, streampos pos)
{
    ios *base = istream_get_ios(this);

    TRACE("(%p %d)\n", this, pos);

    ios_lockbuf(base);
    if (streambuf_seekpos(base->sb, pos, OPENMODE_in) == EOF)
        ios_clear(base, base->state | IOSTATE_failbit);
    ios_unlockbuf(base);
    return this;
}

/* ?seekg@istream@@QAEAAV1@JW4seek_dir@ios@@@Z */
/* ?seekg@istream@@QEAAAEAV1@JW4seek_dir@ios@@@Z */
DEFINE_THISCALL_WRAPPER(istream_seekg_offset, 12)
istream* __thiscall istream_seekg_offset(istream *this, streamoff off, ios_seek_dir dir)
{
    ios *base = istream_get_ios(this);

    TRACE("(%p %d %d)\n", this, off, dir);

    ios_lockbuf(base);
    if (call_streambuf_seekoff(base->sb, off, dir, OPENMODE_in) == EOF)
        ios_clear(base, base->state | IOSTATE_failbit);
    ios_unlockbuf(base);
    return this;
}

/* ?sync@istream@@QAEHXZ */
/* ?sync@istream@@QEAAHXZ */
DEFINE_THISCALL_WRAPPER(istream_sync, 4)
int __thiscall istream_sync(istream *this)
{
    ios *base = istream_get_ios(this);
    int ret;

    TRACE("(%p)\n", this);

    ios_lockbuf(base);
    if ((ret = call_streambuf_sync(base->sb)) == EOF)
        ios_clear(base, base->state | IOSTATE_badbit | IOSTATE_failbit);
    ios_unlockbuf(base);
    return ret;
}

/* ?tellg@istream@@QAEJXZ */
/* ?tellg@istream@@QEAAJXZ */
DEFINE_THISCALL_WRAPPER(istream_tellg, 4)
streampos __thiscall istream_tellg(istream *this)
{
    ios *base = istream_get_ios(this);
    streampos pos;

    TRACE("(%p)\n", this);

    ios_lockbuf(base);
    if ((pos = call_streambuf_seekoff(base->sb, 0, SEEKDIR_cur, OPENMODE_in)) == EOF)
        ios_clear(base, base->state | IOSTATE_failbit);
    ios_unlockbuf(base);
    return pos;
}

static int getint_is_valid_digit(char ch, int base)
{
    if (base == 8) return (ch >= '0' && ch <= '7');
    if (base == 16) return isxdigit(ch);
    return isdigit(ch);
}

/* ?getint@istream@@AAEHPAD@Z */
/* ?getint@istream@@AEAAHPEAD@Z */
DEFINE_THISCALL_WRAPPER(istream_getint, 8)
int __thiscall istream_getint(istream *this, char *str)
{
    ios *base = istream_get_ios(this);
    int ch, num_base = 0, i = 0;
    BOOL scan_sign = TRUE, scan_prefix = TRUE, scan_x = FALSE, valid_integer = FALSE;

    TRACE("(%p %p)\n", this, str);

    if (istream_ipfx(this, 0)) {
        num_base = (base->flags & FLAGS_dec) ? 10 :
            (base->flags & FLAGS_hex) ? 16 :
            (base->flags & FLAGS_oct) ? 8 : 0; /* 0 = autodetect */
        /* scan valid characters, up to 15 (hard limit on Windows) */
        for (ch = streambuf_sgetc(base->sb); i < 15; ch = streambuf_snextc(base->sb)) {
            if ((ch == '+' || ch == '-') && scan_sign) {
                /* no additional sign allowed */
                scan_sign = FALSE;
            } else if ((ch == 'x' || ch == 'X') && scan_x) {
                /* only hex digits can (and must) follow */
                scan_x = valid_integer = FALSE;
                num_base = 16;
            } else if (ch == '0' && scan_prefix) {
                /* might be the octal prefix, the beginning of the hex prefix or a decimal zero */
                scan_sign = scan_prefix = FALSE;
                scan_x = !num_base || num_base == 16;
                valid_integer = TRUE;
                if (!num_base)
                    num_base = 8;
            } else if (getint_is_valid_digit(ch, num_base)) {
                /* only digits in the corresponding base can follow */
                scan_sign = scan_prefix = scan_x = FALSE;
                valid_integer = TRUE;
            } else {
                /* unexpected character, stop scanning */
                if (!valid_integer) {
                    /* the result is not a valid integer */
                    base->state |= IOSTATE_failbit;
                    /* put any extracted character back into the stream */
                    while (i > 0)
                        if (streambuf_sputbackc(base->sb, str[--i]) == EOF)
                            base->state |= IOSTATE_badbit; /* characters have been lost for good */
                } else if (ch == EOF) {
                    base->state |= IOSTATE_eofbit;
                    if (scan_x && !(base->flags & ios_basefield)) {
                        /* when autodetecting, a single zero followed by EOF is regarded as decimal */
                        num_base = 0;
                    }
                }
                break;
            }
            str[i++] = ch;
        }
        /* append a null terminator */
        str[i] = 0;
        istream_isfx(this);
    }
    return num_base;
}

/* ?getdouble@istream@@AAEHPADH@Z */
/* ?getdouble@istream@@AEAAHPEADH@Z */
DEFINE_THISCALL_WRAPPER(istream_getdouble, 12)
int __thiscall istream_getdouble(istream *this, char *str, int count)
{
    ios *base = istream_get_ios(this);
    int ch, i = 0;
    BOOL scan_sign = TRUE, scan_dot = TRUE, scan_exp = TRUE,
        valid_mantissa = FALSE, valid_exponent = FALSE;

    TRACE("(%p %p %d)\n", this, str, count);

    if (istream_ipfx(this, 0)) {
        if (!count) {
            /* can't output anything */
            base->state |= IOSTATE_failbit;
            i = -1;
        } else {
            /* valid mantissas: +d. +.d +d.d (where d are sequences of digits and the sign is optional) */
            /* valid exponents: e+d E+d (where d are sequences of digits and the sign is optional) */
            for (ch = streambuf_sgetc(base->sb); i < count; ch = streambuf_snextc(base->sb)) {
                if ((ch == '+' || ch == '-') && scan_sign) {
                    /* no additional sign allowed */
                    scan_sign = FALSE;
                } else if (ch == '.' && scan_dot) {
                    /* no sign or additional dot allowed */
                    scan_sign = scan_dot = FALSE;
                } else if ((ch == 'e' || ch == 'E') && scan_exp) {
                    /* sign is allowed again but not dots or exponents */
                    scan_sign = TRUE;
                    scan_dot = scan_exp = FALSE;
                } else if (isdigit(ch)) {
                    if (scan_exp)
                        valid_mantissa = TRUE;
                    else
                        valid_exponent = TRUE;
                     /* no sign allowed after a digit */
                    scan_sign = FALSE;
                } else {
                    /* unexpected character, stop scanning */
                    /* check whether the result is a valid double */
                    if (!scan_exp && !valid_exponent) {
                        /* put the last character back into the stream, usually the 'e' or 'E' */
                        if (streambuf_sputbackc(base->sb, str[i--]) == EOF)
                            base->state |= IOSTATE_badbit; /* characters have been lost for good */
                    } else if (ch == EOF)
                        base->state |= IOSTATE_eofbit;
                    if (!valid_mantissa)
                        base->state |= IOSTATE_failbit;
                    break;
                }
                str[i++] = ch;
            }
            /* check if character limit has been reached */
            if (i == count) {
                base->state |= IOSTATE_failbit;
                i--;
            }
            /* append a null terminator */
            str[i] = 0;
        }
        istream_isfx(this);
    }
    return i;
}

/* ??5istream@@QAEAAV0@AAC@Z */
/* ??5istream@@QEAAAEAV0@AEAC@Z */
/* ??5istream@@QAEAAV0@AAD@Z */
/* ??5istream@@QEAAAEAV0@AEAD@Z */
/* ??5istream@@QAEAAV0@AAE@Z */
/* ??5istream@@QEAAAEAV0@AEAE@Z */
DEFINE_THISCALL_WRAPPER(istream_read_char, 8)
istream* __thiscall istream_read_char(istream *this, char *ch)
{
    ios *base = istream_get_ios(this);
    int ret;

    TRACE("(%p %p)\n", this, ch);

    if (istream_ipfx(this, 0)) {
        if ((ret = streambuf_sbumpc(base->sb)) == EOF)
            base->state |= IOSTATE_eofbit | IOSTATE_failbit;
        else
            *ch = ret;
        istream_isfx(this);
    }
    return this;
}

/* ??5istream@@QAEAAV0@PAC@Z */
/* ??5istream@@QEAAAEAV0@PEAC@Z */
/* ??5istream@@QAEAAV0@PAD@Z */
/* ??5istream@@QEAAAEAV0@PEAD@Z */
/* ??5istream@@QAEAAV0@PAE@Z */
/* ??5istream@@QEAAAEAV0@PEAE@Z */
DEFINE_THISCALL_WRAPPER(istream_read_str, 8)
istream* __thiscall istream_read_str(istream *this, char *str)
{
    ios *base = istream_get_ios(this);
    int ch, count = 0;

    TRACE("(%p %p)\n", this, str);

    if (istream_ipfx(this, 0)) {
        if (str) {
            for (ch = streambuf_sgetc(base->sb);
                count < (unsigned int) base->width - 1 && !isspace(ch);
                ch = streambuf_snextc(base->sb)) {
                if (ch == EOF) {
                    base->state |= IOSTATE_eofbit;
                    break;
                }
                str[count++] = ch;
            }
        }
        if (!count) /* nothing to output */
            base->state |= IOSTATE_failbit;
        else /* append a null terminator */
            str[count] = 0;
        base->width = 0;
        istream_isfx(this);
    }
    return this;
}

static LONG istream_internal_read_integer(istream *this, LONG min_value, LONG max_value, BOOL set_flag)
{
    ios *base = istream_get_ios(this);
    char buffer[16];
    int num_base;
    LONG ret;

    TRACE("(%p %d %d %d)\n", this, min_value, max_value, set_flag);

    num_base = istream_getint(this, buffer);
    errno = 0;
    ret = strtol(buffer, NULL, num_base);
    /* check for overflow and whether the value fits in the output var */
    if (set_flag && errno == ERANGE) {
        base->state |= IOSTATE_failbit;
    } else if (ret > max_value) {
        base->state |= IOSTATE_failbit;
        ret = max_value;
    } else if (ret < min_value) {
        base->state |= IOSTATE_failbit;
        ret = min_value;
    }
    return ret;
}

static ULONG istream_internal_read_unsigned_integer(istream *this, LONG min_value, ULONG max_value)
{
    ios *base = istream_get_ios(this);
    char buffer[16];
    int num_base;
    ULONG ret;

    TRACE("(%p %d %u)\n", this, min_value, max_value);

    num_base = istream_getint(this, buffer);
    errno = 0;
    ret = strtoul(buffer, NULL, num_base);
    /* check for overflow and whether the value fits in the output var */
    if ((ret == ULONG_MAX && errno == ERANGE) ||
        (ret > max_value && ret < (ULONG) min_value)) {
        base->state |= IOSTATE_failbit;
        ret = max_value;
    }
    return ret;
}

/* ??5istream@@QAEAAV0@AAF@Z */
/* ??5istream@@QEAAAEAV0@AEAF@Z */
DEFINE_THISCALL_WRAPPER(istream_read_short, 8)
istream* __thiscall istream_read_short(istream *this, short *p)
{
    if (istream_ipfx(this, 0)) {
        *p = istream_internal_read_integer(this, SHRT_MIN, SHRT_MAX, FALSE);
        istream_isfx(this);
    }
    return this;
}

/* ??5istream@@QAEAAV0@AAG@Z */
/* ??5istream@@QEAAAEAV0@AEAG@Z */
DEFINE_THISCALL_WRAPPER(istream_read_unsigned_short, 8)
istream* __thiscall istream_read_unsigned_short(istream *this, unsigned short *p)
{
    if (istream_ipfx(this, 0)) {
        *p = istream_internal_read_unsigned_integer(this, SHRT_MIN, USHRT_MAX);
        istream_isfx(this);
    }
    return this;
}

/* ??5istream@@QAEAAV0@AAH@Z */
/* ??5istream@@QEAAAEAV0@AEAH@Z */
DEFINE_THISCALL_WRAPPER(istream_read_int, 8)
istream* __thiscall istream_read_int(istream *this, int *p)
{
    if (istream_ipfx(this, 0)) {
        *p = istream_internal_read_integer(this, INT_MIN, INT_MAX, FALSE);
        istream_isfx(this);
    }
    return this;
}

/* ??5istream@@QAEAAV0@AAI@Z */
/* ??5istream@@QEAAAEAV0@AEAI@Z */
DEFINE_THISCALL_WRAPPER(istream_read_unsigned_int, 8)
istream* __thiscall istream_read_unsigned_int(istream *this, unsigned int *p)
{
    if (istream_ipfx(this, 0)) {
        *p = istream_internal_read_unsigned_integer(this, INT_MIN, UINT_MAX);
        istream_isfx(this);
    }
    return this;
}

/* ??5istream@@QAEAAV0@AAJ@Z */
/* ??5istream@@QEAAAEAV0@AEAJ@Z */
DEFINE_THISCALL_WRAPPER(istream_read_long, 8)
istream* __thiscall istream_read_long(istream *this, LONG *p)
{
    if (istream_ipfx(this, 0)) {
        *p = istream_internal_read_integer(this, LONG_MIN, LONG_MAX, TRUE);
        istream_isfx(this);
    }
    return this;
}

/* ??5istream@@QAEAAV0@AAK@Z */
/* ??5istream@@QEAAAEAV0@AEAK@Z */
DEFINE_THISCALL_WRAPPER(istream_read_unsigned_long, 8)
istream* __thiscall istream_read_unsigned_long(istream *this, ULONG *p)
{
    if (istream_ipfx(this, 0)) {
        *p = istream_internal_read_unsigned_integer(this, LONG_MIN, ULONG_MAX);
        istream_isfx(this);
    }
    return this;
}

static BOOL istream_internal_read_float(istream *this, int max_chars, double *out)
{
    char buffer[32];
    BOOL read = FALSE;

    TRACE("(%p %d %p)\n", this, max_chars, out);

    if (istream_ipfx(this, 0)) {
        /* character count is limited on Windows */
        if (istream_getdouble(this, buffer, max_chars) > 0) {
            *out = strtod(buffer, NULL);
            read = TRUE;
        }
        istream_isfx(this);
    }
    return read;
}

/* ??5istream@@QAEAAV0@AAM@Z */
/* ??5istream@@QEAAAEAV0@AEAM@Z */
DEFINE_THISCALL_WRAPPER(istream_read_float, 8)
istream* __thiscall istream_read_float(istream *this, float *f)
{
    double tmp;
    if (istream_internal_read_float(this, 20, &tmp)) {
        /* check whether the value fits in the output var */
        if (tmp > FLT_MAX)
            tmp = FLT_MAX;
        else if (tmp < -FLT_MAX)
            tmp = -FLT_MAX;
        else if (tmp > 0 && tmp < FLT_MIN)
            tmp = FLT_MIN;
        else if (tmp < 0 && tmp > -FLT_MIN)
            tmp = -FLT_MIN;
        *f = tmp;
    }
    return this;
}

/* ??5istream@@QAEAAV0@AAN@Z */
/* ??5istream@@QEAAAEAV0@AEAN@Z */
DEFINE_THISCALL_WRAPPER(istream_read_double, 8)
istream* __thiscall istream_read_double(istream *this, double *d)
{
    istream_internal_read_float(this, 28, d);
    return this;
}

/* ??5istream@@QAEAAV0@AAO@Z */
/* ??5istream@@QEAAAEAV0@AEAO@Z */
DEFINE_THISCALL_WRAPPER(istream_read_long_double, 8)
istream* __thiscall istream_read_long_double(istream *this, double *ld)
{
    istream_internal_read_float(this, 32, ld);
    return this;
}

/* ??5istream@@QAEAAV0@PAVstreambuf@@@Z */
/* ??5istream@@QEAAAEAV0@PEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(istream_read_streambuf, 8)
istream* __thiscall istream_read_streambuf(istream *this, streambuf *sb)
{
    ios *base = istream_get_ios(this);
    int ch;

    TRACE("(%p %p)\n", this, sb);

    if (istream_ipfx(this, 0)) {
        while ((ch = streambuf_sbumpc(base->sb)) != EOF)
            if (streambuf_sputc(sb, ch) == EOF)
                base->state |= IOSTATE_failbit;
        istream_isfx(this);
    }
    return this;
}

/* ??5istream@@QAEAAV0@P6AAAV0@AAV0@@Z@Z */
/* ??5istream@@QEAAAEAV0@P6AAEAV0@AEAV0@@Z@Z */
DEFINE_THISCALL_WRAPPER(istream_read_manip, 8)
istream* __thiscall istream_read_manip(istream *this, istream* (__cdecl *func)(istream*))
{
    TRACE("(%p %p)\n", this, func);
    return func(this);
}

/* ??5istream@@QAEAAV0@P6AAAVios@@AAV1@@Z@Z */
/* ??5istream@@QEAAAEAV0@P6AAEAVios@@AEAV1@@Z@Z */
DEFINE_THISCALL_WRAPPER(istream_read_ios_manip, 8)
istream* __thiscall istream_read_ios_manip(istream *this, ios* (__cdecl *func)(ios*))
{
    TRACE("(%p %p)\n", this, func);
    func(istream_get_ios(this));
    return this;
}

/* ?ws@@YAAAVistream@@AAV1@@Z */
/* ?ws@@YAAEAVistream@@AEAV1@@Z */
istream* __cdecl istream_ws(istream *this)
{
    TRACE("(%p)\n", this);
    istream_eatwhite(this);
    return this;
}

/* ??0istream_withassign@@QAE@ABV0@@Z */
/* ??0istream_withassign@@QEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(istream_withassign_copy_ctor, 12)
istream* __thiscall istream_withassign_copy_ctor(istream *this, const istream *copy, BOOL virt_init)
{
    ios *base, *base_copy;

    TRACE("(%p %p %d)\n", this, copy, virt_init);

    base_copy = istream_get_ios(copy);
    if (virt_init) {
        this->vbtable = istream_vbtable;
        base = istream_get_ios(this);
        ios_copy_ctor(base, base_copy);
    } else
        base = istream_get_ios(this);
    ios_init(base, base_copy->sb);
    base->vtable = &MSVCP_istream_withassign_vtable;
    base->flags |= FLAGS_skipws;
    this->extract_delim = 0;
    this->count = 0;
    return this;
}

/* ??0istream_withassign@@QAE@PAVstreambuf@@@Z */
/* ??0istream_withassign@@QEAA@PEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(istream_withassign_sb_ctor, 12)
istream* __thiscall istream_withassign_sb_ctor(istream *this, streambuf *sb, BOOL virt_init)
{
    ios *base;

    TRACE("(%p %p %d)\n", this, sb, virt_init);

    istream_sb_ctor(this, sb, virt_init);
    base = istream_get_ios(this);
    base->vtable = &MSVCP_istream_withassign_vtable;
    return this;
}

/* ??0istream_withassign@@QAE@XZ */
/* ??0istream_withassign@@QEAA@XZ */
DEFINE_THISCALL_WRAPPER(istream_withassign_ctor, 8)
istream* __thiscall istream_withassign_ctor(istream *this, BOOL virt_init)
{
    ios *base;

    TRACE("(%p %d)\n", this, virt_init);

    istream_ctor(this, virt_init);
    base = istream_get_ios(this);
    base->vtable = &MSVCP_istream_withassign_vtable;
    return this;
}

/* ??0istrstream@@QAE@ABV0@@Z */
/* ??0istrstream@@QEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(istrstream_copy_ctor, 12)
istream* __thiscall istrstream_copy_ctor(istream *this, const istream *copy, BOOL virt_init)
{
    TRACE("(%p %p %d)\n", this, copy, virt_init);
    istream_withassign_copy_ctor(this, copy, virt_init);
    istream_get_ios(this)->vtable = &MSVCP_istrstream_vtable;
    return this;
}

/* ??0istrstream@@QAE@PADH@Z */
/* ??0istrstream@@QEAA@PEADH@Z */
DEFINE_THISCALL_WRAPPER(istrstream_buffer_ctor, 16)
istream* __thiscall istrstream_buffer_ctor(istream *this, char *buffer, int length, BOOL virt_init)
{
    ios *base;
    strstreambuf *ssb = MSVCRT_operator_new(sizeof(strstreambuf));

    TRACE("(%p %p %d %d)\n", this, buffer, length, virt_init);

    if (ssb) {
        strstreambuf_buffer_ctor(ssb, buffer, length, NULL);
        istream_sb_ctor(this, &ssb->base, virt_init);
    } else
        istream_ctor(this, virt_init);
    base = istream_get_ios(this);
    base->vtable = &MSVCP_istrstream_vtable;
    base->delbuf = 1;
    return this;
}

/* ??0istrstream@@QAE@PAD@Z */
/* ??0istrstream@@QEAA@PEAD@Z */
DEFINE_THISCALL_WRAPPER(istrstream_str_ctor, 12)
istream* __thiscall istrstream_str_ctor(istream *this, char *str, BOOL virt_init)
{
    return istrstream_buffer_ctor(this, str, 0, virt_init);
}

/* ?rdbuf@istrstream@@QBEPAVstrstreambuf@@XZ */
/* ?rdbuf@istrstream@@QEBAPEAVstrstreambuf@@XZ */
DEFINE_THISCALL_WRAPPER(istrstream_rdbuf, 4)
strstreambuf* __thiscall istrstream_rdbuf(const istream *this)
{
    return (strstreambuf*) istream_get_ios(this)->sb;
}

/* ?str@istrstream@@QAEPADXZ */
/* ?str@istrstream@@QEAAPEADXZ */
DEFINE_THISCALL_WRAPPER(istrstream_str, 4)
char* __thiscall istrstream_str(istream *this)
{
    return strstreambuf_str(istrstream_rdbuf(this));
}

static inline ios* iostream_to_ios(const iostream *this)
{
    return (ios*)((char*)this + iostream_vbtable_istream[1]);
}

static inline iostream* ios_to_iostream(const ios *base)
{
    return (iostream*)((char*)base - iostream_vbtable_istream[1]);
}

/* ??0iostream@@IAE@XZ */
/* ??0iostream@@IEAA@XZ */
DEFINE_THISCALL_WRAPPER(iostream_ctor, 8)
iostream* __thiscall iostream_ctor(iostream *this, BOOL virt_init)
{
    ios *base;

    TRACE("(%p %d)\n", this, virt_init);

    if (virt_init) {
        this->base1.vbtable = iostream_vbtable_istream;
        this->base2.vbtable = iostream_vbtable_ostream;
        base = istream_get_ios(&this->base1);
        ios_ctor(base);
    } else
        base = istream_get_ios(&this->base1);
    istream_ctor(&this->base1, FALSE);
    ostream_ctor(&this->base2, FALSE);
    base->vtable = &MSVCP_iostream_vtable;
    return this;
}

/* ??0iostream@@QAE@PAVstreambuf@@@Z */
/* ??0iostream@@QEAA@PEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(iostream_sb_ctor, 12)
iostream* __thiscall iostream_sb_ctor(iostream *this, streambuf *sb, BOOL virt_init)
{
    TRACE("(%p %p %d)\n", this, sb, virt_init);
    iostream_ctor(this, virt_init);
    ios_init(istream_get_ios(&this->base1), sb);
    return this;
}

/* ??0iostream@@IAE@ABV0@@Z */
/* ??0iostream@@IEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(iostream_copy_ctor, 12)
iostream* __thiscall iostream_copy_ctor(iostream *this, const iostream *copy, BOOL virt_init)
{
    return iostream_sb_ctor(this, istream_get_ios(&copy->base1)->sb, virt_init);
}

/* ??1iostream@@UAE@XZ */
/* ??1iostream@@UEAA@XZ */
/* ??1stdiostream@@UAE@XZ */
/* ??1stdiostream@@UEAA@XZ */
/* ??1strstream@@UAE@XZ */
/* ??1strstream@@UEAA@XZ */
DEFINE_THISCALL_WRAPPER(iostream_dtor, 4)
void __thiscall iostream_dtor(ios *base)
{
    iostream *this = ios_to_iostream(base);

    TRACE("(%p)\n", this);

    ostream_dtor(ostream_to_ios(&this->base2));
    istream_dtor(istream_to_ios(&this->base1));
}

/* ??4iostream@@IAEAAV0@PAVstreambuf@@@Z */
/* ??4iostream@@IEAAAEAV0@PEAVstreambuf@@@Z */
DEFINE_THISCALL_WRAPPER(iostream_assign_sb, 8)
iostream* __thiscall iostream_assign_sb(iostream *this, streambuf *sb)
{
    TRACE("(%p %p)\n", this, sb);
    this->base1.count = 0;
    ostream_assign_sb(&this->base2, sb);
    return this;
}

/* ??4iostream@@IAEAAV0@AAV0@@Z */
/* ??4iostream@@IEAAAEAV0@AEAV0@@Z */
/* ??4stdiostream@@QAEAAV0@AAV0@@Z */
/* ??4stdiostream@@QEAAAEAV0@AEAV0@@Z */
/* ??4strstream@@QAEAAV0@ABV0@@Z */
/* ??4strstream@@QEAAAEAV0@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(iostream_assign, 8)
iostream* __thiscall iostream_assign(iostream *this, const iostream *rhs)
{
    return iostream_assign_sb(this, istream_get_ios(&rhs->base1)->sb);
}

/* ??_Diostream@@QAEXXZ */
/* ??_Diostream@@QEAAXXZ */
/* ??_Dstdiostream@@QAEXXZ */
/* ??_Dstdiostream@@QEAAXXZ */
/* ??_Dstrstream@@QAEXXZ */
/* ??_Dstrstream@@QEAAXXZ */
DEFINE_THISCALL_WRAPPER(iostream_vbase_dtor, 4)
void __thiscall iostream_vbase_dtor(iostream *this)
{
    ios *base = iostream_to_ios(this);

    TRACE("(%p)\n", this);

    iostream_dtor(base);
    ios_dtor(base);
}

/* ??_Eiostream@@UAEPAXI@Z */
/* ??_Estdiostream@@UAEPAXI@Z */
/* ??_Estrstream@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(iostream_vector_dtor, 8)
iostream* __thiscall iostream_vector_dtor(ios *base, unsigned int flags)
{
    iostream *this = ios_to_iostream(base);

    TRACE("(%p %x)\n", this, flags);

    if (flags & 2) {
        /* we have an array, with the number of elements stored before the first object */
        INT_PTR i, *ptr = (INT_PTR *)this-1;

        for (i = *ptr-1; i >= 0; i--)
            iostream_vbase_dtor(this+i);
        MSVCRT_operator_delete(ptr);
    } else {
        iostream_vbase_dtor(this);
        if (flags & 1)
            MSVCRT_operator_delete(this);
    }
    return this;
}

/* ??_Giostream@@UAEPAXI@Z */
/* ??_Gstdiostream@@UAEPAXI@Z */
/* ??_Gstrstream@@UAEPAXI@Z */
DEFINE_THISCALL_WRAPPER(iostream_scalar_dtor, 8)
iostream* __thiscall iostream_scalar_dtor(ios *base, unsigned int flags)
{
    iostream *this = ios_to_iostream(base);

    TRACE("(%p %x)\n", this, flags);

    iostream_vbase_dtor(this);
    if (flags & 1) MSVCRT_operator_delete(this);
    return this;
}

static iostream* iostream_internal_copy_ctor(iostream *this, const iostream *copy, const vtable_ptr *vtbl, BOOL virt_init)
{
    ios *base, *base_copy = istream_get_ios(&copy->base1);

    if (virt_init) {
        this->base1.vbtable = iostream_vbtable_istream;
        this->base2.vbtable = iostream_vbtable_ostream;
        base = istream_get_ios(&this->base1);
        ios_copy_ctor(base, base_copy);
    } else
        base = istream_get_ios(&this->base1);
    ios_init(base, base_copy->sb);
    istream_ctor(&this->base1, FALSE);
    ostream_ctor(&this->base2, FALSE);
    base->vtable = vtbl;
    return this;
}

static iostream* iostream_internal_sb_ctor(iostream *this, streambuf *sb, const vtable_ptr *vtbl, BOOL virt_init)
{
    ios *base;

    iostream_ctor(this, virt_init);
    base = istream_get_ios(&this->base1);
    if (sb)
        ios_init(base, sb);
    base->vtable = vtbl;
    base->delbuf = 1;
    return this;
}

/* ??0strstream@@QAE@ABV0@@Z */
/* ??0strstream@@QEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(strstream_copy_ctor, 12)
iostream* __thiscall strstream_copy_ctor(iostream *this, const iostream *copy, BOOL virt_init)
{
    TRACE("(%p %p %d)\n", this, copy, virt_init);
    return iostream_internal_copy_ctor(this, copy, &MSVCP_strstream_vtable, virt_init);
}

/* ??0strstream@@QAE@PADHH@Z */
/* ??0strstream@@QEAA@PEADHH@Z */
DEFINE_THISCALL_WRAPPER(strstream_buffer_ctor, 20)
iostream* __thiscall strstream_buffer_ctor(iostream *this, char *buffer, int length, int mode, BOOL virt_init)
{
    strstreambuf *ssb = MSVCRT_operator_new(sizeof(strstreambuf));

    TRACE("(%p %p %d %d %d)\n", this, buffer, length, mode, virt_init);

    if (ssb) {
        strstreambuf_buffer_ctor(ssb, buffer, length, buffer);
        if ((mode & OPENMODE_out) && (mode & (OPENMODE_app|OPENMODE_ate)))
            ssb->base.pptr = buffer + strlen(buffer);
        return iostream_internal_sb_ctor(this, &ssb->base, &MSVCP_strstream_vtable, virt_init);
    }
    return iostream_internal_sb_ctor(this, NULL, &MSVCP_strstream_vtable, virt_init);
}

/* ??0strstream@@QAE@XZ */
/* ??0strstream@@QEAA@XZ */
DEFINE_THISCALL_WRAPPER(strstream_ctor, 8)
iostream* __thiscall strstream_ctor(iostream *this, BOOL virt_init)
{
    strstreambuf *ssb = MSVCRT_operator_new(sizeof(strstreambuf));

    TRACE("(%p %d)\n", this, virt_init);

    if (ssb) {
        strstreambuf_ctor(ssb);
        return iostream_internal_sb_ctor(this, &ssb->base, &MSVCP_strstream_vtable, virt_init);
    }
    return iostream_internal_sb_ctor(this, NULL, &MSVCP_strstream_vtable, virt_init);
}

/* ?pcount@strstream@@QBEHXZ */
/* ?pcount@strstream@@QEBAHXZ */
DEFINE_THISCALL_WRAPPER(strstream_pcount, 4)
int __thiscall strstream_pcount(const iostream *this)
{
    return streambuf_out_waiting(istream_get_ios(&this->base1)->sb);
}

/* ?rdbuf@strstream@@QBEPAVstrstreambuf@@XZ */
/* ?rdbuf@strstream@@QEBAPEAVstrstreambuf@@XZ */
DEFINE_THISCALL_WRAPPER(strstream_rdbuf, 4)
strstreambuf* __thiscall strstream_rdbuf(const iostream *this)
{
    return (strstreambuf*) istream_get_ios(&this->base1)->sb;
}

/* ?str@strstream@@QAEPADXZ */
/* ?str@strstream@@QEAAPEADXZ */
DEFINE_THISCALL_WRAPPER(strstream_str, 4)
char* __thiscall strstream_str(iostream *this)
{
    return strstreambuf_str(strstream_rdbuf(this));
}

/* ??0stdiostream@@QAE@ABV0@@Z */
/* ??0stdiostream@@QEAA@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(stdiostream_copy_ctor, 12)
iostream* __thiscall stdiostream_copy_ctor(iostream *this, const iostream *copy, BOOL virt_init)
{
    TRACE("(%p %p %d)\n", this, copy, virt_init);
    return iostream_internal_copy_ctor(this, copy, &MSVCP_stdiostream_vtable, virt_init);
}

/* ??0stdiostream@@QAE@PAU_iobuf@@@Z */
/* ??0stdiostream@@QEAA@PEAU_iobuf@@@Z */
DEFINE_THISCALL_WRAPPER(stdiostream_file_ctor, 12)
iostream* __thiscall stdiostream_file_ctor(iostream *this, FILE *file, BOOL virt_init)
{
    stdiobuf *stb = MSVCRT_operator_new(sizeof(stdiobuf));

    TRACE("(%p %p %d)\n", this, file, virt_init);

    if (stb) {
        stdiobuf_file_ctor(stb, file);
        return iostream_internal_sb_ctor(this, &stb->base, &MSVCP_stdiostream_vtable, virt_init);
    }
    return iostream_internal_sb_ctor(this, NULL, &MSVCP_stdiostream_vtable, virt_init);
}

/* ?rdbuf@stdiostream@@QBEPAVstdiobuf@@XZ */
/* ?rdbuf@stdiostream@@QEBAPEAVstdiobuf@@XZ */
DEFINE_THISCALL_WRAPPER(stdiostream_rdbuf, 4)
stdiobuf* __thiscall stdiostream_rdbuf(const iostream *this)
{
    return (stdiobuf*) istream_get_ios(&this->base1)->sb;
}

/* ??0Iostream_init@@QAE@AAVios@@H@Z */
/* ??0Iostream_init@@QEAA@AEAVios@@H@Z */
DEFINE_THISCALL_WRAPPER(Iostream_init_ios_ctor, 12)
void* __thiscall Iostream_init_ios_ctor(void *this, ios *obj, int n)
{
    TRACE("(%p %p %d)\n", this, obj, n);
    obj->delbuf = 1;
    if (n >= 0) {
        obj->tie = &cout.os;
        if (n > 0)
            ios_setf(obj, FLAGS_unitbuf);
    }
    return this;
}

/* ??0Iostream_init@@QAE@XZ */
/* ??0Iostream_init@@QEAA@XZ */
DEFINE_THISCALL_WRAPPER(Iostream_init_ctor, 4)
void* __thiscall Iostream_init_ctor(void *this)
{
    TRACE("(%p)\n", this);
    return this;
}

/* ??1Iostream_init@@QAE@XZ */
/* ??1Iostream_init@@QEAA@XZ */
DEFINE_THISCALL_WRAPPER(Iostream_init_dtor, 4)
void __thiscall Iostream_init_dtor(void *this)
{
    TRACE("(%p)\n", this);
}

/* ??4Iostream_init@@QAEAAV0@ABV0@@Z */
/* ??4Iostream_init@@QEAAAEAV0@AEBV0@@Z */
DEFINE_THISCALL_WRAPPER(Iostream_init_assign, 8)
void* __thiscall Iostream_init_assign(void *this, const void *rhs)
{
    TRACE("(%p %p)\n", this, rhs);
    return this;
}

/* ?sync_with_stdio@ios@@SAXXZ */
void __cdecl ios_sync_with_stdio(void)
{
    if (!ios_sunk_with_stdio) {
        stdiobuf *new_buf;

        TRACE("()\n");

        /* run at most once */
        ios_sunk_with_stdio++;

         /* calls to [io]stream_assign_sb automatically destroy the old buffers */
        if ((new_buf = MSVCRT_operator_new(sizeof(stdiobuf)))) {
            stdiobuf_file_ctor(new_buf, stdin);
            istream_assign_sb(&cin.is, &new_buf->base);
        } else
            istream_assign_sb(&cin.is, NULL);
        cin.vbase.delbuf = 1;
        ios_setf(&cin.vbase, FLAGS_stdio);

        if ((new_buf = MSVCRT_operator_new(sizeof(stdiobuf)))) {
            stdiobuf_file_ctor(new_buf, stdout);
            stdiobuf_setrwbuf(new_buf, 0, 80);
            ostream_assign_sb(&cout.os, &new_buf->base);
        } else
            ostream_assign_sb(&cout.os, NULL);
        cout.vbase.delbuf = 1;
        ios_setf(&cout.vbase, FLAGS_unitbuf | FLAGS_stdio);

        if ((new_buf = MSVCRT_operator_new(sizeof(stdiobuf)))) {
            stdiobuf_file_ctor(new_buf, stderr);
            stdiobuf_setrwbuf(new_buf, 0, 80);
            ostream_assign_sb(&cerr.os, &new_buf->base);
        } else
            ostream_assign_sb(&cerr.os, NULL);
        cerr.vbase.delbuf = 1;
        ios_setf(&cerr.vbase, FLAGS_unitbuf | FLAGS_stdio);

        if ((new_buf = MSVCRT_operator_new(sizeof(stdiobuf)))) {
            stdiobuf_file_ctor(new_buf, stderr);
            stdiobuf_setrwbuf(new_buf, 0, 512);
            ostream_assign_sb(&clog.os, &new_buf->base);
        } else
            ostream_assign_sb(&clog.os, NULL);
        clog.vbase.delbuf = 1;
        ios_setf(&clog.vbase, FLAGS_stdio);
    }
}


#ifdef __i386__

#define DEFINE_VTBL_WRAPPER(off)            \
    __ASM_GLOBAL_FUNC(vtbl_wrapper_ ## off, \
        "popl %eax\n\t"                     \
        "popl %ecx\n\t"                     \
        "pushl %eax\n\t"                    \
        "movl 0(%ecx), %eax\n\t"            \
        "jmp *" #off "(%eax)\n\t")

DEFINE_VTBL_WRAPPER(0);
DEFINE_VTBL_WRAPPER(4);
DEFINE_VTBL_WRAPPER(8);
DEFINE_VTBL_WRAPPER(12);
DEFINE_VTBL_WRAPPER(16);
DEFINE_VTBL_WRAPPER(20);
DEFINE_VTBL_WRAPPER(24);
DEFINE_VTBL_WRAPPER(28);
DEFINE_VTBL_WRAPPER(32);
DEFINE_VTBL_WRAPPER(36);
DEFINE_VTBL_WRAPPER(40);
DEFINE_VTBL_WRAPPER(44);
DEFINE_VTBL_WRAPPER(48);
DEFINE_VTBL_WRAPPER(52);
DEFINE_VTBL_WRAPPER(56);

#endif

void* (__cdecl *MSVCRT_operator_new)(SIZE_T);
void (__cdecl *MSVCRT_operator_delete)(void*);

static void init_cxx_funcs(void)
{
    HMODULE hmod = GetModuleHandleA("msvcrt.dll");

    if (sizeof(void *) > sizeof(int))  /* 64-bit has different names */
    {
        MSVCRT_operator_new = (void*)GetProcAddress(hmod, "??2@YAPEAX_K@Z");
        MSVCRT_operator_delete = (void*)GetProcAddress(hmod, "??3@YAXPEAX@Z");
    }
    else
    {
        MSVCRT_operator_new = (void*)GetProcAddress(hmod, "??2@YAPAXI@Z");
        MSVCRT_operator_delete = (void*)GetProcAddress(hmod, "??3@YAXPAX@Z");
    }
}

static void init_io(void *base)
{
    filebuf *fb;

#ifdef __x86_64__
    init_streambuf_rtti(base);
    init_filebuf_rtti(base);
    init_strstreambuf_rtti(base);
    init_stdiobuf_rtti(base);
    init_ios_rtti(base);
    init_ostream_rtti(base);
    init_ostream_withassign_rtti(base);
    init_ostrstream_rtti(base);
    init_istream_rtti(base);
    init_istream_withassign_rtti(base);
    init_istrstream_rtti(base);
    init_iostream_rtti(base);
    init_strstream_rtti(base);
    init_stdiostream_rtti(base);
#endif

    if ((fb = MSVCRT_operator_new(sizeof(filebuf)))) {
        filebuf_fd_ctor(fb, 0);
        istream_withassign_sb_ctor(&cin.is, &fb->base, TRUE);
    } else
        istream_withassign_sb_ctor(&cin.is, NULL, TRUE);
    Iostream_init_ios_ctor(NULL, &cin.vbase, 0);

    if ((fb = MSVCRT_operator_new(sizeof(filebuf)))) {
        filebuf_fd_ctor(fb, 1);
        ostream_withassign_sb_ctor(&cout.os, &fb->base, TRUE);
    } else
        ostream_withassign_sb_ctor(&cout.os, NULL, TRUE);
    Iostream_init_ios_ctor(NULL, &cout.vbase, -1);

    if ((fb = MSVCRT_operator_new(sizeof(filebuf)))) {
        filebuf_fd_ctor(fb, 2);
        ostream_withassign_sb_ctor(&cerr.os, &fb->base, TRUE);
    } else
        ostream_withassign_sb_ctor(&cerr.os, NULL, TRUE);
    Iostream_init_ios_ctor(NULL, &cerr.vbase, 1);

    if ((fb = MSVCRT_operator_new(sizeof(filebuf)))) {
        filebuf_fd_ctor(fb, 2);
        ostream_withassign_sb_ctor(&clog.os, &fb->base, TRUE);
    } else
        ostream_withassign_sb_ctor(&clog.os, NULL, TRUE);
    Iostream_init_ios_ctor(NULL, &clog.vbase, 0);
}

static void free_io(void)
{
    /* destructors take care of deleting the buffers */
    istream_vbase_dtor(&cin.is);
    ostream_vbase_dtor(&cout.os);
    ostream_vbase_dtor(&cerr.os);
    ostream_vbase_dtor(&clog.os);
}

BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved )
{
   switch (reason)
   {
   case DLL_WINE_PREATTACH:
       return FALSE;  /* prefer native version */
   case DLL_PROCESS_ATTACH:
       init_cxx_funcs();
       init_exception(inst);
       init_io(inst);
       DisableThreadLibraryCalls( inst );
       break;
   case DLL_PROCESS_DETACH:
       if (reserved) break;
       free_io();
       break;
   }
   return TRUE;
}
