⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tag_file.cpp

📁 FreeAMP(MP3播放)程序源代码-用来研究MP3解码
💻 CPP
字号:
// $Id: tag_file.cpp,v 1.2 2001/01/16 21:08:01 robert Exp $

// id3lib: a C++ library for creating and manipulating id3v1/v2 tags
// Copyright 1999, 2000  Scott Thomas Haug

// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by
// the Free Software Foundation; either version 2 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 Library General Public
// License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with this library; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

// The id3lib authors encourage improvements and optimisations to be sent to
// the id3lib coordinator.  Please see the README file for details on where to
// send such submissions.  See the AUTHORS file for a list of people who have
// contributed to id3lib.  See the ChangeLog file for a list of changes to
// id3lib.  These files are distributed with id3lib at
// http://download.sourceforge.net/id3lib/

#include "id3config.h"
#include <string.h>
#include <fstream.h>
#include <stdlib.h>
#include "utils.h"
#include "writers.h"
#include "io_strings.h"
#include "tag_impl.h"
#include "utils.h"

using namespace dami;

#if !defined HAVE_MKSTEMP
#  include <stdio.h>
#endif

#if defined HAVE_UNISTD_H
#  include <unistd.h>
#endif

#if defined WIN32 && (!defined(WINCE))
#  include <windows.h>
static int truncate(const char *path, size_t length)
{
  int result = -1;
  HANDLE fh;
  
  fh = ::CreateFile(path,
                    GENERIC_WRITE | GENERIC_READ,
                    0,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL);
  
  if(INVALID_HANDLE_VALUE != fh)
  {
    SetFilePointer(fh, length, NULL, FILE_BEGIN);
    SetEndOfFile(fh);
    CloseHandle(fh);
    result = 0;
  }
  
  return result;
}

// prevents a weird error I was getting compiling this under windows
#  if defined CreateFile
#    undef CreateFile
#  endif

#elif defined(WINCE)
// Createfile is apparently to defined to CreateFileW. (Bad Bad Bad), so we 
// work around it by converting path to Unicode
#  include <windows.h>
static int truncate(const char *path, size_t length)
{
  int result = -1;
  wchar_t wcTempPath[256];
  mbstowcs(wcTempPath,path,255);
  HANDLE fh;
  fh = ::CreateFile(wcTempPath,
                    GENERIC_WRITE | GENERIC_READ,
                    0,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL);

  if (INVALID_HANDLE_VALUE != fh)
  {
    SetFilePointer(fh, length, NULL, FILE_BEGIN);
    SetEndOfFile(fh);
    CloseHandle(fh);
    result = 0;
  }
  
  return result;
}
	
#endif

size_t ID3_TagImpl::Link(const char *fileInfo, bool parseID3v1, bool parseLyrics3)
{
  flags_t tt = ID3TT_NONE;
  if (parseID3v1)
  {
    tt |= ID3TT_ID3V1;
  }
  if (parseLyrics3)
  {
    tt |= ID3TT_LYRICS;
  }
  return this->Link(fileInfo, tt);
}

size_t ID3_TagImpl::Link(const char *fileInfo, flags_t tag_types)
{
  _tags_to_parse.set(tag_types);
  
  if (NULL == fileInfo)
  {
    return 0;
  }

  _file_name = fileInfo;
  _changed = true;

  this->ParseFile();
  
  return this->GetPrependedBytes();
}

size_t RenderV1ToFile(ID3_TagImpl& tag, fstream& file)
{
  if (!file)
  {
    return 0;
  }

  if (ID3_V1_LEN > tag.GetAppendedBytes())
  {
    file.seekp(0, ios::end);
  }
  else
  {
    // We want to check if there is already an id3v1 tag, so we can write over
    // it.  First, seek to the beginning of any possible id3v1 tag
    file.seekg(0-ID3_V1_LEN, ios::end);
    char sID[ID3_V1_LEN_ID];

    // Read in the TAG characters
    file.read(sID, ID3_V1_LEN_ID);

    // If those three characters are TAG, then there's a preexisting id3v1 tag,
    // so we should set the file cursor so we can overwrite it with a new tag.
    if (memcmp(sID, "TAG", ID3_V1_LEN_ID) == 0)
    {
      file.seekp(0-ID3_V1_LEN, ios::end);
    }
    // Otherwise, set the cursor to the end of the file so we can append on 
    // the new tag.
    else
    {
      file.seekp(0, ios::end);
    }
  }
  
  ID3_IOStreamWriter out(file);
  
  id3::v1::render(out, tag);

  return ID3_V1_LEN;
}

size_t RenderV2ToFile(const ID3_TagImpl& tag, fstream& file)
{
  ID3D_NOTICE( "RenderV2ToFile: starting" );
  if (!file)
  {
    ID3D_WARNING( "RenderV2ToFile: error in file" );
    return 0;
  }

  String tagString; 
  io::StringWriter writer(tagString);
  id3::v2::render(writer, tag);
  ID3D_NOTICE( "RenderV2ToFile: rendered v2" );

  const char* tagData = tagString.data();
  size_t tagSize = tagString.size();

  // if the new tag fits perfectly within the old and the old one
  // actually existed (ie this isn't the first tag this file has had)
  if ((!tag.GetPrependedBytes() && !ID3_GetDataSize(tag)) ||
      (tagSize == tag.GetPrependedBytes()))
  {
    file.seekp(0, ios::beg);
    file.write(tagData, tagSize);
  }
  else
  {
    String filename = tag.GetFileName();
#if !defined HAVE_MKSTEMP
    // This section is for Windows folk

    FILE *tempOut = tmpfile();
    if (NULL == tempOut)
    {
      // log this
      return 0;
      //ID3_THROW(ID3E_ReadOnly);
    }
    
    fwrite(tagData, 1, tagSize, tempOut);
    
    file.seekg(tag.GetPrependedBytes(), ios::beg);
    
    uchar tmpBuffer[BUFSIZ];
    while (!file)
    {
      file.read(tmpBuffer, BUFSIZ);
      size_t nBytes = file.gcount();
      fwrite(tmpBuffer, 1, nBytes, tempOut);
    }
    
    rewind(tempOut);
    openWritableFile(filename, file);
    
    while (!feof(tempOut))
    {
      size_t nBytes = fread(tmpBuffer, 1, BUFSIZ, tempOut);
      file.write(tmpBuffer, nBytes);
    }
    
    fclose(tempOut);
    
#else

    // else we gotta make a temp file, copy the tag into it, copy the
    // rest of the old file after the tag, delete the old file, rename
    // this new file to the old file's name and update the handle
    String sTmpSuffix = ".XXXXXX";
    if (filename.size() + sTmpSuffix.size() > ID3_PATH_LENGTH)
    {
      // log this
      return 0;
      //ID3_THROW_DESC(ID3E_NoFile, "filename too long");
    }
    char sTempFile[ID3_PATH_LENGTH];
    strcpy(sTempFile, filename.c_str());
    strcat(sTempFile, sTmpSuffix.c_str());
    
    int fd = mkstemp(sTempFile);
    if (fd < 0)
    {
      remove(sTempFile);
      //ID3_THROW_DESC(ID3E_NoFile, "couldn't open temp file");
    }

    ofstream tmpOut(fd);
    if (!tmpOut)
    {
      tmpOut.close();
      remove(sTempFile);
      return 0;
      // log this
      //ID3_THROW(ID3E_ReadOnly);
    }

    tmpOut.write(tagData, tagSize);
    file.seekg(tag.GetPrependedBytes(), ios::beg);
    uchar tmpBuffer[BUFSIZ];
    while (file)
    {
      file.read(tmpBuffer, BUFSIZ);
      size_t nBytes = file.gcount();
      tmpOut.write(tmpBuffer, nBytes);
    }
      
    tmpOut.close();

    file.close();

    remove(filename.c_str());
    rename(sTempFile, filename.c_str());

    openWritableFile(filename, file);
#endif
  }

  return tagSize;
}


flags_t ID3_TagImpl::Update(flags_t ulTagFlag)
{
  flags_t tags = ID3TT_NONE;
  
  fstream file;
  String filename = this->GetFileName();
  ID3_Err err = openWritableFile(filename, file);
  _file_size = getFileSize(file);
  
  if (err == ID3E_NoFile)
  {
    err = createFile(filename, file);
  }
  if (err == ID3E_ReadOnly)
  {
    return tags;
  }

  if ((ulTagFlag & ID3TT_ID3V2) && this->HasChanged())
  {
    _prepended_bytes = RenderV2ToFile(*this, file);
    if (_prepended_bytes)
    {
      tags |= ID3TT_ID3V2;
    }
  }
  
  if ((ulTagFlag & ID3TT_ID3V1) && 
      (!this->HasTagType(ID3TT_ID3V1) || this->HasChanged()))
  {
    size_t tag_bytes = RenderV1ToFile(*this, file);
    if (tag_bytes)
    {
      // only add the tag_bytes if there wasn't an id3v1 tag before
      if (! _file_tags.test(ID3TT_ID3V1))
      {
        _appended_bytes += tag_bytes;
      }
      tags |= ID3TT_ID3V1;
    }
  }
  _changed = false;
  _file_tags.add(tags);
  _file_size = getFileSize(file);
  file.close();
  return tags;
}

flags_t ID3_TagImpl::Strip(flags_t ulTagFlag)
{
  flags_t ulTags = ID3TT_NONE;
  const size_t data_size = ID3_GetDataSize(*this);
  
  // First remove the v2 tag, if requested
  if (ulTagFlag & ID3TT_PREPENDED & _file_tags.get())
  {
    fstream file;
    if (ID3E_NoError != openWritableFile(this->GetFileName(), file))
    {
      return ulTags;
    }
    _file_size = getFileSize(file);

    // We will remove the id3v2 tag in place: since it comes at the beginning
    // of the file, we'll effectively move all the data that comes after the
    // tag back n bytes, where n is the size of the id3v2 tag.  Once we've
    // copied the data, we'll truncate the file.
    file.seekg(this->GetPrependedBytes(), ios::beg);
    
    uchar aucBuffer[BUFSIZ];
    
    // The nBytesRemaining variable indicates how many bytes are to be copied
    size_t nBytesToCopy = data_size;

    // Here we increase the nBytesToCopy by the size of any tags that appear
    // at the end of the file if we don't want to strip them
    if (!(ulTagFlag & ID3TT_APPENDED))
    {
      nBytesToCopy += this->GetAppendedBytes();
    }
    
    // The nBytesRemaining variable indicates how many bytes are left to be 
    // moved in the actual file.
    // The nBytesCopied variable keeps track of how many actual bytes were
    // copied (or moved) so far.
    size_t 
      nBytesRemaining = nBytesToCopy,
      nBytesCopied = 0;
    while (!file.eof())
    {
      size_t nBytesToRead = dami::min<size_t>(nBytesRemaining - nBytesCopied, BUFSIZ);
      file.read(aucBuffer, nBytesToRead);
      size_t nBytesRead = file.gcount();

      if (nBytesRead != nBytesToRead)
      {
        // TODO: log this
        //cerr << "--- attempted to write " << nBytesRead << " bytes, "
        //     << "only wrote " << nBytesWritten << endl;
      }
      if (nBytesRead > 0)
      {
        long offset = nBytesRead + this->GetPrependedBytes();
        file.seekp(-offset, ios::cur);
        file.write(aucBuffer, nBytesRead);
        file.seekg(this->GetPrependedBytes(), ios::cur);
        nBytesCopied += nBytesRead;
      }
      
      if (nBytesCopied == nBytesToCopy || nBytesToRead < BUFSIZ)
      {
        break;
      }
    }
    file.close();
  }
  
  size_t nNewFileSize = data_size;

  if ((_file_tags.get() & ID3TT_APPENDED) && (ulTagFlag & ID3TT_APPENDED))
  {
    ulTags |= _file_tags.get() & ID3TT_APPENDED;
  }
  else
  {
    // if we're not stripping the appended tags, be sure to increase the file
    // size by those bytes
    nNewFileSize += this->GetAppendedBytes();
  }
  
  if ((ulTagFlag & ID3TT_PREPENDED) && (_file_tags.get() & ID3TT_PREPENDED))
  {
    // If we're stripping the ID3v2 tag, there's no need to adjust the new
    // file size, since it doesn't account for the ID3v2 tag size
    ulTags |= _file_tags.get() & ID3TT_PREPENDED;
  }
  else
  {
    // add the original prepended tag size since we don't want to delete it,
    // and the new file size represents the file size _not_ counting the ID3v2
    // tag
    nNewFileSize += this->GetPrependedBytes();
  }

  if (ulTags && (truncate(_file_name.c_str(), nNewFileSize) == -1))
  {
    // log this
    return 0;
    //ID3_THROW(ID3E_NoFile);
  }

  _prepended_bytes = (ulTags & ID3TT_PREPENDED) ? 0 : _prepended_bytes;
  _appended_bytes  = (ulTags & ID3TT_APPENDED)  ? 0 : _appended_bytes;
  _file_size = data_size + _prepended_bytes + _appended_bytes;
  
  _changed = _file_tags.remove(ulTags) || _changed;
  
  return ulTags;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -