Aegisub/devel/vsfilter/dsutil/DSUtil.cpp

2219 lines
55 KiB
C++
Raw Blame History

/*
* Copyright (C) 2003-2006 Gabest
* http://www.gabest.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Make; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/
#include "stdafx.h"
#include <Vfw.h>
#include "../include/winddk/devioctl.h"
#include "../include/winddk/ntddcdrm.h"
#include "DSUtil.h"
#include "../include/moreuuids.h"
void DumpStreamConfig(TCHAR* fn, IAMStreamConfig* pAMVSCCap)
{
CString s, ss;
CStdioFile f;
if(!f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeText))
return;
int cnt = 0, size = 0;
if(FAILED(pAMVSCCap->GetNumberOfCapabilities(&cnt, &size)))
return;
s.Empty();
s.Format(_T("cnt %d, size %d\n"), cnt, size);
f.WriteString(s);
if(size == sizeof(VIDEO_STREAM_CONFIG_CAPS))
{
for(int i = 0; i < cnt; i++)
{
AM_MEDIA_TYPE* pmt = NULL;
VIDEO_STREAM_CONFIG_CAPS caps;
memset(&caps, 0, sizeof(caps));
s.Empty();
ss.Format(_T("%d\n"), i); s += ss;
f.WriteString(s);
if(FAILED(pAMVSCCap->GetStreamCaps(i, &pmt, (BYTE*)&caps)))
continue;
{
s.Empty();
ss.Format(_T("VIDEO_STREAM_CONFIG_CAPS\n")); s += ss;
ss.Format(_T("\tVideoStandard 0x%08x\n"), caps.VideoStandard); s += ss;
ss.Format(_T("\tInputSize %dx%d\n"), caps.InputSize); s += ss;
ss.Format(_T("\tCroppingSize %dx%d - %dx%d\n"), caps.MinCroppingSize, caps.MaxCroppingSize); s += ss;
ss.Format(_T("\tCropGranularity %d, %d\n"), caps.CropGranularityX, caps.CropGranularityY); s += ss;
ss.Format(_T("\tCropAlign %d, %d\n"), caps.CropAlignX, caps.CropAlignY); s += ss;
ss.Format(_T("\tOutputSize %dx%d - %dx%d\n"), caps.MinOutputSize, caps.MaxOutputSize); s += ss;
ss.Format(_T("\tOutputGranularity %d, %d\n"), caps.OutputGranularityX, caps.OutputGranularityY); s += ss;
ss.Format(_T("\tStretchTaps %d, %d\n"), caps.StretchTapsX, caps.StretchTapsY); s += ss;
ss.Format(_T("\tShrinkTaps %d, %d\n"), caps.ShrinkTapsX, caps.ShrinkTapsY); s += ss;
ss.Format(_T("\tFrameInterval %I64d, %I64d (%.4f, %.4f)\n"),
caps.MinFrameInterval, caps.MaxFrameInterval,
(float)10000000/caps.MinFrameInterval, (float)10000000/caps.MaxFrameInterval); s += ss;
ss.Format(_T("\tBitsPerSecond %d - %d\n"), caps.MinBitsPerSecond, caps.MaxBitsPerSecond); s += ss;
f.WriteString(s);
}
BITMAPINFOHEADER* pbh;
if(pmt->formattype == FORMAT_VideoInfo)
{
VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->pbFormat;
pbh = &vih->bmiHeader;
s.Empty();
ss.Format(_T("FORMAT_VideoInfo\n")); s += ss;
ss.Format(_T("\tAvgTimePerFrame %I64d, %.4f\n"), vih->AvgTimePerFrame, (float)10000000/vih->AvgTimePerFrame); s += ss;
ss.Format(_T("\trcSource %d,%d,%d,%d\n"), vih->rcSource); s += ss;
ss.Format(_T("\trcTarget %d,%d,%d,%d\n"), vih->rcTarget); s += ss;
f.WriteString(s);
}
else if(pmt->formattype == FORMAT_VideoInfo2)
{
VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->pbFormat;
pbh = &vih->bmiHeader;
s.Empty();
ss.Format(_T("FORMAT_VideoInfo2\n")); s += ss;
ss.Format(_T("\tAvgTimePerFrame %I64d, %.4f\n"), vih->AvgTimePerFrame, (float)10000000/vih->AvgTimePerFrame); s += ss;
ss.Format(_T("\trcSource %d,%d,%d,%d\n"), vih->rcSource); s += ss;
ss.Format(_T("\trcTarget %d,%d,%d,%d\n"), vih->rcTarget); s += ss;
ss.Format(_T("\tdwInterlaceFlags 0x%x\n"), vih->dwInterlaceFlags); s += ss;
ss.Format(_T("\tdwPictAspectRatio %d:%d\n"), vih->dwPictAspectRatioX, vih->dwPictAspectRatioY); s += ss;
f.WriteString(s);
}
else
{
DeleteMediaType(pmt);
continue;
}
s.Empty();
ss.Format(_T("BITMAPINFOHEADER\n")); s += ss;
ss.Format(_T("\tbiCompression %x\n"), pbh->biCompression); s += ss;
ss.Format(_T("\tbiWidth %d\n"), pbh->biWidth); s += ss;
ss.Format(_T("\tbiHeight %d\n"), pbh->biHeight); s += ss;
ss.Format(_T("\tbiBitCount %d\n"), pbh->biBitCount); s += ss;
ss.Format(_T("\tbiPlanes %d\n"), pbh->biPlanes); s += ss;
ss.Format(_T("\tbiSizeImage %d\n"), pbh->biSizeImage); s += ss;
f.WriteString(s);
DeleteMediaType(pmt);
}
}
else if(size == sizeof(AUDIO_STREAM_CONFIG_CAPS))
{
// TODO
}
}
int CountPins(IBaseFilter* pBF, int& nIn, int& nOut, int& nInC, int& nOutC)
{
nIn = nOut = 0;
nInC = nOutC = 0;
BeginEnumPins(pBF, pEP, pPin)
{
PIN_DIRECTION dir;
if(SUCCEEDED(pPin->QueryDirection(&dir)))
{
CComPtr<IPin> pPinConnectedTo;
pPin->ConnectedTo(&pPinConnectedTo);
if(dir == PINDIR_INPUT) {nIn++; if(pPinConnectedTo) nInC++;}
else if(dir == PINDIR_OUTPUT) {nOut++; if(pPinConnectedTo) nOutC++;}
}
}
EndEnumPins
return(nIn+nOut);
}
bool IsSplitter(IBaseFilter* pBF, bool fCountConnectedOnly)
{
int nIn, nOut, nInC, nOutC;
CountPins(pBF, nIn, nOut, nInC, nOutC);
return(fCountConnectedOnly ? nOutC > 1 : nOut > 1);
}
bool IsMultiplexer(IBaseFilter* pBF, bool fCountConnectedOnly)
{
int nIn, nOut, nInC, nOutC;
CountPins(pBF, nIn, nOut, nInC, nOutC);
return(fCountConnectedOnly ? nInC > 1 : nIn > 1);
}
bool IsStreamStart(IBaseFilter* pBF)
{
CComQIPtr<IAMFilterMiscFlags> pAMMF(pBF);
if(pAMMF && pAMMF->GetMiscFlags()&AM_FILTER_MISC_FLAGS_IS_SOURCE)
return(true);
int nIn, nOut, nInC, nOutC;
CountPins(pBF, nIn, nOut, nInC, nOutC);
AM_MEDIA_TYPE mt;
CComPtr<IPin> pIn = GetFirstPin(pBF);
return((nOut > 1)
|| (nOut > 0 && nIn == 1 && pIn && SUCCEEDED(pIn->ConnectionMediaType(&mt)) && mt.majortype == MEDIATYPE_Stream));
}
bool IsStreamEnd(IBaseFilter* pBF)
{
int nIn, nOut, nInC, nOutC;
CountPins(pBF, nIn, nOut, nInC, nOutC);
return(nOut == 0);
}
bool IsVideoRenderer(IBaseFilter* pBF)
{
int nIn, nOut, nInC, nOutC;
CountPins(pBF, nIn, nOut, nInC, nOutC);
if(nInC > 0 && nOut == 0)
{
BeginEnumPins(pBF, pEP, pPin)
{
AM_MEDIA_TYPE mt;
if(S_OK != pPin->ConnectionMediaType(&mt))
continue;
FreeMediaType(mt);
return(!!(mt.majortype == MEDIATYPE_Video));
/*&& (mt.formattype == FORMAT_VideoInfo || mt.formattype == FORMAT_VideoInfo2));*/
}
EndEnumPins
}
CLSID clsid;
memcpy(&clsid, &GUID_NULL, sizeof(clsid));
pBF->GetClassID(&clsid);
return(clsid == CLSID_VideoRenderer || clsid == CLSID_VideoRendererDefault);
}
#include <initguid.h>
DEFINE_GUID(CLSID_ReClock,
0x9dc15360, 0x914c, 0x46b8, 0xb9, 0xdf, 0xbf, 0xe6, 0x7f, 0xd3, 0x6c, 0x6a);
bool IsAudioWaveRenderer(IBaseFilter* pBF)
{
int nIn, nOut, nInC, nOutC;
CountPins(pBF, nIn, nOut, nInC, nOutC);
if(nInC > 0 && nOut == 0 && CComQIPtr<IBasicAudio>(pBF))
{
BeginEnumPins(pBF, pEP, pPin)
{
AM_MEDIA_TYPE mt;
if(S_OK != pPin->ConnectionMediaType(&mt))
continue;
FreeMediaType(mt);
return(!!(mt.majortype == MEDIATYPE_Audio)
/*&& mt.formattype == FORMAT_WaveFormatEx*/);
}
EndEnumPins
}
CLSID clsid;
memcpy(&clsid, &GUID_NULL, sizeof(clsid));
pBF->GetClassID(&clsid);
return(clsid == CLSID_DSoundRender || clsid == CLSID_AudioRender || clsid == CLSID_ReClock
|| clsid == __uuidof(CNullAudioRenderer) || clsid == __uuidof(CNullUAudioRenderer));
}
IBaseFilter* GetUpStreamFilter(IBaseFilter* pBF, IPin* pInputPin)
{
return GetFilterFromPin(GetUpStreamPin(pBF, pInputPin));
}
IPin* GetUpStreamPin(IBaseFilter* pBF, IPin* pInputPin)
{
BeginEnumPins(pBF, pEP, pPin)
{
if(pInputPin && pInputPin != pPin) continue;
PIN_DIRECTION dir;
CComPtr<IPin> pPinConnectedTo;
if(SUCCEEDED(pPin->QueryDirection(&dir)) && dir == PINDIR_INPUT
&& SUCCEEDED(pPin->ConnectedTo(&pPinConnectedTo)))
{
IPin* pRet = pPinConnectedTo.Detach();
pRet->Release();
return(pRet);
}
}
EndEnumPins
return(NULL);
}
IPin* GetFirstPin(IBaseFilter* pBF, PIN_DIRECTION dir)
{
if(!pBF) return(NULL);
BeginEnumPins(pBF, pEP, pPin)
{
PIN_DIRECTION dir2;
pPin->QueryDirection(&dir2);
if(dir == dir2)
{
IPin* pRet = pPin.Detach();
pRet->Release();
return(pRet);
}
}
EndEnumPins
return(NULL);
}
IPin* GetFirstDisconnectedPin(IBaseFilter* pBF, PIN_DIRECTION dir)
{
if(!pBF) return(NULL);
BeginEnumPins(pBF, pEP, pPin)
{
PIN_DIRECTION dir2;
pPin->QueryDirection(&dir2);
CComPtr<IPin> pPinTo;
if(dir == dir2 && (S_OK != pPin->ConnectedTo(&pPinTo)))
{
IPin* pRet = pPin.Detach();
pRet->Release();
return(pRet);
}
}
EndEnumPins
return(NULL);
}
IBaseFilter* FindFilter(LPCWSTR clsid, IFilterGraph* pFG)
{
CLSID clsid2;
CLSIDFromString(CComBSTR(clsid), &clsid2);
return(FindFilter(clsid2, pFG));
}
IBaseFilter* FindFilter(const CLSID& clsid, IFilterGraph* pFG)
{
BeginEnumFilters(pFG, pEF, pBF)
{
CLSID clsid2;
if(SUCCEEDED(pBF->GetClassID(&clsid2)) && clsid == clsid2)
return(pBF);
}
EndEnumFilters
return NULL;
}
CStringW GetFilterName(IBaseFilter* pBF)
{
CStringW name;
CFilterInfo fi;
if(pBF && SUCCEEDED(pBF->QueryFilterInfo(&fi)))
name = fi.achName;
return(name);
}
CStringW GetPinName(IPin* pPin)
{
CStringW name;
CPinInfo pi;
if(pPin && SUCCEEDED(pPin->QueryPinInfo(&pi)))
name = pi.achName;
return(name);
}
IFilterGraph* GetGraphFromFilter(IBaseFilter* pBF)
{
if(!pBF) return NULL;
IFilterGraph* pGraph = NULL;
CFilterInfo fi;
if(pBF && SUCCEEDED(pBF->QueryFilterInfo(&fi)))
pGraph = fi.pGraph;
return(pGraph);
}
IBaseFilter* GetFilterFromPin(IPin* pPin)
{
if(!pPin) return NULL;
IBaseFilter* pBF = NULL;
CPinInfo pi;
if(pPin && SUCCEEDED(pPin->QueryPinInfo(&pi)))
pBF = pi.pFilter;
return(pBF);
}
IPin* AppendFilter(IPin* pPin, CString DisplayName, IGraphBuilder* pGB)
{
IPin* pRet = pPin;
CInterfaceList<IBaseFilter> pFilters;
do
{
if(!pPin || DisplayName.IsEmpty() || !pGB)
break;
CComPtr<IPin> pPinTo;
PIN_DIRECTION dir;
if(FAILED(pPin->QueryDirection(&dir)) || dir != PINDIR_OUTPUT || SUCCEEDED(pPin->ConnectedTo(&pPinTo)))
break;
CComPtr<IBindCtx> pBindCtx;
CreateBindCtx(0, &pBindCtx);
CComPtr<IMoniker> pMoniker;
ULONG chEaten;
if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
break;
CComPtr<IBaseFilter> pBF;
if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
break;
CComPtr<IPropertyBag> pPB;
if(FAILED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB)))
break;
CComVariant var;
if(FAILED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
break;
pFilters.AddTail(pBF);
BeginEnumFilters(pGB, pEnum, pBF2)
pFilters.AddTail(pBF2);
EndEnumFilters
if(FAILED(pGB->AddFilter(pBF, CStringW(var.bstrVal))))
break;
BeginEnumFilters(pGB, pEnum, pBF2)
if(!pFilters.Find(pBF2) && SUCCEEDED(pGB->RemoveFilter(pBF2)))
pEnum->Reset();
EndEnumFilters
pPinTo = GetFirstPin(pBF, PINDIR_INPUT);
if(!pPinTo)
{
pGB->RemoveFilter(pBF);
break;
}
HRESULT hr;
if(FAILED(hr = pGB->ConnectDirect(pPin, pPinTo, NULL)))
{
hr = pGB->Connect(pPin, pPinTo);
pGB->RemoveFilter(pBF);
break;
}
BeginEnumFilters(pGB, pEnum, pBF2)
if(!pFilters.Find(pBF2) && SUCCEEDED(pGB->RemoveFilter(pBF2)))
pEnum->Reset();
EndEnumFilters
pRet = GetFirstPin(pBF, PINDIR_OUTPUT);
if(!pRet)
{
pRet = pPin;
pGB->RemoveFilter(pBF);
break;
}
}
while(false);
return(pRet);
}
IPin* InsertFilter(IPin* pPin, CString DisplayName, IGraphBuilder* pGB)
{
do
{
if(!pPin || DisplayName.IsEmpty() || !pGB)
break;
PIN_DIRECTION dir;
if(FAILED(pPin->QueryDirection(&dir)))
break;
CComPtr<IPin> pFrom, pTo;
if(dir == PINDIR_INPUT)
{
pPin->ConnectedTo(&pFrom);
pTo = pPin;
}
else if(dir == PINDIR_OUTPUT)
{
pFrom = pPin;
pPin->ConnectedTo(&pTo);
}
if(!pFrom || !pTo)
break;
CComPtr<IBindCtx> pBindCtx;
CreateBindCtx(0, &pBindCtx);
CComPtr<IMoniker> pMoniker;
ULONG chEaten;
if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
break;
CComPtr<IBaseFilter> pBF;
if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
break;
CComPtr<IPropertyBag> pPB;
if(FAILED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB)))
break;
CComVariant var;
if(FAILED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
break;
if(FAILED(pGB->AddFilter(pBF, CStringW(var.bstrVal))))
break;
CComPtr<IPin> pFromTo = GetFirstPin(pBF, PINDIR_INPUT);
if(!pFromTo)
{
pGB->RemoveFilter(pBF);
break;
}
if(FAILED(pGB->Disconnect(pFrom)) || FAILED(pGB->Disconnect(pTo)))
{
pGB->RemoveFilter(pBF);
pGB->ConnectDirect(pFrom, pTo, NULL);
break;
}
HRESULT hr;
if(FAILED(hr = pGB->ConnectDirect(pFrom, pFromTo, NULL)))
{
pGB->RemoveFilter(pBF);
pGB->ConnectDirect(pFrom, pTo, NULL);
break;
}
CComPtr<IPin> pToFrom = GetFirstPin(pBF, PINDIR_OUTPUT);
if(!pToFrom)
{
pGB->RemoveFilter(pBF);
pGB->ConnectDirect(pFrom, pTo, NULL);
break;
}
if(FAILED(pGB->ConnectDirect(pToFrom, pTo, NULL)))
{
pGB->RemoveFilter(pBF);
pGB->ConnectDirect(pFrom, pTo, NULL);
break;
}
pPin = pToFrom;
}
while(false);
return(pPin);
}
void ExtractMediaTypes(IPin* pPin, CAtlArray<GUID>& types)
{
types.RemoveAll();
BeginEnumMediaTypes(pPin, pEM, pmt)
{
bool fFound = false;
for(int i = 0; !fFound && i < types.GetCount(); i += 2)
{
if(types[i] == pmt->majortype && types[i+1] == pmt->subtype)
fFound = true;
}
if(!fFound)
{
types.Add(pmt->majortype);
types.Add(pmt->subtype);
}
}
EndEnumMediaTypes(pmt)
}
void ExtractMediaTypes(IPin* pPin, CAtlList<CMediaType>& mts)
{
mts.RemoveAll();
BeginEnumMediaTypes(pPin, pEM, pmt)
{
bool fFound = false;
POSITION pos = mts.GetHeadPosition();
while(!fFound && pos)
{
CMediaType& mt = mts.GetNext(pos);
if(mt.majortype == pmt->majortype && mt.subtype == pmt->subtype)
fFound = true;
}
if(!fFound)
{
mts.AddTail(CMediaType(*pmt));
}
}
EndEnumMediaTypes(pmt)
}
int Eval_Exception(int n_except)
{
if(n_except == STATUS_ACCESS_VIOLATION)
{
AfxMessageBox(_T("The property page of this filter has just caused a\nmemory access violation. The application will gently die now :)"));
}
return EXCEPTION_CONTINUE_SEARCH;
}
void MyOleCreatePropertyFrame(HWND hwndOwner, UINT x, UINT y, LPCOLESTR lpszCaption, ULONG cObjects, LPUNKNOWN FAR* lplpUnk, ULONG cPages, LPCLSID lpPageClsID, LCID lcid, DWORD dwReserved, LPVOID lpvReserved)
{
__try
{
OleCreatePropertyFrame(hwndOwner, x, y, lpszCaption, cObjects, lplpUnk, cPages, lpPageClsID, lcid, dwReserved, lpvReserved);
}
__except(Eval_Exception(GetExceptionCode()))
{
// No code; this block never executed.
}
}
void ShowPPage(CString DisplayName, HWND hParentWnd)
{
CComPtr<IBindCtx> pBindCtx;
CreateBindCtx(0, &pBindCtx);
CComPtr<IMoniker> pMoniker;
ULONG chEaten;
if(S_OK != MkParseDisplayName(pBindCtx, CStringW(DisplayName), &chEaten, &pMoniker))
return;
CComPtr<IBaseFilter> pBF;
if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
return;
ShowPPage(pBF, hParentWnd);
}
void ShowPPage(IUnknown* pUnk, HWND hParentWnd)
{
CComQIPtr<ISpecifyPropertyPages> pSPP = pUnk;
if(!pSPP) return;
CString str;
CComQIPtr<IBaseFilter> pBF = pSPP;
CFilterInfo fi;
CComQIPtr<IPin> pPin = pSPP;
CPinInfo pi;
if(pBF && SUCCEEDED(pBF->QueryFilterInfo(&fi)))
str = fi.achName;
else if(pPin && SUCCEEDED(pPin->QueryPinInfo(&pi)))
str = pi.achName;
CAUUID caGUID;
caGUID.pElems = NULL;
if(SUCCEEDED(pSPP->GetPages(&caGUID)))
{
IUnknown* lpUnk = NULL;
pSPP.QueryInterface(&lpUnk);
MyOleCreatePropertyFrame(
hParentWnd, 0, 0, CStringW(str),
1, (IUnknown**)&lpUnk,
caGUID.cElems, caGUID.pElems,
0, 0, NULL);
lpUnk->Release();
if(caGUID.pElems) CoTaskMemFree(caGUID.pElems);
}
}
CLSID GetCLSID(IBaseFilter* pBF)
{
CLSID clsid = GUID_NULL;
if(pBF) pBF->GetClassID(&clsid);
return(clsid);
}
CLSID GetCLSID(IPin* pPin)
{
return(GetCLSID(GetFilterFromPin(pPin)));
}
bool IsCLSIDRegistered(LPCTSTR clsid)
{
CString rootkey1(_T("CLSID\\"));
CString rootkey2(_T("CLSID\\{083863F1-70DE-11d0-BD40-00A0C911CE86}\\Instance\\"));
return ERROR_SUCCESS == CRegKey().Open(HKEY_CLASSES_ROOT, rootkey1 + clsid, KEY_READ)
|| ERROR_SUCCESS == CRegKey().Open(HKEY_CLASSES_ROOT, rootkey2 + clsid, KEY_READ);
}
bool IsCLSIDRegistered(const CLSID& clsid)
{
bool fRet = false;
LPOLESTR pStr = NULL;
if(S_OK == StringFromCLSID(clsid, &pStr) && pStr)
{
fRet = IsCLSIDRegistered(CString(pStr));
CoTaskMemFree(pStr);
}
return(fRet);
}
void CStringToBin(CString str, CAtlArray<BYTE>& data)
{
str.Trim();
ASSERT((str.GetLength()&1) == 0);
data.SetCount(str.GetLength()/2);
BYTE b = 0;
str.MakeUpper();
for(int i = 0, j = str.GetLength(); i < j; i++)
{
TCHAR c = str[i];
if(c >= '0' && c <= '9')
{
if(!(i&1)) b = ((char(c-'0')<<4)&0xf0)|(b&0x0f);
else b = (char(c-'0')&0x0f)|(b&0xf0);
}
else if(c >= 'A' && c <= 'F')
{
if(!(i&1)) b = ((char(c-'A'+10)<<4)&0xf0)|(b&0x0f);
else b = (char(c-'A'+10)&0x0f)|(b&0xf0);
}
else break;
if(i&1)
{
data[i>>1] = b;
b = 0;
}
}
}
CString BinToCString(BYTE* ptr, int len)
{
CString ret;
while(len-- > 0)
{
TCHAR high, low;
high = (*ptr>>4) >= 10 ? (*ptr>>4)-10 + 'A' : (*ptr>>4) + '0';
low = (*ptr&0xf) >= 10 ? (*ptr&0xf)-10 + 'A' : (*ptr&0xf) + '0';
CString str;
str.Format(_T("%c%c"), high, low);
ret += str;
ptr++;
}
return(ret);
}
static void FindFiles(CString fn, CAtlList<CString>& files)
{
CString path = fn;
path.Replace('/', '\\');
path = path.Left(path.ReverseFind('\\')+1);
WIN32_FIND_DATA findData;
HANDLE h = FindFirstFile(fn, &findData);
if(h != INVALID_HANDLE_VALUE)
{
do {files.AddTail(path + findData.cFileName);}
while(FindNextFile(h, &findData));
FindClose(h);
}
}
cdrom_t GetCDROMType(TCHAR drive, CAtlList<CString>& files)
{
files.RemoveAll();
CString path;
path.Format(_T("%c:"), drive);
if(GetDriveType(path + _T("\\")) == DRIVE_CDROM)
{
// CDROM_VideoCD
FindFiles(path + _T("\\mpegav\\avseq??.dat"), files);
FindFiles(path + _T("\\mpegav\\avseq??.mpg"), files);
FindFiles(path + _T("\\mpeg2\\avseq??.dat"), files);
FindFiles(path + _T("\\mpeg2\\avseq??.mpg"), files);
FindFiles(path + _T("\\mpegav\\music??.dat"), files);
FindFiles(path + _T("\\mpegav\\music??.mpg"), files);
FindFiles(path + _T("\\mpeg2\\music??.dat"), files);
FindFiles(path + _T("\\mpeg2\\music??.mpg"), files);
if(files.GetCount() > 0) return CDROM_VideoCD;
// CDROM_DVDVideo
FindFiles(path + _T("\\VIDEO_TS\\video_ts.ifo"), files);
if(files.GetCount() > 0) return CDROM_DVDVideo;
// CDROM_Audio
if(!(GetVersion()&0x80000000))
{
HANDLE hDrive = CreateFile(CString(_T("\\\\.\\")) + path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if(hDrive != INVALID_HANDLE_VALUE)
{
DWORD BytesReturned;
CDROM_TOC TOC;
if(DeviceIoControl(hDrive, IOCTL_CDROM_READ_TOC, NULL, 0, &TOC, sizeof(TOC), &BytesReturned, 0))
{
for(int i = TOC.FirstTrack; i <= TOC.LastTrack; i++)
{
// MMC-3 Draft Revision 10g: Table 222 <20> Q Sub-channel control field
TOC.TrackData[i-1].Control &= 5;
if(TOC.TrackData[i-1].Control == 0 || TOC.TrackData[i-1].Control == 1)
{
CString fn;
fn.Format(_T("%s\\track%02d.cda"), path, i);
files.AddTail(fn);
}
}
}
CloseHandle(hDrive);
}
}
if(files.GetCount() > 0) return CDROM_Audio;
// it is a cdrom but nothing special
return CDROM_Unknown;
}
return CDROM_NotFound;
}
CString GetDriveLabel(TCHAR drive)
{
CString label;
CString path;
path.Format(_T("%c:\\"), drive);
TCHAR VolumeNameBuffer[MAX_PATH], FileSystemNameBuffer[MAX_PATH];
DWORD VolumeSerialNumber, MaximumComponentLength, FileSystemFlags;
if(GetVolumeInformation(path,
VolumeNameBuffer, MAX_PATH, &VolumeSerialNumber, &MaximumComponentLength,
&FileSystemFlags, FileSystemNameBuffer, MAX_PATH))
{
label = VolumeNameBuffer;
}
return(label);
}
bool GetKeyFrames(CString fn, CUIntArray& kfs)
{
kfs.RemoveAll();
CString fn2 = CString(fn).MakeLower();
if(fn2.Mid(fn2.ReverseFind('.')+1) == _T("avi"))
{
AVIFileInit();
PAVIFILE pfile;
if(AVIFileOpen(&pfile, fn, OF_SHARE_DENY_WRITE, 0L) == 0)
{
AVIFILEINFO afi;
memset(&afi, 0, sizeof(afi));
AVIFileInfo(pfile, &afi, sizeof(AVIFILEINFO));
CComPtr<IAVIStream> pavi;
if(AVIFileGetStream(pfile, &pavi, streamtypeVIDEO, 0) == AVIERR_OK)
{
AVISTREAMINFO si;
AVIStreamInfo(pavi, &si, sizeof(si));
if(afi.dwCaps&AVIFILECAPS_ALLKEYFRAMES)
{
kfs.SetSize(si.dwLength);
for(int kf = 0; kf < si.dwLength; kf++) kfs[kf] = kf;
}
else
{
for(int kf = 0; ; kf++)
{
kf = pavi->FindSample(kf, FIND_KEY|FIND_NEXT);
if(kf < 0 || kfs.GetCount() > 0 && kfs[kfs.GetCount()-1] >= kf) break;
kfs.Add(kf);
}
if(kfs.GetCount() > 0 && kfs[kfs.GetCount()-1] < si.dwLength-1)
{
kfs.Add(si.dwLength-1);
}
}
}
AVIFileRelease(pfile);
}
AVIFileExit();
}
return(kfs.GetCount() > 0);
}
DVD_HMSF_TIMECODE RT2HMSF(REFERENCE_TIME rt, double fps)
{
DVD_HMSF_TIMECODE hmsf =
{
(BYTE)((rt/10000000/60/60)),
(BYTE)((rt/10000000/60)%60),
(BYTE)((rt/10000000)%60),
(BYTE)(1.0*((rt/10000)%1000) * fps / 1000)
};
return hmsf;
}
REFERENCE_TIME HMSF2RT(DVD_HMSF_TIMECODE hmsf, double fps)
{
if(fps == 0) {hmsf.bFrames = 0; fps = 1;}
return (REFERENCE_TIME)((((REFERENCE_TIME)hmsf.bHours*60+hmsf.bMinutes)*60+hmsf.bSeconds)*1000+1.0*hmsf.bFrames*1000/fps)*10000;
}
void memsetd(void* dst, unsigned int c, int nbytes)
{
__asm
{
mov eax, c
mov ecx, nbytes
shr ecx, 2
mov edi, dst
cld
rep stosd
}
}
bool ExtractBIH(const AM_MEDIA_TYPE* pmt, BITMAPINFOHEADER* bih)
{
if(pmt && bih)
{
memset(bih, 0, sizeof(*bih));
if(pmt->formattype == FORMAT_VideoInfo)
{
VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->pbFormat;
memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
return true;
}
else if(pmt->formattype == FORMAT_VideoInfo2)
{
VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->pbFormat;
memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
return true;
}
else if(pmt->formattype == FORMAT_MPEGVideo)
{
VIDEOINFOHEADER* vih = &((MPEG1VIDEOINFO*)pmt->pbFormat)->hdr;
memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
return true;
}
else if(pmt->formattype == FORMAT_MPEG2_VIDEO)
{
VIDEOINFOHEADER2* vih = &((MPEG2VIDEOINFO*)pmt->pbFormat)->hdr;
memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
return true;
}
else if(pmt->formattype == FORMAT_DiracVideoInfo)
{
VIDEOINFOHEADER2* vih = &((DIRACINFOHEADER*)pmt->pbFormat)->hdr;
memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
return true;
}
}
return false;
}
bool ExtractBIH(IMediaSample* pMS, BITMAPINFOHEADER* bih)
{
AM_MEDIA_TYPE* pmt = NULL;
pMS->GetMediaType(&pmt);
if(pmt)
{
bool fRet = ExtractBIH(pmt, bih);
DeleteMediaType(pmt);
return(fRet);
}
return(false);
}
bool ExtractDim(const AM_MEDIA_TYPE* pmt, int& w, int& h, int& arx, int& ary)
{
w = h = arx = ary = 0;
if(pmt->formattype == FORMAT_VideoInfo || pmt->formattype == FORMAT_MPEGVideo)
{
VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->pbFormat;
w = vih->bmiHeader.biWidth;
h = abs(vih->bmiHeader.biHeight);
arx = w * vih->bmiHeader.biYPelsPerMeter;
ary = h * vih->bmiHeader.biXPelsPerMeter;
}
else if(pmt->formattype == FORMAT_VideoInfo2 || pmt->formattype == FORMAT_MPEG2_VIDEO || pmt->formattype == FORMAT_DiracVideoInfo)
{
VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->pbFormat;
w = vih->bmiHeader.biWidth;
h = abs(vih->bmiHeader.biHeight);
arx = vih->dwPictAspectRatioX;
ary = vih->dwPictAspectRatioY;
}
else
{
return(false);
}
if(!arx || !ary)
{
arx = w;
ary = h;
}
BYTE* ptr = NULL;
DWORD len = 0;
if(pmt->formattype == FORMAT_MPEGVideo)
{
ptr = ((MPEG1VIDEOINFO*)pmt->pbFormat)->bSequenceHeader;
len = ((MPEG1VIDEOINFO*)pmt->pbFormat)->cbSequenceHeader;
if(ptr && len >= 8 && *(DWORD*)ptr == 0xb3010000)
{
w = (ptr[4]<<4)|(ptr[5]>>4);
h = ((ptr[5]&0xf)<<8)|ptr[6];
float ar[] =
{
1.0000f,1.0000f,0.6735f,0.7031f,
0.7615f,0.8055f,0.8437f,0.8935f,
0.9157f,0.9815f,1.0255f,1.0695f,
1.0950f,1.1575f,1.2015f,1.0000f,
};
arx = (int)((float)w / ar[ptr[7]>>4] + 0.5);
ary = h;
}
}
else if(pmt->formattype == FORMAT_MPEG2_VIDEO)
{
ptr = (BYTE*)((MPEG2VIDEOINFO*)pmt->pbFormat)->dwSequenceHeader;
len = ((MPEG2VIDEOINFO*)pmt->pbFormat)->cbSequenceHeader;
if(ptr && len >= 8 && *(DWORD*)ptr == 0xb3010000)
{
w = (ptr[4]<<4)|(ptr[5]>>4);
h = ((ptr[5]&0xf)<<8)|ptr[6];
struct {int x, y;} ar[] = {{w,h},{4,3},{16,9},{221,100},{w,h}};
int i = min(max(ptr[7]>>4, 1), 5)-1;
arx = ar[i].x;
ary = ar[i].y;
}
}
if(ptr && len >= 8)
{
}
DWORD a = arx, b = ary;
while(a) {int tmp = a; a = b % tmp; b = tmp;}
if(b) arx /= b, ary /= b;
return(true);
}
bool MakeMPEG2MediaType(CMediaType& mt, BYTE* seqhdr, DWORD len, int w, int h)
{
if(len < 4 || *(DWORD*)seqhdr != 0xb3010000) return false;
BYTE* seqhdr_ext = NULL;
BYTE* seqhdr_end = seqhdr + 11;
if(seqhdr_end - seqhdr > len) return false;
if(*seqhdr_end & 0x02) seqhdr_end += 64;
if(seqhdr_end - seqhdr > len) return false;
if(*seqhdr_end & 0x01) seqhdr_end += 64;
if(seqhdr_end - seqhdr > len) return false;
seqhdr_end++;
if(seqhdr_end - seqhdr > len) return false;
if(len - (seqhdr_end - seqhdr) > 4 && *(DWORD*)seqhdr_end == 0xb5010000) {seqhdr_ext = seqhdr_end; seqhdr_end += 10;}
if(seqhdr_end - seqhdr > len) return false;
len = seqhdr_end - seqhdr;
mt = CMediaType();
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_MPEG2_VIDEO;
mt.formattype = FORMAT_MPEG2Video;
MPEG2VIDEOINFO* vih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + len);
memset(mt.Format(), 0, mt.FormatLength());
vih->hdr.bmiHeader.biSize = sizeof(vih->hdr.bmiHeader);
vih->hdr.bmiHeader.biWidth = w;
vih->hdr.bmiHeader.biHeight = h;
BYTE* pSequenceHeader = (BYTE*)vih->dwSequenceHeader;
memcpy(pSequenceHeader, seqhdr, len);
vih->cbSequenceHeader = len;
static char profile[8] =
{
0, AM_MPEG2Profile_High, AM_MPEG2Profile_SpatiallyScalable, AM_MPEG2Profile_SNRScalable,
AM_MPEG2Profile_Main, AM_MPEG2Profile_Simple, 0, 0
};
static char level[16] =
{
0, 0, 0, 0,
AM_MPEG2Level_High, 0, AM_MPEG2Level_High1440, 0,
AM_MPEG2Level_Main, 0, AM_MPEG2Level_Low, 0,
0, 0, 0, 0
};
if(seqhdr_ext && (seqhdr_ext[4] & 0xf0) == 0x10)
{
vih->dwProfile = profile[seqhdr_ext[4] & 0x07];
vih->dwLevel = level[seqhdr_ext[5] >> 4];
}
return true;
}
unsigned __int64 GetFileVersion(LPCTSTR fn)
{
unsigned __int64 ret = 0;
DWORD buff[4];
VS_FIXEDFILEINFO* pvsf = (VS_FIXEDFILEINFO*)buff;
DWORD d; // a variable that GetFileVersionInfoSize sets to zero (but why is it needed ?????????????????????????????? :)
DWORD len = GetFileVersionInfoSize((TCHAR*)fn, &d);
if(len)
{
TCHAR* b1 = new TCHAR[len];
if(b1)
{
UINT uLen;
if(GetFileVersionInfo((TCHAR*)fn, 0, len, b1) && VerQueryValue(b1, _T("\\"), (void**)&pvsf, &uLen))
{
ret = ((unsigned __int64)pvsf->dwFileVersionMS<<32) | pvsf->dwFileVersionLS;
}
delete [] b1;
}
}
return ret;
}
bool CreateFilter(CStringW DisplayName, IBaseFilter** ppBF, CStringW& FriendlyName)
{
if(!ppBF) return(false);
*ppBF = NULL;
FriendlyName.Empty();
CComPtr<IBindCtx> pBindCtx;
CreateBindCtx(0, &pBindCtx);
CComPtr<IMoniker> pMoniker;
ULONG chEaten;
if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
return(false);
if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)ppBF)) || !*ppBF)
return(false);
CComPtr<IPropertyBag> pPB;
CComVariant var;
if(SUCCEEDED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB))
&& SUCCEEDED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
FriendlyName = var.bstrVal;
return(true);
}
IBaseFilter* AppendFilter(IPin* pPin, IMoniker* pMoniker, IGraphBuilder* pGB)
{
do
{
if(!pPin || !pMoniker || !pGB)
break;
CComPtr<IPin> pPinTo;
PIN_DIRECTION dir;
if(FAILED(pPin->QueryDirection(&dir)) || dir != PINDIR_OUTPUT || SUCCEEDED(pPin->ConnectedTo(&pPinTo)))
break;
CComPtr<IBindCtx> pBindCtx;
CreateBindCtx(0, &pBindCtx);
CComPtr<IPropertyBag> pPB;
if(FAILED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB)))
break;
CComVariant var;
if(FAILED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
break;
CComPtr<IBaseFilter> pBF;
if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
break;
if(FAILED(pGB->AddFilter(pBF, CStringW(var.bstrVal))))
break;
BeginEnumPins(pBF, pEP, pPinTo)
{
PIN_DIRECTION dir;
if(FAILED(pPinTo->QueryDirection(&dir)) || dir != PINDIR_INPUT)
continue;
if(SUCCEEDED(pGB->ConnectDirect(pPin, pPinTo, NULL)))
return(pBF);
}
EndEnumFilters
pGB->RemoveFilter(pBF);
}
while(false);
return(NULL);
}
CStringW GetFriendlyName(CStringW DisplayName)
{
CStringW FriendlyName;
CComPtr<IBindCtx> pBindCtx;
CreateBindCtx(0, &pBindCtx);
CComPtr<IMoniker> pMoniker;
ULONG chEaten;
if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
return(false);
CComPtr<IPropertyBag> pPB;
CComVariant var;
if(SUCCEEDED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB))
&& SUCCEEDED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
FriendlyName = var.bstrVal;
return FriendlyName;
}
typedef struct
{
CString path;
HINSTANCE hInst;
CLSID clsid;
} ExternalObject;
static CAtlList<ExternalObject> s_extobjs;
HRESULT LoadExternalObject(LPCTSTR path, REFCLSID clsid, REFIID iid, void** ppv)
{
CheckPointer(ppv, E_POINTER);
CString fullpath = MakeFullPath(path);
HINSTANCE hInst = NULL;
bool fFound = false;
POSITION pos = s_extobjs.GetHeadPosition();
while(pos)
{
ExternalObject& eo = s_extobjs.GetNext(pos);
if(!eo.path.CompareNoCase(fullpath))
{
hInst = eo.hInst;
fFound = true;
break;
}
}
HRESULT hr = E_FAIL;
if(hInst || (hInst = CoLoadLibrary(CComBSTR(fullpath), TRUE)))
{
typedef HRESULT (__stdcall * PDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
PDllGetClassObject p = (PDllGetClassObject)GetProcAddress(hInst, "DllGetClassObject");
if(p && FAILED(hr = p(clsid, iid, ppv)))
{
CComPtr<IClassFactory> pCF;
if(SUCCEEDED(hr = p(clsid, __uuidof(IClassFactory), (void**)&pCF)))
{
hr = pCF->CreateInstance(NULL, iid, ppv);
}
}
}
if(FAILED(hr) && hInst && !fFound)
{
CoFreeLibrary(hInst);
return hr;
}
if(hInst && !fFound)
{
ExternalObject eo;
eo.path = fullpath;
eo.hInst = hInst;
eo.clsid = clsid;
s_extobjs.AddTail(eo);
}
return hr;
}
HRESULT LoadExternalFilter(LPCTSTR path, REFCLSID clsid, IBaseFilter** ppBF)
{
return LoadExternalObject(path, clsid, __uuidof(IBaseFilter), (void**)ppBF);
}
HRESULT LoadExternalPropertyPage(IPersist* pP, REFCLSID clsid, IPropertyPage** ppPP)
{
CLSID clsid2 = GUID_NULL;
if(FAILED(pP->GetClassID(&clsid2))) return E_FAIL;
POSITION pos = s_extobjs.GetHeadPosition();
while(pos)
{
ExternalObject& eo = s_extobjs.GetNext(pos);
if(eo.clsid == clsid2)
{
return LoadExternalObject(eo.path, clsid, __uuidof(IPropertyPage), (void**)ppPP);
}
}
return E_FAIL;
}
void UnloadExternalObjects()
{
POSITION pos = s_extobjs.GetHeadPosition();
while(pos)
{
ExternalObject& eo = s_extobjs.GetNext(pos);
CoFreeLibrary(eo.hInst);
}
s_extobjs.RemoveAll();
}
CString MakeFullPath(LPCTSTR path)
{
CString full(path);
full.Replace('/', '\\');
CString fn;
fn.ReleaseBuffer(GetModuleFileName(AfxGetInstanceHandle(), fn.GetBuffer(MAX_PATH), MAX_PATH));
CPath p(fn);
if(full.GetLength() >= 2 && full[0] == '\\' && full[1] != '\\')
{
p.StripToRoot();
full = CString(p) + full.Mid(1);
}
else if(full.Find(_T(":\\")) < 0)
{
p.RemoveFileSpec();
p.AddBackslash();
full = CString(p) + full;
}
CPath c(full);
c.Canonicalize();
return CString(c);
}
//
CString GetMediaTypeName(const GUID& guid)
{
CString ret = guid == GUID_NULL
? _T("Any type")
: CString(GuidNames[guid]);
if(ret == _T("FOURCC GUID"))
{
CString str;
if(guid.Data1 >= 0x10000)
str.Format(_T("Video: %c%c%c%c"), (guid.Data1>>0)&0xff, (guid.Data1>>8)&0xff, (guid.Data1>>16)&0xff, (guid.Data1>>24)&0xff);
else
str.Format(_T("Audio: 0x%08x"), guid.Data1);
ret = str;
}
else if(ret == _T("Unknown GUID Name"))
{
WCHAR null[128] = {0}, buff[128];
StringFromGUID2(GUID_NULL, null, 127);
ret = CString(CStringW(StringFromGUID2(guid, buff, 127) ? buff : null));
}
return ret;
}
GUID GUIDFromCString(CString str)
{
GUID guid = GUID_NULL;
HRESULT hr = CLSIDFromString(CComBSTR(str), &guid);
ASSERT(SUCCEEDED(hr));
return guid;
}
HRESULT GUIDFromCString(CString str, GUID& guid)
{
guid = GUID_NULL;
return CLSIDFromString(CComBSTR(str), &guid);
}
CString CStringFromGUID(const GUID& guid)
{
WCHAR null[128] = {0}, buff[128];
StringFromGUID2(GUID_NULL, null, 127);
return CString(StringFromGUID2(guid, buff, 127) > 0 ? buff : null);
}
CStringW UTF8To16(LPCSTR utf8)
{
CStringW str;
int n = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0)-1;
if(n < 0) return str;
str.ReleaseBuffer(MultiByteToWideChar(CP_UTF8, 0, utf8, -1, str.GetBuffer(n), n+1)-1);
return str;
}
CStringA UTF16To8(LPCWSTR utf16)
{
CStringA str;
int n = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL, 0, NULL, NULL)-1;
if(n < 0) return str;
str.ReleaseBuffer(WideCharToMultiByte(CP_UTF8, 0, utf16, -1, str.GetBuffer(n), n+1, NULL, NULL)-1);
return str;
}
static struct {LPCSTR name, iso6392, iso6391;} s_isolangs[] =
{
{"Abkhazian", "abk", "ab"},
{"Achinese", "ace", ""},
{"Acoli", "ach", ""},
{"Adangme", "ada", ""},
{"Afar", "aar", "aa"},
{"Afrihili", "afh", ""},
{"Afrikaans", "afr", "af"},
{"Afro-Asiatic (Other)", "afa", ""},
{"Akan", "aka", "ak"},
{"Akkadian", "akk", ""},
{"Albanian", "alb", "sq"},
{"Albanian", "sqi", "sq"},
{"Aleut", "ale", ""},
{"Algonquian languages", "alg", ""},
{"Altaic (Other)", "tut", ""},
{"Amharic", "amh", "am"},
{"Apache languages", "apa", ""},
{"Arabic", "ara", "ar"},
{"Aragonese", "arg", "an"},
{"Aramaic", "arc", ""},
{"Arapaho", "arp", ""},
{"Araucanian", "arn", ""},
{"Arawak", "arw", ""},
{"Armenian", "arm", "hy"},
{"Armenian", "hye", "hy"},
{"Artificial (Other)", "art", ""},
{"Assamese", "asm", "as"},
{"Asturian; Bable", "ast", ""},
{"Athapascan languages", "ath", ""},
{"Australian languages", "aus", ""},
{"Austronesian (Other)", "map", ""},
{"Avaric", "ava", "av"},
{"Avestan", "ave", "ae"},
{"Awadhi", "awa", ""},
{"Aymara", "aym", "ay"},
{"Azerbaijani", "aze", "az"},
{"Bable; Asturian", "ast", ""},
{"Balinese", "ban", ""},
{"Baltic (Other)", "bat", ""},
{"Baluchi", "bal", ""},
{"Bambara", "bam", "bm"},
{"Bamileke languages", "bai", ""},
{"Banda", "bad", ""},
{"Bantu (Other)", "bnt", ""},
{"Basa", "bas", ""},
{"Bashkir", "bak", "ba"},
{"Basque", "baq", "eu"},
{"Basque", "eus", "eu"},
{"Batak (Indonesia)", "btk", ""},
{"Beja", "bej", ""},
{"Belarusian", "bel", "be"},
{"Bemba", "bem", ""},
{"Bengali", "ben", "bn"},
{"Berber (Other)", "ber", ""},
{"Bhojpuri", "bho", ""},
{"Bihari", "bih", "bh"},
{"Bikol", "bik", ""},
{"Bini", "bin", ""},
{"Bislama", "bis", "bi"},
{"Bokm<EFBFBD>l, Norwegian; Norwegian Bokm<6B>l", "nob", "nb"},
{"Bosnian", "bos", "bs"},
{"Braj", "bra", ""},
{"Breton", "bre", "br"},
{"Buginese", "bug", ""},
{"Bulgarian", "bul", "bg"},
{"Buriat", "bua", ""},
{"Burmese", "bur", "my"},
{"Burmese", "mya", "my"},
{"Caddo", "cad", ""},
{"Carib", "car", ""},
{"Spanish; Castilian", "spa", "es"},
{"Catalan", "cat", "ca"},
{"Caucasian (Other)", "cau", ""},
{"Cebuano", "ceb", ""},
{"Celtic (Other)", "cel", ""},
{"Central American Indian (Other)", "cai", ""},
{"Chagatai", "chg", ""},
{"Chamic languages", "cmc", ""},
{"Chamorro", "cha", "ch"},
{"Chechen", "che", "ce"},
{"Cherokee", "chr", ""},
{"Chewa; Chichewa; Nyanja", "nya", "ny"},
{"Cheyenne", "chy", ""},
{"Chibcha", "chb", ""},
{"Chichewa; Chewa; Nyanja", "nya", "ny"},
{"Chinese", "chi", "zh"},
{"Chinese", "zho", "zh"},
{"Chinook jargon", "chn", ""},
{"Chipewyan", "chp", ""},
{"Choctaw", "cho", ""},
{"Chuang; Zhuang", "zha", "za"},
{"Church Slavic; Old Church Slavonic", "chu", "cu"},
{"Old Church Slavonic; Old Slavonic; ", "chu", "cu"},
{"Church Slavonic; Old Bulgarian; Church Slavic;", "chu", "cu"},
{"Old Slavonic; Church Slavonic; Old Bulgarian;", "chu", "cu"},
{"Church Slavic; Old Church Slavonic", "chu", "cu"},
{"Chuukese", "chk", ""},
{"Chuvash", "chv", "cv"},
{"Coptic", "cop", ""},
{"Cornish", "cor", "kw"},
{"Corsican", "cos", "co"},
{"Cree", "cre", "cr"},
{"Creek", "mus", ""},
{"Creoles and pidgins (Other)", "crp", ""},
{"Creoles and pidgins,", "cpe", ""},
// {"English-based (Other)", "", ""},
{"Creoles and pidgins,", "cpf", ""},
// {"French-based (Other)", "", ""},
{"Creoles and pidgins,", "cpp", ""},
// {"Portuguese-based (Other)", "", ""},
{"Croatian", "scr", "hr"},
{"Croatian", "hrv", "hr"},
{"Cushitic (Other)", "cus", ""},
{"Czech", "cze", "cs"},
{"Czech", "ces", "cs"},
{"Dakota", "dak", ""},
{"Danish", "dan", "da"},
{"Dargwa", "dar", ""},
{"Dayak", "day", ""},
{"Delaware", "del", ""},
{"Dinka", "din", ""},
{"Divehi", "div", "dv"},
{"Dogri", "doi", ""},
{"Dogrib", "dgr", ""},
{"Dravidian (Other)", "dra", ""},
{"Duala", "dua", ""},
{"Dutch; Flemish", "dut", "nl"},
{"Dutch; Flemish", "nld", "nl"},
{"Dutch, Middle (ca. 1050-1350)", "dum", ""},
{"Dyula", "dyu", ""},
{"Dzongkha", "dzo", "dz"},
{"Efik", "efi", ""},
{"Egyptian (Ancient)", "egy", ""},
{"Ekajuk", "eka", ""},
{"Elamite", "elx", ""},
{"English", "eng", "en"},
{"English, Middle (1100-1500)", "enm", ""},
{"English, Old (ca.450-1100)", "ang", ""},
{"Esperanto", "epo", "eo"},
{"Estonian", "est", "et"},
{"Ewe", "ewe", "ee"},
{"Ewondo", "ewo", ""},
{"Fang", "fan", ""},
{"Fanti", "fat", ""},
{"Faroese", "fao", "fo"},
{"Fijian", "fij", "fj"},
{"Finnish", "fin", "fi"},
{"Finno-Ugrian (Other)", "fiu", ""},
{"Flemish; Dutch", "dut", "nl"},
{"Flemish; Dutch", "nld", "nl"},
{"Fon", "fon", ""},
{"French", "fre", "fr"},
{"French", "fra*", "fr"},
{"French, Middle (ca.1400-1600)", "frm", ""},
{"French, Old (842-ca.1400)", "fro", ""},
{"Frisian", "fry", "fy"},
{"Friulian", "fur", ""},
{"Fulah", "ful", "ff"},
{"Ga", "gaa", ""},
{"Gaelic; Scottish Gaelic", "gla", "gd"},
{"Gallegan", "glg", "gl"},
{"Ganda", "lug", "lg"},
{"Gayo", "gay", ""},
{"Gbaya", "gba", ""},
{"Geez", "gez", ""},
{"Georgian", "geo", "ka"},
{"Georgian", "kat", "ka"},
{"German", "ger", "de"},
{"German", "deu", "de"},
{"German, Low; Saxon, Low; Low German; Low Saxon", "nds", ""},
{"German, Middle High (ca.1050-1500)", "gmh", ""},
{"German, Old High (ca.750-1050)", "goh", ""},
{"Germanic (Other)", "gem", ""},
{"Gikuyu; Kikuyu", "kik", "ki"},
{"Gilbertese", "gil", ""},
{"Gondi", "gon", ""},
{"Gorontalo", "gor", ""},
{"Gothic", "got", ""},
{"Grebo", "grb", ""},
{"Greek, Ancient (to 1453)", "grc", ""},
{"Greek, Modern (1453-)", "gre", "el"},
{"Greek, Modern (1453-)", "ell", "el"},
{"Greenlandic; Kalaallisut", "kal", "kl"},
{"Guarani", "grn", "gn"},
{"Gujarati", "guj", "gu"},
{"Gwich<EFBFBD>in", "gwi", ""},
{"Haida", "hai", ""},
{"Hausa", "hau", "ha"},
{"Hawaiian", "haw", ""},
{"Hebrew", "heb", "he"},
{"Herero", "her", "hz"},
{"Hiligaynon", "hil", ""},
{"Himachali", "him", ""},
{"Hindi", "hin", "hi"},
{"Hiri Motu", "hmo", "ho"},
{"Hittite", "hit", ""},
{"Hmong", "hmn", ""},
{"Hungarian", "hun", "hu"},
{"Hupa", "hup", ""},
{"Iban", "iba", ""},
{"Icelandic", "ice", "is"},
{"Icelandic", "isl", "is"},
{"Ido", "ido", "io"},
{"Igbo", "ibo", "ig"},
{"Ijo", "ijo", ""},
{"Iloko", "ilo", ""},
{"Inari Sami", "smn", ""},
{"Indic (Other)", "inc", ""},
{"Indo-European (Other)", "ine", ""},
{"Indonesian", "ind", "id"},
{"Ingush", "inh", ""},
{"Interlingua (International", "ina", "ia"},
// {"Auxiliary Language Association)", "", ""},
{"Interlingue", "ile", "ie"},
{"Inuktitut", "iku", "iu"},
{"Inupiaq", "ipk", "ik"},
{"Iranian (Other)", "ira", ""},
{"Irish", "gle", "ga"},
{"Irish, Middle (900-1200)", "mga", ""},
{"Irish, Old (to 900)", "sga", ""},
{"Iroquoian languages", "iro", ""},
{"Italian", "ita", "it"},
{"Japanese", "jpn", "ja"},
{"Javanese", "jav", "jv"},
{"Judeo-Arabic", "jrb", ""},
{"Judeo-Persian", "jpr", ""},
{"Kabardian", "kbd", ""},
{"Kabyle", "kab", ""},
{"Kachin", "kac", ""},
{"Kalaallisut; Greenlandic", "kal", "kl"},
{"Kamba", "kam", ""},
{"Kannada", "kan", "kn"},
{"Kanuri", "kau", "kr"},
{"Kara-Kalpak", "kaa", ""},
{"Karen", "kar", ""},
{"Kashmiri", "kas", "ks"},
{"Kawi", "kaw", ""},
{"Kazakh", "kaz", "kk"},
{"Khasi", "kha", ""},
{"Khmer", "khm", "km"},
{"Khoisan (Other)", "khi", ""},
{"Khotanese", "kho", ""},
{"Kikuyu; Gikuyu", "kik", "ki"},
{"Kimbundu", "kmb", ""},
{"Kinyarwanda", "kin", "rw"},
{"Kirghiz", "kir", "ky"},
{"Komi", "kom", "kv"},
{"Kongo", "kon", "kg"},
{"Konkani", "kok", ""},
{"Korean", "kor", "ko"},
{"Kosraean", "kos", ""},
{"Kpelle", "kpe", ""},
{"Kru", "kro", ""},
{"Kuanyama; Kwanyama", "kua", "kj"},
{"Kumyk", "kum", ""},
{"Kurdish", "kur", "ku"},
{"Kurukh", "kru", ""},
{"Kutenai", "kut", ""},
{"Kwanyama, Kuanyama", "kua", "kj"},
{"Ladino", "lad", ""},
{"Lahnda", "lah", ""},
{"Lamba", "lam", ""},
{"Lao", "lao", "lo"},
{"Latin", "lat", "la"},
{"Latvian", "lav", "lv"},
{"Letzeburgesch; Luxembourgish", "ltz", "lb"},
{"Lezghian", "lez", ""},
{"Limburgan; Limburger; Limburgish", "lim", "li"},
{"Limburger; Limburgan; Limburgish;", "lim", "li"},
{"Limburgish; Limburger; Limburgan", "lim", "li"},
{"Lingala", "lin", "ln"},
{"Lithuanian", "lit", "lt"},
{"Low German; Low Saxon; German, Low; Saxon, Low", "nds", ""},
{"Low Saxon; Low German; Saxon, Low; German, Low", "nds", ""},
{"Lozi", "loz", ""},
{"Luba-Katanga", "lub", "lu"},
{"Luba-Lulua", "lua", ""},
{"Luiseno", "lui", ""},
{"Lule Sami", "smj", ""},
{"Lunda", "lun", ""},
{"Luo (Kenya and Tanzania)", "luo", ""},
{"Lushai", "lus", ""},
{"Luxembourgish; Letzeburgesch", "ltz", "lb"},
{"Macedonian", "mac", "mk"},
{"Macedonian", "mkd", "mk"},
{"Madurese", "mad", ""},
{"Magahi", "mag", ""},
{"Maithili", "mai", ""},
{"Makasar", "mak", ""},
{"Malagasy", "mlg", "mg"},
{"Malay", "may", "ms"},
{"Malay", "msa", "ms"},
{"Malayalam", "mal", "ml"},
{"Maltese", "mlt", "mt"},
{"Manchu", "mnc", ""},
{"Mandar", "mdr", ""},
{"Mandingo", "man", ""},
{"Manipuri", "mni", ""},
{"Manobo languages", "mno", ""},
{"Manx", "glv", "gv"},
{"Maori", "mao", "mi"},
{"Maori", "mri", "mi"},
{"Marathi", "mar", "mr"},
{"Mari", "chm", ""},
{"Marshallese", "mah", "mh"},
{"Marwari", "mwr", ""},
{"Masai", "mas", ""},
{"Mayan languages", "myn", ""},
{"Mende", "men", ""},
{"Micmac", "mic", ""},
{"Minangkabau", "min", ""},
{"Miscellaneous languages", "mis", ""},
{"Mohawk", "moh", ""},
{"Moldavian", "mol", "mo"},
{"Mon-Khmer (Other)", "mkh", ""},
{"Mongo", "lol", ""},
{"Mongolian", "mon", "mn"},
{"Mossi", "mos", ""},
{"Multiple languages", "mul", ""},
{"Munda languages", "mun", ""},
{"Nahuatl", "nah", ""},
{"Nauru", "nau", "na"},
{"Navaho, Navajo", "nav", "nv"},
{"Navajo; Navaho", "nav", "nv"},
{"Ndebele, North", "nde", "nd"},
{"Ndebele, South", "nbl", "nr"},
{"Ndonga", "ndo", "ng"},
{"Neapolitan", "nap", ""},
{"Nepali", "nep", "ne"},
{"Newari", "new", ""},
{"Nias", "nia", ""},
{"Niger-Kordofanian (Other)", "nic", ""},
{"Nilo-Saharan (Other)", "ssa", ""},
{"Niuean", "niu", ""},
{"Norse, Old", "non", ""},
{"North American Indian (Other)", "nai", ""},
{"Northern Sami", "sme", "se"},
{"North Ndebele", "nde", "nd"},
{"Norwegian", "nor", "no"},
{"Norwegian Bokm<6B>l; Bokm<6B>l, Norwegian", "nob", "nb"},
{"Norwegian Nynorsk; Nynorsk, Norwegian", "nno", "nn"},
{"Nubian languages", "nub", ""},
{"Nyamwezi", "nym", ""},
{"Nyanja; Chichewa; Chewa", "nya", "ny"},
{"Nyankole", "nyn", ""},
{"Nynorsk, Norwegian; Norwegian Nynorsk", "nno", "nn"},
{"Nyoro", "nyo", ""},
{"Nzima", "nzi", ""},
{"Occitan (post 1500},; Proven<65>al", "oci", "oc"},
{"Ojibwa", "oji", "oj"},
{"Old Bulgarian; Old Slavonic; Church Slavonic;", "chu", "cu"},
{"Oriya", "ori", "or"},
{"Oromo", "orm", "om"},
{"Osage", "osa", ""},
{"Ossetian; Ossetic", "oss", "os"},
{"Ossetic; Ossetian", "oss", "os"},
{"Otomian languages", "oto", ""},
{"Pahlavi", "pal", ""},
{"Palauan", "pau", ""},
{"Pali", "pli", "pi"},
{"Pampanga", "pam", ""},
{"Pangasinan", "pag", ""},
{"Panjabi", "pan", "pa"},
{"Papiamento", "pap", ""},
{"Papuan (Other)", "paa", ""},
{"Persian", "per", "fa"},
{"Persian", "fas", "fa"},
{"Persian, Old (ca.600-400 B.C.)", "peo", ""},
{"Philippine (Other)", "phi", ""},
{"Phoenician", "phn", ""},
{"Pohnpeian", "pon", ""},
{"Polish", "pol", "pl"},
{"Portuguese", "por", "pt"},
{"Prakrit languages", "pra", ""},
{"Proven<EFBFBD>al; Occitan (post 1500)", "oci", "oc"},
{"Proven<EFBFBD>al, Old (to 1500)", "pro", ""},
{"Pushto", "pus", "ps"},
{"Quechua", "que", "qu"},
{"Raeto-Romance", "roh", "rm"},
{"Rajasthani", "raj", ""},
{"Rapanui", "rap", ""},
{"Rarotongan", "rar", ""},
{"Reserved for local use", "qaa-qtz", ""},
{"Romance (Other)", "roa", ""},
{"Romanian", "rum", "ro"},
{"Romanian", "ron", "ro"},
{"Romany", "rom", ""},
{"Rundi", "run", "rn"},
{"Russian", "rus", "ru"},
{"Salishan languages", "sal", ""},
{"Samaritan Aramaic", "sam", ""},
{"Sami languages (Other)", "smi", ""},
{"Samoan", "smo", "sm"},
{"Sandawe", "sad", ""},
{"Sango", "sag", "sg"},
{"Sanskrit", "san", "sa"},
{"Santali", "sat", ""},
{"Sardinian", "srd", "sc"},
{"Sasak", "sas", ""},
{"Saxon, Low; German, Low; Low Saxon; Low German", "nds", ""},
{"Scots", "sco", ""},
{"Scottish Gaelic; Gaelic", "gla", "gd"},
{"Selkup", "sel", ""},
{"Semitic (Other)", "sem", ""},
{"Serbian", "scc", "sr"},
{"Serbian", "srp", "sr"},
{"Serer", "srr", ""},
{"Shan", "shn", ""},
{"Shona", "sna", "sn"},
{"Sichuan Yi", "iii", "ii"},
{"Sidamo", "sid", ""},
{"Sign languages", "sgn", ""},
{"Siksika", "bla", ""},
{"Sindhi", "snd", "sd"},
{"Sinhalese", "sin", "si"},
{"Sino-Tibetan (Other)", "sit", ""},
{"Siouan languages", "sio", ""},
{"Skolt Sami", "sms", ""},
{"Slave (Athapascan)", "den", ""},
{"Slavic (Other)", "sla", ""},
{"Slovak", "slo", "sk"},
{"Slovak", "slk", "sk"},
{"Slovenian", "slv", "sl"},
{"Sogdian", "sog", ""},
{"Somali", "som", "so"},
{"Songhai", "son", ""},
{"Soninke", "snk", ""},
{"Sorbian languages", "wen", ""},
{"Sotho, Northern", "nso", ""},
{"Sotho, Southern", "sot", "st"},
{"South American Indian (Other)", "sai", ""},
{"Southern Sami", "sma", ""},
{"South Ndebele", "nbl", "nr"},
{"Spanish; Castilian", "spa", "es"},
{"Sukuma", "suk", ""},
{"Sumerian", "sux", ""},
{"Sundanese", "sun", "su"},
{"Susu", "sus", ""},
{"Swahili", "swa", "sw"},
{"Swati", "ssw", "ss"},
{"Swedish", "swe", "sv"},
{"Syriac", "syr", ""},
{"Tagalog", "tgl", "tl"},
{"Tahitian", "tah", "ty"},
{"Tai (Other)", "tai", ""},
{"Tajik", "tgk", "tg"},
{"Tamashek", "tmh", ""},
{"Tamil", "tam", "ta"},
{"Tatar", "tat", "tt"},
{"Telugu", "tel", "te"},
{"Tereno", "ter", ""},
{"Tetum", "tet", ""},
{"Thai", "tha", "th"},
{"Tibetan", "tib", "bo"},
{"Tibetan", "bod", "bo"},
{"Tigre", "tig", ""},
{"Tigrinya", "tir", "ti"},
{"Timne", "tem", ""},
{"Tiv", "tiv", ""},
{"Tlingit", "tli", ""},
{"Tok Pisin", "tpi", ""},
{"Tokelau", "tkl", ""},
{"Tonga (Nyasa)", "tog", ""},
{"Tonga (Tonga Islands)", "ton", "to"},
{"Tsimshian", "tsi", ""},
{"Tsonga", "tso", "ts"},
{"Tswana", "tsn", "tn"},
{"Tumbuka", "tum", ""},
{"Tupi languages", "tup", ""},
{"Turkish", "tur", "tr"},
{"Turkish, Ottoman (1500-1928)", "ota", ""},
{"Turkmen", "tuk", "tk"},
{"Tuvalu", "tvl", ""},
{"Tuvinian", "tyv", ""},
{"Twi", "twi", "tw"},
{"Ugaritic", "uga", ""},
{"Uighur", "uig", "ug"},
{"Ukrainian", "ukr", "uk"},
{"Umbundu", "umb", ""},
{"Undetermined", "und", ""},
{"Urdu", "urd", "ur"},
{"Uzbek", "uzb", "uz"},
{"Vai", "vai", ""},
{"Venda", "ven", "ve"},
{"Vietnamese", "vie", "vi"},
{"Volap<EFBFBD>k", "vol", "vo"},
{"Votic", "vot", ""},
{"Wakashan languages", "wak", ""},
{"Walamo", "wal", ""},
{"Walloon", "wln", "wa"},
{"Waray", "war", ""},
{"Washo", "was", ""},
{"Welsh", "wel", "cy"},
{"Welsh", "cym", "cy"},
{"Wolof", "wol", "wo"},
{"Xhosa", "xho", "xh"},
{"Yakut", "sah", ""},
{"Yao", "yao", ""},
{"Yapese", "yap", ""},
{"Yiddish", "yid", "yi"},
{"Yoruba", "yor", "yo"},
{"Yupik languages", "ypk", ""},
{"Zande", "znd", ""},
{"Zapotec", "zap", ""},
{"Zenaga", "zen", ""},
{"Zhuang; Chuang", "zha", "za"},
{"Zulu", "zul", "zu"},
{"Zuni", "zun", ""},
{"Classical Newari", "nwc", ""},
{"Klingon", "tlh", ""},
{"Blin", "byn", ""},
{"Lojban", "jbo", ""},
{"Lower Sorbian", "dsb", ""},
{"Upper Sorbian", "hsb", ""},
{"Kashubian", "csb", ""},
{"Crimean Turkish", "crh", ""},
{"Erzya", "myv", ""},
{"Moksha", "mdf", ""},
{"Karachay-Balkar", "krc", ""},
{"Adyghe", "ady", ""},
{"Udmurt", "udm", ""},
{"Dargwa", "dar", ""},
{"Ingush", "inh", ""},
{"Nogai", "nog", ""},
{"Haitian", "hat", "ht"},
{"Kalmyk", "xal", ""},
{"", "", ""},
};
CString ISO6391ToLanguage(LPCSTR code)
{
CHAR tmp[2+1];
strncpy(tmp, code, 2);
tmp[2] = 0;
_strlwr(tmp);
for(int i = 0, j = countof(s_isolangs); i < j; i++)
if(!strcmp(s_isolangs[i].iso6391, tmp))
{
CString ret = CString(CStringA(s_isolangs[i].name));
int i = ret.Find(';');
if(i > 0) ret = ret.Left(i);
return ret;
}
return(_T(""));
}
CString ISO6392ToLanguage(LPCSTR code)
{
CHAR tmp[3+1];
strncpy(tmp, code, 3);
tmp[3] = 0;
_strlwr(tmp);
for(int i = 0, j = countof(s_isolangs); i < j; i++)
{
if(!strcmp(s_isolangs[i].iso6392, tmp))
{
CString ret = CString(CStringA(s_isolangs[i].name));
int i = ret.Find(';');
if(i > 0) ret = ret.Left(i);
return ret;
}
}
return _T("");
}
CString ISO6391To6392(LPCSTR code)
{
CHAR tmp[2+1];
strncpy(tmp, code, 2);
tmp[2] = 0;
_strlwr(tmp);
for(int i = 0, j = countof(s_isolangs); i < j; i++)
if(!strcmp(s_isolangs[i].iso6391, tmp))
return CString(CStringA(s_isolangs[i].iso6392));
return _T("");
}
CString ISO6392To6391(LPCSTR code)
{
CHAR tmp[3+1];
strncpy(tmp, code, 3);
tmp[3] = 0;
_strlwr(tmp);
for(int i = 0, j = countof(s_isolangs); i < j; i++)
if(!strcmp(s_isolangs[i].iso6392, tmp))
return CString(CStringA(s_isolangs[i].iso6391));
return _T("");
}
CString LanguageToISO6392(LPCTSTR lang)
{
CString str = lang;
str.MakeLower();
for(int i = 0, j = countof(s_isolangs); i < j; i++)
{
CAtlList<CString> sl;
Explode(CString(s_isolangs[i].name), sl, ';');
POSITION pos = sl.GetHeadPosition();
while(pos)
{
if(!str.CompareNoCase(sl.GetNext(pos)))
return CString(s_isolangs[i].iso6392);
}
}
return _T("");
}
int MakeAACInitData(BYTE* pData, int profile, int freq, int channels)
{
int srate_idx;
if(92017 <= freq) srate_idx = 0;
else if(75132 <= freq) srate_idx = 1;
else if(55426 <= freq) srate_idx = 2;
else if(46009 <= freq) srate_idx = 3;
else if(37566 <= freq) srate_idx = 4;
else if(27713 <= freq) srate_idx = 5;
else if(23004 <= freq) srate_idx = 6;
else if(18783 <= freq) srate_idx = 7;
else if(13856 <= freq) srate_idx = 8;
else if(11502 <= freq) srate_idx = 9;
else if(9391 <= freq) srate_idx = 10;
else srate_idx = 11;
pData[0] = ((abs(profile) + 1) << 3) | ((srate_idx & 0xe) >> 1);
pData[1] = ((srate_idx & 0x1) << 7) | (channels << 3);
int ret = 2;
if(profile < 0)
{
freq *= 2;
if(92017 <= freq) srate_idx = 0;
else if(75132 <= freq) srate_idx = 1;
else if(55426 <= freq) srate_idx = 2;
else if(46009 <= freq) srate_idx = 3;
else if(37566 <= freq) srate_idx = 4;
else if(27713 <= freq) srate_idx = 5;
else if(23004 <= freq) srate_idx = 6;
else if(18783 <= freq) srate_idx = 7;
else if(13856 <= freq) srate_idx = 8;
else if(11502 <= freq) srate_idx = 9;
else if(9391 <= freq) srate_idx = 10;
else srate_idx = 11;
pData[2] = 0x2B7>>3;
pData[3] = (BYTE)((0x2B7<<5) | 5);
pData[4] = (1<<7) | (srate_idx<<3);
ret = 5;
}
return(ret);
}
BOOL CFileGetStatus(LPCTSTR lpszFileName, CFileStatus& status)
{
try
{
return CFile::GetStatus(lpszFileName, status);
}
catch(CException* e)
{
// MFCBUG: E_INVALIDARG / "Parameter is incorrect" is thrown for certain cds (vs2003)
// http://groups.google.co.uk/groups?hl=en&lr=&ie=UTF-8&threadm=OZuXYRzWDHA.536%40TK2MSFTNGP10.phx.gbl&rnum=1&prev=/groups%3Fhl%3Den%26lr%3D%26ie%3DISO-8859-1
TRACE(_T("CFile::GetStatus has thrown an exception\n"));
e->Delete();
return false;
}
}
// filter registration helpers
bool DeleteRegKey(LPCTSTR pszKey, LPCTSTR pszSubkey)
{
bool bOK = false;
HKEY hKey;
LONG ec = ::RegOpenKeyEx(HKEY_CLASSES_ROOT, pszKey, 0, KEY_ALL_ACCESS, &hKey);
if(ec == ERROR_SUCCESS)
{
if(pszSubkey != 0)
ec = ::RegDeleteKey(hKey, pszSubkey);
bOK = (ec == ERROR_SUCCESS);
::RegCloseKey(hKey);
}
return bOK;
}
bool SetRegKeyValue(LPCTSTR pszKey, LPCTSTR pszSubkey, LPCTSTR pszValueName, LPCTSTR pszValue)
{
bool bOK = false;
CString szKey(pszKey);
if(pszSubkey != 0)
szKey += CString(_T("\\")) + pszSubkey;
HKEY hKey;
LONG ec = ::RegCreateKeyEx(HKEY_CLASSES_ROOT, szKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, 0);
if(ec == ERROR_SUCCESS)
{
if(pszValue != 0)
{
ec = ::RegSetValueEx(hKey, pszValueName, 0, REG_SZ,
reinterpret_cast<BYTE*>(const_cast<LPTSTR>(pszValue)),
(_tcslen(pszValue) + 1) * sizeof(TCHAR));
}
bOK = (ec == ERROR_SUCCESS);
::RegCloseKey(hKey);
}
return bOK;
}
bool SetRegKeyValue(LPCTSTR pszKey, LPCTSTR pszSubkey, LPCTSTR pszValue)
{
return SetRegKeyValue(pszKey, pszSubkey, 0, pszValue);
}
void RegisterSourceFilter(const CLSID& clsid, const GUID& subtype2, LPCTSTR chkbytes, LPCTSTR ext, ...)
{
CString null = CStringFromGUID(GUID_NULL);
CString majortype = CStringFromGUID(MEDIATYPE_Stream);
CString subtype = CStringFromGUID(subtype2);
SetRegKeyValue(_T("Media Type\\") + majortype, subtype, _T("0"), chkbytes);
SetRegKeyValue(_T("Media Type\\") + majortype, subtype, _T("Source Filter"), CStringFromGUID(clsid));
DeleteRegKey(_T("Media Type\\") + null, subtype);
va_list marker;
va_start(marker, ext);
for(; ext; ext = va_arg(marker, LPCTSTR))
DeleteRegKey(_T("Media Type\\Extensions"), ext);
va_end(marker);
}
void RegisterSourceFilter(const CLSID& clsid, const GUID& subtype2, const CAtlList<CString>& chkbytes, LPCTSTR ext, ...)
{
CString null = CStringFromGUID(GUID_NULL);
CString majortype = CStringFromGUID(MEDIATYPE_Stream);
CString subtype = CStringFromGUID(subtype2);
POSITION pos = chkbytes.GetHeadPosition();
for(int i = 0; pos; i++)
{
CString idx;
idx.Format(_T("%d"), i);
SetRegKeyValue(_T("Media Type\\") + majortype, subtype, idx, chkbytes.GetNext(pos));
}
SetRegKeyValue(_T("Media Type\\") + majortype, subtype, _T("Source Filter"), CStringFromGUID(clsid));
DeleteRegKey(_T("Media Type\\") + null, subtype);
va_list marker;
va_start(marker, ext);
for(; ext; ext = va_arg(marker, LPCTSTR))
DeleteRegKey(_T("Media Type\\Extensions"), ext);
va_end(marker);
}
void UnRegisterSourceFilter(const GUID& subtype)
{
DeleteRegKey(_T("Media Type\\") + CStringFromGUID(MEDIATYPE_Stream), CStringFromGUID(subtype));
}