開発環境 Microsoft Visual C++ 2010 Express (SP1)
実行環境 Microsoft Windows XP Home Edition (SP3)
プロジェクトの種類 Win32 コンソール アプリケーション
プロジェクト名 fdmp3
アプリケーションの種類 コンソール アプリケーション
追加のオプション 空のプロジェクト

fdmp3.cpp
#pragma comment(lib, "winmm")
 
#include <fcntl.h>	// _O_WTEXT
#include <io.h>		// _setmode
#include <stdio.h>	// _fileno
#include <string.h>	// memcmp
#include <tchar.h>
#include <Windows.h>
 
#include <map>
#include <set>
#include <string>
 
#define BUF_SIZE	(10 * 1024 * 1024)
 
// 型定義
typedef struct {		// ファイル情報構造体
	std::wstring strPath;
	DWORD offset;
	int iDelFlag;		// 削除フラグ
	int iDupFlag;		// 重複フラグ
} FileInfo;
typedef std::multimap<DWORD, FileInfo> mmfsfi;
typedef std::set<std::wstring> setstr;
 
// 関数プロトタイプ宣言
int files(const _TCHAR *ptcDir);
int filecmp(LPCTSTR ptcFile1, LPCTSTR ptcFile2, DWORD offset1, DWORD offset2, DWORD size);
int filedel(void);
BOOL LoadMP3(LPCTSTR path, DWORD &dataOffset, DWORD &dataSize);
 
// グローバル変数
mmfsfi g_mmap;
setstr g_set;
int g_iDelFlag = 0;
 
//==============================================================================
int _tmain(int argc, _TCHAR *argv[])
{
	mmfsfi::iterator it;
	mmfsfi::iterator itTarget;
	FileInfo *pfi;
	FileInfo *pfiTarget;
	_TCHAR atcDir[_MAX_PATH];
	size_t size;
	int iCount;
	int i;
 
	// BOMなしUTF-16LE
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
 
	if (argc < 2) {
		_ftprintf(stderr, _T("usage: fdmp3 [-d] dir [...]\n"));
		return 1;
	}
	for (i = 1; i < argc; i++) {
		if (argv[i][0] == _T('-')) {
			if (argv[i][1] == _T('d')) {
				g_iDelFlag = 1;
			}
		} else {
			_tcscpy_s(atcDir, argv[i]);
			size = _tcslen(atcDir);
			if (0 < size && atcDir[size - 1] == _T('\\')) {
				atcDir[size - 1] = _T('\0');
			}
			files(atcDir);
		}
	}
 
	for (it = g_mmap.begin(); it != g_mmap.end(); it++) {
		pfi = &(it->second);
		if (pfi->iDupFlag) continue;
		iCount = 0;
		for (itTarget = it; ++itTarget != g_mmap.end(); ) {
			if (itTarget->first != it->first) break;
			pfiTarget = &(itTarget->second);
			if (pfiTarget->iDupFlag) continue;
			int ret = filecmp(pfi->strPath.c_str(), pfiTarget->strPath.c_str(),
				pfi->offset, pfiTarget->offset, it->first);
			if (ret == 0) {
				pfiTarget->iDupFlag = 1;
				if (iCount == 0) {
					_tprintf(_T("\n%u KB (%u B)\n"),
						(it->first + 1023) / 1024, it->first);
					_tprintf(_T("%s\n"), pfi->strPath.c_str());
				}
				_tprintf(_T("%s\n"), pfiTarget->strPath.c_str());
				iCount++;
			}
		}
	}
 
	if (g_iDelFlag) filedel();
 
	return 0;
}
 
//------------------------------------------------------------------------------
int files(const TCHAR *ptcDir)
{
	size_t sizeDir = _tcslen(ptcDir);
	if (_MAX_PATH <= sizeDir + 4) {
		_ftprintf(stderr, _T("error: パスが長過ぎます。%u[%s]\n"), sizeDir, ptcDir);
		return -1;
	}
	TCHAR atcPath[_MAX_PATH];
	_stprintf_s(atcPath, _T("%s\\*.*"), ptcDir);
 
	FileInfo fi;
	fi.iDelFlag = g_iDelFlag;
	fi.iDupFlag = 0;
	_wfinddata_t fd;
	intptr_t handle = _tfindfirst(atcPath, &fd);
	if (handle == -1) {
		_ftprintf(stderr, _T("error: _tfindfirst[%s]\n"), ptcDir);
		return -1;
	}
	bool empty = true;
	do {
		if (_MAX_PATH <= sizeDir + 1 + _tcslen(fd.name)) {
			_ftprintf(stderr, _T("error: パスが長過ぎます。[%s][%s]\n"),
				ptcDir, fd.name);
			continue;
		}
		_stprintf_s(atcPath, _T("%s\\%s"), ptcDir, fd.name);
		if (fd.attrib & _A_SUBDIR) {
			if (_tcscmp(fd.name, _T(".")) && _tcscmp(fd.name, _T(".."))) {
				files(atcPath);
				empty = false;
			}
		} else {
			size_t len = _tcslen(fd.name);
			if (len <= 4 || _tcsicmp(fd.name + len - 4, _T(".mp3"))) continue;
			std::pair<setstr::iterator, bool> pair = g_set.insert(atcPath);
			if (pair.second == true) {
				DWORD size;
				if (LoadMP3(atcPath, fi.offset, size)) {
					fi.strPath = atcPath;
					g_mmap.insert(mmfsfi::value_type(size, fi));
					//_tprintf(_T("%u %u %s\n"), fi.offset, size, atcPath);
				}
			}
			empty = false;
		}
	} while (_tfindnext(handle, &fd) == 0);
	_findclose(handle);
	if (empty) {
		_ftprintf(stderr, _T("empty: [%s]\n"), ptcDir);
	}
 
	return 0;
}
 
//------------------------------------------------------------------------------
int filecmp(LPCTSTR ptcFile1, LPCTSTR ptcFile2, DWORD offset1, DWORD offset2, DWORD size)
{
	static char acBuf1[BUF_SIZE];
	static char acBuf2[BUF_SIZE];
	FILE *pFile1 = NULL;
	FILE *pFile2 = NULL;
	int iRetVal = -1;
 
	if (_tfopen_s(&pFile1, ptcFile1, _T("rb")) != 0) {
		_ftprintf(stderr, _T("%s を開けません\n"), ptcFile1);
		goto Exit;
	}
	if (_tfopen_s(&pFile2, ptcFile2, _T("rb")) != 0) {
		_ftprintf(stderr, _T("%s を開けません\n"), ptcFile2);
		goto Exit;
	}
 
	// offset分読み飛ばす
	fseek(pFile1, offset1, SEEK_SET);
	fseek(pFile2, offset2, SEEK_SET);
 
	for (size_t read = 0; ; ) {
		size_t count = min(size - read, BUF_SIZE);
		size_t sizeRead1 = fread(acBuf1, 1, count, pFile1);
		if (ferror(pFile1) != 0 || sizeRead1 != count) {
			_ftprintf(stderr, _T("error: fread[%s]\n"), ptcFile1);
			goto Exit;
		}
		size_t sizeRead2 = fread(acBuf2, 1, count, pFile2);
		if (ferror(pFile2) != 0 || sizeRead2 != count) {
			_ftprintf(stderr, _T("error: fread[%s]\n"), ptcFile2);
			goto Exit;
		}
		if (memcmp(acBuf1, acBuf2, sizeRead1) != 0) {
			iRetVal = 1;
			break;
		}
		read += count;
		if (size <= read) {
			iRetVal = 0;
			break;
		}
	}
Exit:
	if (pFile2) fclose(pFile2);
	if (pFile1) fclose(pFile1);
	return iRetVal;
}
 
//------------------------------------------------------------------------------
int filedel(void)
{
	mmfsfi::iterator it;
	FileInfo *pfi;
	FILE *pFile;
 
	if (_tfopen_s(&pFile, _T("finddup.log"), _T("wt,ccs=UNICODE"))) {
		_ftprintf(stderr, _T("ログファイルの作成に失敗しました\n"));
		return -1;
	}
	for(it = g_mmap.begin(); it != g_mmap.end(); it++) {
		pfi = &(it->second);
		if (pfi->iDupFlag && pfi->iDelFlag) {
			_ftprintf(pFile, _T("del \"%s\"\n"), pfi->strPath.c_str());
		}
	}
	fclose(pFile);
	return 0;
}
 
BOOL LoadMP3(LPCTSTR path, DWORD &dataOffset, DWORD &dataSize)
{
	// MP3ファイル部
	HMMIO hmmio = mmioOpen((LPTSTR)path, NULL, MMIO_READ);
	if (hmmio == NULL) {
		_ftprintf(stderr, _T("%s を開けません\n"), path);
		return FALSE;
	}
	DWORD dwFile = mmioSeek(hmmio, 0, SEEK_END);
	LPBYTE lpFile = new BYTE[dwFile];
	mmioSeek(hmmio, 0, SEEK_SET);
	mmioRead(hmmio, (HPSTR)lpFile, dwFile);
	mmioClose(hmmio, 0);
 
	// MP3データ部
	dataOffset = 0;
	dataSize = dwFile;
 
	// ID3v1
	if (128 <= dwFile && memcmp(lpFile + dwFile - 128, "TAG", 3) == 0) {
		dataSize -= 128;
	}
 
	// ID3v2
	if (10 <= dataSize && memcmp(lpFile, "ID3", 3) == 0) {
		LPBYTE size = lpFile + 6;
		DWORD dwTagSize = ((size[0]<<21)|(size[1]<<14)|(size[2]<<7)|size[3]) + 10;
		dataOffset = dwTagSize;
		dataSize -= dwTagSize;
	}
 
	delete[] lpFile;
	return TRUE;
}
 
最終更新:2013年03月07日 22:14