#endif  // _WIN32
}

class FileStream::FileStreamPrivate
{
public:
FileStreamPrivate(const FileName &fileName)
: file(InvalidFileHandle)
, name(fileName)
, readOnly(true)
{
}

FileHandle file;
FileNameHandle name;
bool readOnly;
};

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

FileStream::FileStream(FileName fileName, bool openReadOnly)
: d(new FileStreamPrivate(fileName))
{
// First try with read / write mode, if that fails, fall back to read only.

if(!openReadOnly)
d->file = openFile(fileName, false);

if(d->file != InvalidFileHandle)
d->readOnly = false;
else
d->file = openFile(fileName, true);

if(d->file == InvalidFileHandle)
{
# ifdef _WIN32
debug("Could not open file " + fileName.toString());
# else
debug("Could not open file " + String(static_cast(d->name)));
# endif
}
}

FileStream::~FileStream()
{
if(isOpen())
closeFile(d->file);

delete d;
}

FileName FileStream::name() const
{
return d->name;
}

ByteVector FileStream::readBlock(ulong length)
{
if(!isOpen()) {
debug("FileStream::readBlock() -- invalid file.");
return ByteVector::null;
}

if(length == 0)
return ByteVector::null;

const ulong streamLength = static_cast(FileStream::length());
if(length > bufferSize() && length > streamLength)
length = streamLength;

ByteVector buffer(static_cast(length));

const size_t count = readFile(d->file, buffer);
buffer.resize(static_cast(count));

return buffer;
}

void FileStream::writeBlock(const ByteVector &data)
{
if(!isOpen()) {
debug("FileStream::writeBlock() -- invalid file.");
return;
}

if(readOnly()) {
debug("FileStream::writeBlock() -- read only file.");
return;
}

writeFile(d->file, data);
}

void FileStream::insert(const ByteVector &data, ulong start, ulong replace)
{
if(!isOpen()) {
debug("FileStream::insert() -- invalid file.");
return;
}

if(readOnly()) {
debug("FileStream::insert() -- read only file.");
return;
}

if(data.size() == replace) {
seek(start);
writeBlock(data);
return;
}
else if(data.size() < replace) {
seek(start);
writeBlock(data);
removeBlock(start + data.size(), replace - data.size());
return;
}

// Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore
// and avoid TagLib's high level API for rendering just copying parts of
// the file that don't contain tag data.
//
// Now I'll explain the steps in this ugliness:

// First, make sure that we're working with a buffer that is longer than
// the *differnce* in the tag sizes. We want to avoid overwriting parts
// that aren't yet in memory, so this is necessary.

ulong bufferLength = bufferSize();

while(data.size() - replace > bufferLength)
bufferLength += bufferSize();

// Set where to start the reading and writing.

long readPosition = start + replace;
long writePosition = start;

ByteVector buffer = data;
ByteVector aboutToOverwrite(static_cast(bufferLength));

while(true)
{
// Seek to the current read position and read the data that we're about
// to overwrite. Appropriately increment the readPosition.

seek(readPosition);
const size_t bytesRead = readFile(d->file, aboutToOverwrite);
aboutToOverwrite.resize(bytesRead);
readPosition += bufferLength;

// Check to see if we just read the last block. We need to call clear()
// if we did so that the last write succeeds.

if(bytesRead < bufferLength)
clear();

// Seek to the write position and write our buffer. Increment the
// writePosition.

seek(writePosition);
writeBlock(buffer);

// We hit the end of the file.

if(bytesRead == 0)
break;

writePosition += buffer.size();

// Make the current buffer the data that we read in the beginning.

buffer = aboutToOverwrite;
}
}

void FileStream::removeBlock(ulong start, ulong length)
{
if(!isOpen()) {
debug("FileStream::removeBlock() -- invalid file.");
return;
}

ulong bufferLength = bufferSize();

long readPosition = start + length;
long writePosition = start;

ByteVector buffer(static_cast(bufferLength));

for(size_t bytesRead = -1; bytesRead != 0;)
{
seek(readPosition);
bytesRead = readFile(d->file, buffer);
readPosition += bytesRead;

// Check to see if we just read the last block. We need to call clear()
// if we did so that the last write succeeds.

if(bytesRead < buffer.size()) {
clear();
buffer.resize(bytesRead);
}

seek(writePosition);
writeFile(d->file, buffer);

writePosition += bytesRead;
}

truncate(writePosition);
}

bool FileStream::readOnly() const
{
return d->readOnly;
}

bool FileStream::isOpen() const
{
return (d->file != InvalidFileHandle);
}

void FileStream::seek(long offset, Position p)
{
if(!isOpen()) {
debug("FileStream::seek() -- invalid file.");
return;
}

#ifdef _WIN32

DWORD whence;
switch(p) {
case Beginning:
whence = FILE_BEGIN;
break;
case Current:
whence = FILE_CURRENT;
break;
case End:
whence = FILE_END;
break;
default:
debug("FileStream::seek() -- Invalid Position value.");
return;
}

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
LARGE_INTEGER distance = { 0 };
distance.QuadPart = offset;

if (!SetFilePointerEx(d->file, distance, NULL, whence))
#else
SetLastError(NO_ERROR);
SetFilePointer(d->file, offset, NULL, whence);

const int lastError = GetLastError();
if(lastError != NO_ERROR && lastError != ERROR_NEGATIVE_SEEK)
#endif
debug("FileStream::seek() -- Failed to set the file pointer.");

#else

int whence;
switch(p) {
case Beginning:
whence = SEEK_SET;
break;
case Current:
whence = SEEK_CUR;
break;
case End:
whence = SEEK_END;
break;
default:
debug("FileStream::seek() -- Invalid Position value.");
return;
}

fseek(d->file, offset, whence);

#endif
}

void FileStream::clear()
{
#ifdef _WIN32

// NOP

#else

clearerr(d->file);

#endif
}

long FileStream::tell() const
{
#ifdef _WIN32

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
LARGE_INTEGER distance = { 0 };
LARGE_INTEGER position = { 0 };
if (SetFilePointerEx(d->file, distance, &position, FILE_CURRENT)) {
return static_cast(position.QuadPart);
}
#else
SetLastError(NO_ERROR);
const DWORD position = SetFilePointer(d->file, 0, NULL, FILE_CURRENT);
if(GetLastError() == NO_ERROR) {
return static_cast(position);
}
#endif
else {
debug("FileStream::tell() -- Failed to get the file pointer.");
return 0;
}

#else

return ftell(d->file);

#endif
}

long FileStream::length()
{
if(!isOpen()) {
debug("FileStream::length() -- invalid file.");
return 0;
}

#ifdef _WIN32

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
LARGE_INTEGER fileSize = { 0 };

if (GetFileSizeEx(d->file, &fileSize)) {
return static_cast(fileSize.QuadPart);
}
#else
SetLastError(NO_ERROR);
const DWORD fileSize = GetFileSize(d->file, NULL);

if (GetLastError() == NO_ERROR) {
return static_cast(fileSize);
}
#endif
else {
debug("FileStream::length() -- Failed to get the file size.");
return 0;
}

#else

const long curpos = tell();

seek(0, End);
const long endpos = tell();

seek(curpos, Beginning);

return endpos;

#endif
}

////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////

void FileStream::truncate(long length)
{
#ifdef _WIN32

const long currentPos = tell();

seek(length);

SetLastError(NO_ERROR);
SetEndOfFile(d->file);
if(GetLastError() != NO_ERROR) {
debug("FileStream::truncate() -- Failed to truncate the file.");
}

seek(currentPos);

#else

const int error = ftruncate(fileno(d->file), length);
if(error != 0) {
debug("FileStream::truncate() -- Coundn't truncate the file.");
}

#endif
}

TagLib::uint FileStream::bufferSize()
{
return 1024;
}



为了便于调试,还需要修改taglib\toolkit\tdebuglistener.cpp,以便在调试直接在Output窗口输出调试信息,完整代码如下:

/***************************************************************************
copyright : (C) 2013 by Tsuda Kageyu
email : tsuda.kageyu@gmail.com
***************************************************************************/

/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* 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 Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/

#include "tdebuglistener.h"

#include
#include

#ifdef _WIN32
# include
#endif

using namespace TagLib;

namespace
{
class DefaultListener : public DebugListener
{
public:
virtual void printMessage(const String &msg)
{
#ifdef _WIN32

const wstring wstr = msg.toWString();
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
OutputDebugStringW(wstr.c_str());
#else
const int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
if(len != 0) {
std::vector buf(len);
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &buf[0], len, NULL, NULL);

std::cerr << std::string(&buf[0]);
}
#endif

#else

std::cerr << msg;

#endif
}
};

DefaultListener defaultListener;
}

namespace TagLib
{
DebugListener *debugListener = &defaultListener;

DebugListener::DebugListener()
{
}

DebugListener::~DebugListener()
{
}

void setDebugListener(DebugListener *listener)
{
if(listener)
debugListener = listener;
else
debugListener = &defaultListener;
}
}


最后,编译吧,骚年!!!