int Ebml_WriteWebMSeekInfo(EbmlGlobal *ebml)
{
    int rc = VINF_SUCCESS;

    /* Save the current file pointer */
    uint64_t pos = RTFileTell(ebml->file);

    if (ebml->seek_info_pos)
        rc = RTFileSeek(ebml->file, ebml->seek_info_pos, RTFILE_SEEK_BEGIN, NULL);
    else
        ebml->seek_info_pos = pos;

    {
        uint64_t start;

        if (RT_SUCCESS(rc))
            rc = ebml_StartSubElement(ebml, &start, SeekHead);
        if (RT_SUCCESS(rc))
            rc = Ebml_WriteWebMSeekElement(ebml, Tracks, ebml->track_pos);
        if (RT_SUCCESS(rc))
            rc = Ebml_WriteWebMSeekElement(ebml, Cues, ebml->cue_pos);
        if (RT_SUCCESS(rc))
            rc = Ebml_WriteWebMSeekElement(ebml, Info, ebml->segment_info_pos);
        if (RT_SUCCESS(rc))
            rc = ebml_EndSubElement(ebml, start);
    }
    {
        //segment info
        uint64_t startInfo;
        uint64_t frame_time;

        frame_time = (uint64_t)1000 * ebml->framerate.den / ebml->framerate.num;
        ebml->segment_info_pos = RTFileTell(ebml->file);
        if (RT_SUCCESS(rc))
            rc = ebml_StartSubElement(ebml, &startInfo, Info);
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000);
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeFloat(ebml, Segment_Duration,
                                     (double)(ebml->last_pts_ms + frame_time));
        char szVersion[64];
        RTStrPrintf(szVersion, sizeof(szVersion), "vpxenc%",
                    ebml->debug ? vpx_codec_version_str() : "");
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeString(ebml, MuxingApp, szVersion);
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeString(ebml, WritingApp, szVersion);
        if (RT_SUCCESS(rc))
            rc = ebml_EndSubElement(ebml, startInfo);
    }
    return rc;
}
int Ebml_WriteWebMFileFooter(EbmlGlobal *glob, long hash)
{
    int rc = VINF_SUCCESS;
    if (glob->cluster_open)
        rc = ebml_EndSubElement(glob, glob->startCluster);

    {
        uint64_t start;

        glob->cue_pos = RTFileTell(glob->file);
        if (RT_SUCCESS(rc))
            rc = ebml_StartSubElement(glob, &start, Cues);
        for (unsigned i = 0;  i < glob->cues; i++)
        {
            struct cue_entry *cue = &glob->cue_list[i];
            uint64_t startSub;

            if (RT_SUCCESS(rc))
                rc = ebml_StartSubElement(glob, &startSub, CuePoint);
            {
                uint64_t startSubsub;

                if (RT_SUCCESS(rc))
                    rc = Ebml_SerializeUnsigned(glob, CueTime, cue->time);
                if (RT_SUCCESS(rc))
                    rc = ebml_StartSubElement(glob, &startSubsub, CueTrackPositions);
                if (RT_SUCCESS(rc))
                    rc = Ebml_SerializeUnsigned(glob, CueTrack, 1);
                if (RT_SUCCESS(rc))
                    rc = Ebml_SerializeUnsigned64(glob, CueClusterPosition,
                                                  cue->loc - glob->position_reference);
                //Ebml_SerializeUnsigned(glob, CueBlockNumber, cue->blockNumber);
                if (RT_SUCCESS(rc))
                    rc = ebml_EndSubElement(glob, startSubsub);
            }
            if (RT_SUCCESS(rc))
                rc = ebml_EndSubElement(glob, startSub);
        }
        if (RT_SUCCESS(rc))
            rc = ebml_EndSubElement(glob, start);
    }

    if (RT_SUCCESS(rc))
        rc = ebml_EndSubElement(glob, glob->startSegment);

    /* Patch up the seek info block */
    if (RT_SUCCESS(rc))
        rc = Ebml_WriteWebMSeekInfo(glob);

    /* Patch up the track id */
    if (RT_SUCCESS(rc))
        rc = RTFileSeek(glob->file, glob->track_id_pos, RTFILE_SEEK_BEGIN, NULL);
    if (RT_SUCCESS(rc))
        rc  = ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash);

    if (RT_SUCCESS(rc))
        rc = RTFileSeek(glob->file, 0, RTFILE_SEEK_END, NULL);
    return rc;
}
static int ebml_StartSubElement(EbmlGlobal *glob, uint64_t *ebmlLoc, uint32_t class_id)
{
    // todo this is always taking 8 bytes, this may need later optimization
    // this is a key that says lenght unknown
    uint64_t unknownLen =  UINT64_C(0x01FFFFFFFFFFFFFF);

    ebml_WriteID(glob, class_id);
    *ebmlLoc = RTFileTell(glob->file);
    return ebml_WriteU64(glob, RT_H2BE_U64(unknownLen));
}
Example #4
0
/** @copydoc KRDROPS::pfnTell */
static KFOFF krdrRTFileTell(PKRDR pRdr)
{
    PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;

    /*
     * If the offset is undefined, try figure out what it is.
     */
    if (pRdrFile->off == -1)
    {
        pRdrFile->off = RTFileTell(pRdrFile->File);
        if (pRdrFile->off < 0)
            pRdrFile->off = -1;
    }
    return pRdrFile->off;
}
static int ebml_EndSubElement(EbmlGlobal *glob, uint64_t ebmlLoc)
{
    /* Save the current file pointer */
    uint64_t pos = RTFileTell(glob->file);

    /* Calculate the size of this element */
    uint64_t size  = pos - ebmlLoc - 8;
    size |= UINT64_C(0x0100000000000000);

    /* Seek back to the beginning of the element and write the new size */
    RTFileSeek(glob->file, ebmlLoc, RTFILE_SEEK_BEGIN, NULL);
    int rc = ebml_WriteU64(glob, RT_H2BE_U64(size));

    /* Reset the file pointer */
    RTFileSeek(glob->file, pos, RTFILE_SEEK_BEGIN, NULL);

    return rc;
}
/**
 * Worker function for dbgfR3CoreWrite() which does the writing.
 *
 * @returns VBox status code
 * @param   pVM                 Pointer to the VM.
 * @param   hFile               The file to write to.  Caller closes this.
 */
static int dbgfR3CoreWriteWorker(PVM pVM, RTFILE hFile)
{
    /*
     * Collect core information.
     */
    uint32_t const cu32MemRanges = dbgfR3GetRamRangeCount(pVM);
    uint16_t const cMemRanges    = cu32MemRanges < UINT16_MAX - 1 ? cu32MemRanges : UINT16_MAX - 1; /* One PT_NOTE Program header */
    uint16_t const cProgHdrs     = cMemRanges + 1;

    DBGFCOREDESCRIPTOR CoreDescriptor;
    RT_ZERO(CoreDescriptor);
    CoreDescriptor.u32Magic           = DBGFCORE_MAGIC;
    CoreDescriptor.u32FmtVersion      = DBGFCORE_FMT_VERSION;
    CoreDescriptor.cbSelf             = sizeof(CoreDescriptor);
    CoreDescriptor.u32VBoxVersion     = VBOX_FULL_VERSION;
    CoreDescriptor.u32VBoxRevision    = VMMGetSvnRev();
    CoreDescriptor.cCpus              = pVM->cCpus;

    Log((DBGFLOG_NAME ": CoreDescriptor Version=%u Revision=%u\n", CoreDescriptor.u32VBoxVersion, CoreDescriptor.u32VBoxRevision));

    /*
     * Compute the file layout (see pg_dbgf_vmcore).
     */
    uint64_t const offElfHdr          = RTFileTell(hFile);
    uint64_t const offNoteSection     = offElfHdr         + sizeof(Elf64_Ehdr);
    uint64_t const offLoadSections    = offNoteSection    + sizeof(Elf64_Phdr);
    uint64_t const cbLoadSections     = cMemRanges * sizeof(Elf64_Phdr);
    uint64_t const offCoreDescriptor  = offLoadSections   + cbLoadSections;
    uint64_t const cbCoreDescriptor   = Elf64NoteSectionSize(g_pcszCoreVBoxCore, sizeof(CoreDescriptor));
    uint64_t const offCpuDumps        = offCoreDescriptor + cbCoreDescriptor;
    uint64_t const cbCpuDumps         = pVM->cCpus * Elf64NoteSectionSize(g_pcszCoreVBoxCpu, sizeof(DBGFCORECPU));
    uint64_t const offMemory          = offCpuDumps       + cbCpuDumps;

    uint64_t const offNoteSectionData = offCoreDescriptor;
    uint64_t const cbNoteSectionData  = cbCoreDescriptor + cbCpuDumps;

    /*
     * Write ELF header.
     */
    int rc = Elf64WriteElfHdr(hFile, cProgHdrs, 0 /* cSecHdrs */);
    if (RT_FAILURE(rc))
    {
        LogRel((DBGFLOG_NAME ": Elf64WriteElfHdr failed. rc=%Rrc\n", rc));
        return rc;
    }

    /*
     * Write PT_NOTE program header.
     */
    Assert(RTFileTell(hFile) == offNoteSection);
    rc = Elf64WriteProgHdr(hFile, PT_NOTE, PF_R,
                           offNoteSectionData,  /* file offset to contents */
                           cbNoteSectionData,   /* size in core file */
                           cbNoteSectionData,   /* size in memory */
                           0);                  /* physical address */
    if (RT_FAILURE(rc))
    {
        LogRel((DBGFLOG_NAME ": Elf64WritreProgHdr failed for PT_NOTE. rc=%Rrc\n", rc));
        return rc;
    }

    /*
     * Write PT_LOAD program header for each memory range.
     */
    Assert(RTFileTell(hFile) == offLoadSections);
    uint64_t offMemRange = offMemory;
    for (uint16_t iRange = 0; iRange < cMemRanges; iRange++)
    {
        RTGCPHYS    GCPhysStart;
        RTGCPHYS    GCPhysEnd;
        bool        fIsMmio;
        rc = PGMR3PhysGetRange(pVM, iRange, &GCPhysStart, &GCPhysEnd, NULL /* pszDesc */, &fIsMmio);
        if (RT_FAILURE(rc))
        {
            LogRel((DBGFLOG_NAME ": PGMR3PhysGetRange failed for iRange(%u) rc=%Rrc\n", iRange, rc));
            return rc;
        }

        uint64_t cbMemRange  = GCPhysEnd - GCPhysStart + 1;
        uint64_t cbFileRange = fIsMmio ? 0 : cbMemRange;

        Log((DBGFLOG_NAME ": PGMR3PhysGetRange iRange=%u GCPhysStart=%#x GCPhysEnd=%#x cbMemRange=%u\n",
             iRange, GCPhysStart, GCPhysEnd, cbMemRange));

        rc = Elf64WriteProgHdr(hFile, PT_LOAD, PF_R,
                               offMemRange,                         /* file offset to contents */
                               cbFileRange,                         /* size in core file */
                               cbMemRange,                          /* size in memory */
                               GCPhysStart);                        /* physical address */
        if (RT_FAILURE(rc))
        {
            LogRel((DBGFLOG_NAME ": Elf64WriteProgHdr failed for memory range(%u) cbFileRange=%u cbMemRange=%u rc=%Rrc\n",
                    iRange, cbFileRange, cbMemRange, rc));
            return rc;
        }

        offMemRange += cbFileRange;
    }

    /*
     * Write the Core descriptor note header and data.
     */
    Assert(RTFileTell(hFile) == offCoreDescriptor);
    rc = Elf64WriteNoteHdr(hFile, NT_VBOXCORE, g_pcszCoreVBoxCore, &CoreDescriptor, sizeof(CoreDescriptor));
    if (RT_FAILURE(rc))
    {
        LogRel((DBGFLOG_NAME ": Elf64WriteNoteHdr failed for Note '%s' rc=%Rrc\n", g_pcszCoreVBoxCore, rc));
        return rc;
    }

    /*
     * Write the CPU context note headers and data.
     */
    Assert(RTFileTell(hFile) == offCpuDumps);
    PDBGFCORECPU pDbgfCoreCpu = (PDBGFCORECPU)RTMemAlloc(sizeof(*pDbgfCoreCpu));
    if (RT_UNLIKELY(!pDbgfCoreCpu))
    {
        LogRel((DBGFLOG_NAME ": failed to alloc %u bytes for DBGFCORECPU\n", sizeof(*pDbgfCoreCpu)));
        return VERR_NO_MEMORY;
    }

    for (uint32_t iCpu = 0; iCpu < pVM->cCpus; iCpu++)
    {
        PVMCPU      pVCpu = &pVM->aCpus[iCpu];
        PCPUMCTX    pCtx  = CPUMQueryGuestCtxPtr(pVCpu);
        if (RT_UNLIKELY(!pCtx))
        {
            LogRel((DBGFLOG_NAME ": CPUMQueryGuestCtxPtr failed for vCPU[%u]\n", iCpu));
            RTMemFree(pDbgfCoreCpu);
            return VERR_INVALID_POINTER;
        }

        RT_BZERO(pDbgfCoreCpu, sizeof(*pDbgfCoreCpu));
        dbgfR3GetCoreCpu(pCtx, pDbgfCoreCpu);
        rc = Elf64WriteNoteHdr(hFile, NT_VBOXCPU, g_pcszCoreVBoxCpu, pDbgfCoreCpu, sizeof(*pDbgfCoreCpu));
        if (RT_FAILURE(rc))
        {
            LogRel((DBGFLOG_NAME ": Elf64WriteNoteHdr failed for vCPU[%u] rc=%Rrc\n", iCpu, rc));
            RTMemFree(pDbgfCoreCpu);
            return rc;
        }
    }
    RTMemFree(pDbgfCoreCpu);
    pDbgfCoreCpu = NULL;

    /*
     * Write memory ranges.
     */
    Assert(RTFileTell(hFile) == offMemory);
    for (uint16_t iRange = 0; iRange < cMemRanges; iRange++)
    {
        RTGCPHYS GCPhysStart;
        RTGCPHYS GCPhysEnd;
        bool     fIsMmio;
        rc = PGMR3PhysGetRange(pVM, iRange, &GCPhysStart, &GCPhysEnd, NULL /* pszDesc */, &fIsMmio);
        if (RT_FAILURE(rc))
        {
            LogRel((DBGFLOG_NAME ": PGMR3PhysGetRange(2) failed for iRange(%u) rc=%Rrc\n", iRange, rc));
            return rc;
        }

        if (fIsMmio)
            continue;

        /*
         * Write page-by-page of this memory range.
         *
         * The read function may fail on MMIO ranges, we write these as zero
         * pages for now (would be nice to have the VGA bits there though).
         */
        uint64_t cbMemRange  = GCPhysEnd - GCPhysStart + 1;
        uint64_t cPages      = cbMemRange >> PAGE_SHIFT;
        for (uint64_t iPage = 0; iPage < cPages; iPage++)
        {
            uint8_t abPage[PAGE_SIZE];
            rc = PGMPhysSimpleReadGCPhys(pVM, abPage, GCPhysStart + (iPage << PAGE_SHIFT),  sizeof(abPage));
            if (RT_FAILURE(rc))
            {
                if (rc != VERR_PGM_PHYS_PAGE_RESERVED)
                    LogRel((DBGFLOG_NAME ": PGMPhysRead failed for iRange=%u iPage=%u. rc=%Rrc. Ignoring...\n", iRange, iPage, rc));
                RT_ZERO(abPage);
            }

            rc = RTFileWrite(hFile, abPage, sizeof(abPage), NULL /* all */);
            if (RT_FAILURE(rc))
            {
                LogRel((DBGFLOG_NAME ": RTFileWrite failed. iRange=%u iPage=%u rc=%Rrc\n", iRange, iPage, rc));
                return rc;
            }
        }
    }

    return rc;
}
int Ebml_WriteWebMBlock(EbmlGlobal                *glob,
                        const vpx_codec_enc_cfg_t *cfg,
                        const vpx_codec_cx_pkt_t  *pkt)
{
    uint16_t block_timecode = 0;
    int64_t  pts_ms;
    int      start_cluster = 0;
    int      rc = VINF_SUCCESS;

    /* Calculate the PTS of this frame in milliseconds */
    pts_ms = pkt->data.frame.pts * 1000
             * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den;
    if (pts_ms <= glob->last_pts_ms)
        pts_ms = glob->last_pts_ms + 1;
    glob->last_pts_ms = pts_ms;

    /* Calculate the relative time of this block */
    if (pts_ms - glob->cluster_timecode > 65536)
        start_cluster = 1;
    else
        block_timecode = (uint16_t)(pts_ms - glob->cluster_timecode);

    int fKeyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY);
    if (start_cluster || fKeyframe)
    {
        if (glob->cluster_open)
            rc = ebml_EndSubElement(glob, glob->startCluster);

        /* Open the new cluster */
        block_timecode = 0;
        glob->cluster_open = 1;
        glob->cluster_timecode = (uint32_t)pts_ms;
        glob->cluster_pos = RTFileTell(glob->file);
        if (RT_SUCCESS(rc))
            rc = ebml_StartSubElement(glob, &glob->startCluster, Cluster); //cluster
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode);

        /* Save a cue point if this is a keyframe. */
        if (fKeyframe)
        {
            struct cue_entry *cue;

            glob->cue_list = (cue_entry*)RTMemRealloc(glob->cue_list, (glob->cues+1) * sizeof(cue_entry));
            cue = &glob->cue_list[glob->cues];
            cue->time = glob->cluster_timecode;
            cue->loc = glob->cluster_pos;
            glob->cues++;
        }
    }

    /* Write the Simple Block */
    if (RT_SUCCESS(rc))
        rc = ebml_WriteID(glob, SimpleBlock);

    uint32_t block_length = pkt->data.frame.sz + 4;
    block_length |= 0x10000000;
    if (RT_SUCCESS(rc))
        rc = ebml_WriteU32(glob, RT_H2BE_U32(block_length));

    uint8_t track_number = 0x80 | 1;
    if (RT_SUCCESS(rc))
        rc = ebml_WriteU8(glob, track_number);

    if (RT_SUCCESS(rc))
        rc = ebml_WriteU16(glob, RT_H2BE_U16(block_timecode));

    uint8_t flags = 0;
    if (fKeyframe)
        flags |= 0x80;
    if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
        flags |= 0x08;
    if (RT_SUCCESS(rc))
        rc = ebml_WriteU8(glob, flags);

    if (RT_SUCCESS(rc))
        rc = ebml_Write(glob, pkt->data.frame.buf, pkt->data.frame.sz);

    return rc;
}
int Ebml_WriteWebMFileHeader(EbmlGlobal                *glob,
                             const vpx_codec_enc_cfg_t *cfg,
                             const struct vpx_rational *fps)
{
    int rc = VINF_SUCCESS;
    {
        uint64_t start;
        rc = ebml_StartSubElement(glob, &start, EBML);
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeUnsigned(glob, EBMLVersion, 1);
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1);
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4);
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8);
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeString(glob, DocType, "webm");
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeUnsigned(glob, DocTypeVersion, 2);
        if (RT_SUCCESS(rc))
            rc = Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2);
        if (RT_SUCCESS(rc))
            rc = ebml_EndSubElement(glob, start);
    }
    {
        if (RT_SUCCESS(rc))
            rc = ebml_StartSubElement(glob, &glob->startSegment, Segment);
        glob->position_reference = RTFileTell(glob->file);
        glob->framerate = *fps;
        if (RT_SUCCESS(rc))
            rc = Ebml_WriteWebMSeekInfo(glob);
        {
            uint64_t trackStart;
            glob->track_pos = RTFileTell(glob->file);
            if (RT_SUCCESS(rc))
                rc = ebml_StartSubElement(glob, &trackStart, Tracks);
            {
                uint32_t trackNumber = 1;
                uint32_t trackID = 0;

                uint64_t start;
                if (RT_SUCCESS(rc))
                    rc = ebml_StartSubElement(glob, &start, TrackEntry);
                if (RT_SUCCESS(rc))
                    rc = Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber);
                glob->track_id_pos = RTFileTell(glob->file);
                ebml_SerializeUnsigned32(glob, TrackUID, trackID);
                Ebml_SerializeUnsigned(glob, TrackType, 1); //video is always 1
                Ebml_SerializeString(glob, CodecID, "V_VP8");
                {
                    uint64_t videoStart;
                    if (RT_SUCCESS(rc))
                        rc = ebml_StartSubElement(glob, &videoStart, Video);
                    uint32_t pixelWidth = cfg->g_w;
                    if (RT_SUCCESS(rc))
                        rc = Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth);
                    uint32_t pixelHeight = cfg->g_h;
                    if (RT_SUCCESS(rc))
                        rc = Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight);
                    double   frameRate   = (double)fps->num / (double)fps->den;
                    if (RT_SUCCESS(rc))
                        rc = Ebml_SerializeFloat(glob, FrameRate, frameRate);
                    if (RT_SUCCESS(rc))
                        rc = ebml_EndSubElement(glob, videoStart);
                }
                if (RT_SUCCESS(rc))
                    rc = ebml_EndSubElement(glob, start); //Track Entry
            }
            if (RT_SUCCESS(rc))
                rc = ebml_EndSubElement(glob, trackStart);
        }
        // segment element is open
    }
    return rc;
}
Example #9
0
int main()
{
    int         cErrors = 0;
    RTPrintf("tstFile: TESTING\n");
    RTR3InitExeNoArguments(0);

    RTFILE    File;
    int rc = RTFileOpen(&File, "tstFile#1.tst", RTFILE_O_READWRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
    if (RT_FAILURE(rc))
    {
        RTPrintf("tstFile: FATAL ERROR - Failed to open file #1. rc=%Rrc\n", rc);
        return 1;
    }

    RTFOFF cbMax = -2;
    rc = RTFileGetMaxSizeEx(File, &cbMax);
    if (RT_FAILURE(rc))
    {
        RTPrintf("tstFile: RTFileGetMaxSizeEx failed: %Rrc\n", rc);
        cErrors++;
    }
    else if (cbMax <= 0)
    {
        RTPrintf("tstFile: RTFileGetMaxSizeEx failed: cbMax=%RTfoff\n", cbMax);
        cErrors++;
    }
    else if (RTFileGetMaxSize(File) != cbMax)
    {
        RTPrintf("tstFile: RTFileGetMaxSize failed; returns %RTfoff instead of %RTfoff\n", RTFileGetMaxSize(File), cbMax);
        cErrors++;
    }
    else
        RTPrintf("Maximum file size is %RTfoff bytes.\n", cbMax);

    /* grow file beyond 2G */
    rc = RTFileSetSize(File, _2G + _1M);
    if (RT_FAILURE(rc))
    {
        RTPrintf("Failed to grow file #1 to 2.001GB. rc=%Rrc\n", rc);
        cErrors++;
    }
    else
    {
        uint64_t cb;
        rc = RTFileGetSize(File, &cb);
        if (RT_FAILURE(rc))
        {
            RTPrintf("Failed to get file size of #1. rc=%Rrc\n", rc);
            cErrors++;
        }
        else if (cb != _2G + _1M)
        {
            RTPrintf("RTFileGetSize return %RX64 bytes, expected %RX64.\n", cb, _2G + _1M);
            cErrors++;
        }
        else
            RTPrintf("tstFile: cb=%RX64\n", cb);

        /*
         * Try some writes at the beginning of the file.
         */
        uint64_t offFile = RTFileTell(File);
        if (offFile != 0)
        {
            RTPrintf("RTFileTell -> %#RX64, expected 0 (#1)\n", offFile);
            cErrors++;
        }
        static const char szTestBuf[] = "Sausages and bacon for breakfast again!";
        size_t cbWritten = 0;
        while (cbWritten < sizeof(szTestBuf))
        {
            size_t cbWrittenPart;
            rc = RTFileWrite(File, &szTestBuf[cbWritten], sizeof(szTestBuf) - cbWritten, &cbWrittenPart);
            if (RT_FAILURE(rc))
                break;
            cbWritten += cbWrittenPart;
        }
        if (RT_FAILURE(rc))
        {
            RTPrintf("Failed to write to file #1 at offset 0. rc=%Rrc\n", rc);
            cErrors++;
        }
        else
        {
            /* check that it was written correctly. */
            rc = RTFileSeek(File, 0, RTFILE_SEEK_BEGIN, NULL);
            if (RT_FAILURE(rc))
            {
                RTPrintf("Failed to seek offset 0 in file #1. rc=%Rrc\n", rc);
                cErrors++;
            }
            else
            {
                char        szReadBuf[sizeof(szTestBuf)];
                size_t      cbRead = 0;
                while (cbRead < sizeof(szTestBuf))
                {
                    size_t cbReadPart;
                    rc = RTFileRead(File, &szReadBuf[cbRead], sizeof(szTestBuf) - cbRead, &cbReadPart);
                    if (RT_FAILURE(rc))
                        break;
                    cbRead += cbReadPart;
                }
                if (RT_FAILURE(rc))
                {
                    RTPrintf("Failed to read from file #1 at offset 0. rc=%Rrc\n", rc);
                    cErrors++;
                }
                else
                {
                    if (!memcmp(szReadBuf, szTestBuf, sizeof(szTestBuf)))
                        RTPrintf("tstFile: head write ok\n");
                    else
                    {
                        RTPrintf("Data read from file #1 at offset 0 differs from what we wrote there.\n");
                        cErrors++;
                    }
                }
            }
        }

        /*
         * Try some writes at the end of the file.
         */
        rc = RTFileSeek(File, _2G + _1M, RTFILE_SEEK_BEGIN, NULL);
        if (RT_FAILURE(rc))
        {
            RTPrintf("Failed to seek to _2G + _1M in file #1. rc=%Rrc\n", rc);
            cErrors++;
        }
        else
        {
            offFile = RTFileTell(File);
            if (offFile != _2G + _1M)
            {
                RTPrintf("RTFileTell -> %#llx, expected %#llx (#2)\n", offFile, _2G + _1M);
                cErrors++;
            }
            else
            {
                cbWritten = 0;
                while (cbWritten < sizeof(szTestBuf))
                {
                    size_t cbWrittenPart;
                    rc = RTFileWrite(File, &szTestBuf[cbWritten], sizeof(szTestBuf) - cbWritten, &cbWrittenPart);
                    if (RT_FAILURE(rc))
                        break;
                    cbWritten += cbWrittenPart;
                }
                if (RT_FAILURE(rc))
                {
                    RTPrintf("Failed to write to file #1 at offset 2G + 1M.  rc=%Rrc\n", rc);
                    cErrors++;
                }
                else
                {
                    rc = RTFileSeek(File, offFile, RTFILE_SEEK_BEGIN, NULL);
                    if (RT_FAILURE(rc))
                    {
                        RTPrintf("Failed to seek offset %RX64 in file #1. rc=%Rrc\n", offFile, rc);
                        cErrors++;
                    }
                    else
                    {
                        char        szReadBuf[sizeof(szTestBuf)];
                        size_t      cbRead = 0;
                        while (cbRead < sizeof(szTestBuf))
                        {
                            size_t cbReadPart;
                            rc = RTFileRead(File, &szReadBuf[cbRead], sizeof(szTestBuf) - cbRead, &cbReadPart);
                            if (RT_FAILURE(rc))
                                break;
                            cbRead += cbReadPart;
                        }
                        if (RT_FAILURE(rc))
                        {
                            RTPrintf("Failed to read from file #1 at offset 2G + 1M.  rc=%Rrc\n", rc);
                            cErrors++;
                        }
                        else
                        {
                            if (!memcmp(szReadBuf, szTestBuf, sizeof(szTestBuf)))
                                RTPrintf("tstFile: tail write ok\n");
                            else
                            {
                                RTPrintf("Data read from file #1 at offset 2G + 1M differs from what we wrote there.\n");
                                cErrors++;
                            }
                        }
                    }
                }
            }
        }

        /*
         * Some general seeking around.
         */
        rc = RTFileSeek(File, _2G + 1, RTFILE_SEEK_BEGIN, NULL);
        if (RT_FAILURE(rc))
        {
            RTPrintf("Failed to seek to _2G + 1 in file #1. rc=%Rrc\n", rc);
            cErrors++;
        }
        else
        {
            offFile = RTFileTell(File);
            if (offFile != _2G + 1)
            {
                RTPrintf("RTFileTell -> %#llx, expected %#llx (#3)\n", offFile, _2G + 1);
                cErrors++;
            }
        }

        /* seek end */
        rc = RTFileSeek(File, 0, RTFILE_SEEK_END, NULL);
        if (RT_FAILURE(rc))
        {
            RTPrintf("Failed to seek to end of file #1. rc=%Rrc\n", rc);
            cErrors++;
        }
        else
        {
            offFile = RTFileTell(File);
            if (offFile != _2G + _1M + sizeof(szTestBuf)) /* assuming tail write was ok. */
            {
                RTPrintf("RTFileTell -> %#RX64, expected %#RX64 (#4)\n", offFile, _2G + _1M + sizeof(szTestBuf));
                cErrors++;
            }
        }

        /* seek start */
        rc = RTFileSeek(File, 0, RTFILE_SEEK_BEGIN, NULL);
        if (RT_FAILURE(rc))
        {
            RTPrintf("Failed to seek to end of file #1. rc=%Rrc\n", rc);
            cErrors++;
        }
        else
        {
            offFile = RTFileTell(File);
            if (offFile != 0)
            {
                RTPrintf("RTFileTell -> %#llx, expected 0 (#5)\n", offFile);
                cErrors++;
            }
        }
    }


    /*
     * Cleanup.
     */
    rc = RTFileClose(File);
    if (RT_FAILURE(rc))
    {
        RTPrintf("Failed to close file #1. rc=%Rrc\n", rc);
        cErrors++;
    }
    rc = RTFileDelete("tstFile#1.tst");
    if (RT_FAILURE(rc))
    {
        RTPrintf("Failed to delete file #1. rc=%Rrc\n", rc);
        cErrors++;
    }

    /*
     * Summary
     */
    if (cErrors == 0)
        RTPrintf("tstFile: SUCCESS\n");
    else
        RTPrintf("tstFile: FAILURE - %d errors\n", cErrors);
    return !!cErrors;
}