Source file: /~heha/hs/cdcat1plugin.zip/src/cdcat1.cpp

#include "fsplugin.h"
#include "cunicode.h"
#include <shlwapi.h>	// wnsprinf()
#include "Xml.h"

#define pluginrootlen 1
#define vsnprintf _vsnprintf

HINSTANCE hInstance;
char inifilename[MAX_PATH];  // Unused in this plugin, may be used to save data
volatile bool wantStop;

static void _cdecl debug(char*t,...) {
#ifdef _DEBUG
 char buf[1024];
 va_list va;
 va_start(va,t);
 vsnprintf(buf,elemof(buf),t,va);
 OutputDebugStringA(buf);
 va_end(va);
#endif
}

extern "C" BOOL APIENTRY _DllMainCRTStartup(HINSTANCE h,DWORD reason,void*) {
 debug("DllMain(%p,%d)\n",h,reason);
 switch (reason) {
  case DLL_PROCESS_ATTACH: {
   hInstance=h;
   DisableThreadLibraryCalls(h);
  }break;
 }
 return TRUE;
}

int PluginNumber;
tProgressProc ProgressProc;
tLogProc LogProc;
tRequestProc RequestProc;
tProgressProcW ProgressProcW;
tLogProcW LogProcW;
tRequestProcW RequestProcW;

int WINAPI FsInit(int n,tProgressProc p1,tLogProc p2,tRequestProc p3) {
 PluginNumber=n;
 ProgressProc=p1;
 LogProc=p2;
 RequestProc=p3;
 return 0;
}

int WINAPI FsInitW(int n,tProgressProcW p1,tLogProcW p2,tRequestProcW p3) {
 debug("%s(%d,%p,%p,%p)\n","FsInitW",n,p1,p2,p3);
 PluginNumber=n;
 ProgressProcW=p1;
 LogProcW=p2;
 RequestProcW=p3;
 return 0;
}

struct FindHandle{
 int iItem;
 const Xml::Node*item;
 operator HANDLE() {return HANDLE(this);}
 FindHandle(const Xml::Node*n):item(n),iItem(0) {}
};

struct LoadedFile{
 Xml*xml;	// !=0 when loaded
 int dirty;	// counts number of changes
 DWORD packer_id;
 FILETIME getLastMod() const;
 char displayname[64];	// TODO: Katalogname aus XML-Datei
 char filename[MAX_PATH];
 bool load();
 bool save();
 ~LoadedFile();
 static bool convertFile(HANDLE,HANDLE,const char*);
 static bool unpackFile(HANDLE&,const char*);
 static bool packFile(HANDLE&,const char*);
private:
 HANDLE open(DWORD creation=OPEN_EXISTING) const;
};

LoadedFile::~LoadedFile() {
 if (xml) {
  if (dirty) save();
  delete xml;
 }
}

HANDLE LoadedFile::open(DWORD creation) const{
 int l=MultiByteToWideChar(CP_UTF8,0,filename,-1,0,0);
 WCHAR*n=(WCHAR*)_alloca(l*sizeof*n);
 MultiByteToWideChar(CP_UTF8,0,filename,-1,n,l);
 SECURITY_ATTRIBUTES sa={sizeof sa,0,TRUE};
 return CreateFileW(n,
	creation==OPEN_EXISTING?GENERIC_READ:GENERIC_READ|GENERIC_WRITE,
	FILE_SHARE_READ,&sa,creation,0,0);
}

FILETIME LoadedFile::getLastMod() const{
 union{
  ULONGLONG ull;
  FILETIME ft;
 }ret={-1};
 HANDLE h=open();
 if (h!=INVALID_HANDLE_VALUE) {
  GetFileTime(h,0,0,&ret.ft);
  CloseHandle(h);
 }
 return ret.ft;
}

bool LoadedFile::convertFile(HANDLE hr,HANDLE hw,const char*cmd) {
 int l=MultiByteToWideChar(CP_UTF8,0,cmd,-1,0,0);
 WCHAR*p=(WCHAR*)_alloca(l*sizeof*p);
 MultiByteToWideChar(CP_UTF8,0,cmd,-1,p,l);
 STARTUPINFOW si;
 memset(&si,0,sizeof si);
 si.cb=sizeof si;
 si.dwFlags=STARTF_USESTDHANDLES|STARTF_FORCEOFFFEEDBACK;
// SetHandleInformation(hr,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
// SetHandleInformation(hw,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
 si.hStdInput=hr;
 si.hStdOutput=hw;
 si.hStdError=hw;
 PROCESS_INFORMATION pi;
 if (!CreateProcessW(0,p,0,0,TRUE,CREATE_NO_WINDOW,0,0,&si,&pi)) return false;
 CloseHandle(pi.hThread);
 CloseHandle(pi.hProcess);
 CloseHandle(hr);	// CreateProcess ruft intern DuplicateHandle()
 CloseHandle(hw);
 return true;
}


// Packt Datei im Worker-Thread aus und tauscht das Dateihandle gegen das Read-Pipe-Handle
bool LoadedFile::unpackFile(HANDLE&h,const char*cmd) {
 HANDLE hr, hw;
 SECURITY_ATTRIBUTES sa={sizeof sa,0,TRUE};
 if (!CreatePipe(&hr,&hw,&sa,Xml::BUFSIZE)) return false;
 if (!convertFile(h,hw,cmd)) return false;
 h=hr;
 return true;
}

// Packt Datei im Worker-Thread ein und tauscht das Dateihandle gegen das Write-Pipe-Handle
bool LoadedFile::packFile(HANDLE&h,const char*cmd) {
 HANDLE hr, hw;
 SECURITY_ATTRIBUTES sa={sizeof sa,0,TRUE};
 if (!CreatePipe(&hr,&hw,&sa,Xml::BUFSIZE)) return false;
 if (!convertFile(hr,h,cmd)) return false;
 h=hw;
 return true;
}

bool LoadedFile::load() {
 if (xml) return true;	// bereits geladen
 HANDLE h=open();
 if (h==INVALID_HANDLE_VALUE) return false;
 debug("load %s start\n",filename);
 DWORD br;
 ReadFile(h,&packer_id,4,&br,0);
 SetFilePointer(h,0,0,FILE_BEGIN);
// Kann eine Weile dauern! ProgressProcW() aufrufen
 WCHAR f[256],s[64];
 MultiByteToWideChar(CP_UTF8,0,filename,-1,f,elemof(f));
 LoadStringW(hInstance,3,s,elemof(s));	// "RAM-Speicher"
 ProgressProcW(PluginNumber,f,s,0);
 bool ret=true;
// unpackFile() geht schnell (blockiert kaum),
// denn das ersetzt <h> durch eine Pipe mit einem Fütter-Thread
 switch (packer_id) {
  case 0x00088B1F: ret=unpackFile(h,"gunzip"); break;
  case 0x31685A42: ret=unpackFile(h,"bunzip2"); break;
 }
 if (ret) {
  xml=new Xml(h);
  ret=xml->parse();	// DAS dauert lange! Lässt sich nur schwerlich parallelisieren.
 }
 ProgressProcW(PluginNumber,f,s,100);
 debug("load %s %s\n",filename,ret?"success":"fail");
 if (!ret) debug("Error %x at %s:%u:%u\n",filename,xml->ec,xml->line+1,xml->pos+1);
 return ret;
}

bool LoadedFile::save() {
// make backup!!
 HANDLE h=open(CREATE_ALWAYS);
 if (h==INVALID_HANDLE_VALUE) return false;
 switch (packer_id) {
  case 0x00088B1F: if (!packFile(h,"gzip")) return false; break;
  case 0x31685A42: if (!packFile(h,"bzip2")) return false; break;
 }
 xml->hStream=h;
 return xml->serialize();
}

// std::vector beißt sich mit /NOD (nodefaultlib)
class LoadedFiles{
 int fill;
 LoadedFile a[10];
public:
 const LoadedFile&operator[](int i) const {return a[i];}
 LoadedFile&operator[](int i) {return a[i];}
 int size() const {return fill;}
 void size(int s) {fill=s;}
}loadedFiles;

static bool hasAttrName(const Xml::Node*n,void*p) {
 const Xml::Node*a=n->findChildNode(Xml::Node::Attr,"name");
 if (!a) return false;
 return !strcmp(a->value,(const char*)p);
}

static const Xml::Node*StringToNode(const WCHAR*s) {
 if (s[0]!='\\') return 0;	// Nur absoluter Pfad erlaubt!
 if (!s[1]) return 0;		// Wurzel = Liste der Dateinamen
 int len=WideCharToMultiByte(CP_UTF8,0,s+1,-1,0,0,0,0);
 char*p=(char*)_alloca(len+2);
 WideCharToMultiByte(CP_UTF8,0,s+1,-1,p,len,0,0);
 p[len]=0; p[len+1]=0;
 for (char*q=p;;++q) {
  q=strchr(q,'\\');
  if (q) *q=0; else break;	// Doppelnulltertimierten Pfadstring generieren
 }
 for (int i=0; i<loadedFiles.size(); i++) {
  if (!lstrcmp(loadedFiles[i].displayname,p)) {	// Dateiname gefunden
   if (!loadedFiles[i].load()) return 0;	// Kann nicht laden
   const Xml::Node*n=loadedFiles[i].xml->root->findChildNode(Xml::Node::Element,"catalog");
   for (char*q=p; *(q+=lstrlen(q)+1);) {
    n=n->findChildNode(Xml::Node::Element,hasAttrName,(void*)q);
    if (!n) return 0;
   }
   return n;
  }
 }
 return 0;
}

enum{
 eRoot=32,	// erlaubt das Auflisten von Katalogen (falls mehrere; unzulässiges XML)
 eCatalog=16,
 eMedia=8,
 eDir=4,
 eFile=2,
 eOther=1,
};

BYTE classify(const Xml::Node*n,BYTE check=0xFF) {	// Liefert 1-aus-n-Bitmaske
 if (!n) return 0;			// nichts
 if (check&eRoot	&& n->type==Xml::Node::root)		return eRoot;
 if (n->type!=Xml::Node::Element) return 0;	// nichts
 if (check&eCatalog	&& !lstrcmp(n->name,"catalog"))	return eCatalog;
 if (check&eMedia	&& !lstrcmp(n->name,"media"))	return eMedia;
 if (check&eDir		&& !lstrcmp(n->name,"directory"))return eDir;
 if (check&eFile	&& !lstrcmp(n->name,"file"))	return eFile;
 return check&eOther;
}

bool setFindData(const Xml::Node*c,WIN32_FIND_DATAW&W) {
 if (!c) return false;
 W.dwFileAttributes=classify(c,eFile)?0:FILE_ATTRIBUTE_DIRECTORY;
 W.ftLastWriteTime.dwHighDateTime=DWORD(-1);
 W.ftLastWriteTime.dwLowDateTime=DWORD(-2);
 W.nFileSizeHigh=0;
 W.nFileSizeLow=0;
 const Xml::Node*a=c->findChildNode(Xml::Node::Attr,"time");
 if (a) {
  SYSTEMTIME st;
  st.wDayOfWeek=st.wMilliseconds=0;
  if (sscanf(a->value,"%hu-%hu-%hu %hu:%hu:%hu",
	&st.wYear,&st.wMonth,&st.wDay,
	&st.wHour,&st.wMinute,&st.wSecond)==6) {
   FILETIME ft;
   SystemTimeToFileTime(&st,&ft);
   LocalFileTimeToFileTime(&ft,&W.ftLastWriteTime);
  }
 }
 if (!W.dwFileAttributes) {	// Datei, kein Verzeichnis
  a=c->findChildNode(Xml::Node::Attr,"size");
  if (a) {
   int l=lstrlen(a->value);
   char*s=(char*)_alloca(l+1);
   memcpy(s,a->value,l+1);
   char*k=(char*)memchr(s,',',l);
   if (k) *k='.';
   float fsz;
   char kmg[2];
   if (sscanf(s,"%f %1[KMGT]b",
	&fsz,kmg)==2) {
    switch (kmg[0]) {
     case 'T': fsz*=1000;
     case 'G': fsz*=1000;
     case 'M': fsz*=1000;	// Auch wenn das wahrscheinlich nicht stimmt, sieht im Commander besser aus
    }
    ULONGLONG sz=ULONGLONG(fsz*1000);
    W.nFileSizeHigh=DWORD(sz>>32);
    W.nFileSizeLow=DWORD(sz);
   }
  }
 }
 a=c->findChildNode(Xml::Node::Attr,"name");
 if (a) {
  unicopy(CP_UTF8,W.cFileName,elemof(W.cFileName),a->value);
 }else *W.cFileName=0;		// ohne Name (Datenbank-Bug)
 *W.cAlternateFileName=0;
 return true;
}

static void initfinddata(WIN32_FIND_DATAW&W) {
 memset(&W,0,sizeof W);
 W.ftLastWriteTime.dwHighDateTime=DWORD(-1);
 W.ftLastWriteTime.dwLowDateTime=DWORD(-2);
}

static bool classify1(const Xml::Node*n,void*) {
 return !!classify(n,/*eCatalog|*/eMedia|eDir|eFile);
}

HANDLE WINAPI FsFindFirstW(WCHAR*Path,WIN32_FIND_DATAW&W) {
// debug("%s(%S,%p)\n","FsFindFirstW",Path,&W);
 initfinddata(W);
 if (Path[0]=='\\' && !Path[1]) {
  W.dwFileAttributes=FILE_ATTRIBUTE_READONLY;		// Kein Verzeichnis zum Öffnen
  FindHandle*lf=new FindHandle(0);
  LoadStringW(hInstance,2,W.cFileName,elemof(W.cFileName));	//"Neuer Katalog"
  return lf;
 }
 const Xml::Node*n=StringToNode(Path);
 if (!classify(n,/*eRoot|*/eCatalog|eMedia|eDir)) return INVALID_HANDLE_VALUE;
 const Xml::Node*c=n->findChildNode(Xml::Node::Element,classify1,0);
 if (!setFindData(c,W)) {
  SetLastError(ERROR_NO_MORE_FILES);
  return INVALID_HANDLE_VALUE;
 }
 return new FindHandle(c);
}

HANDLE WINAPI FsFindFirst(char* Path,WIN32_FIND_DATA&A) {
 WIN32_FIND_DATAW W;
 WCHAR PathW[LONGPATH_MAX];
 HANDLE retval=FsFindFirstW(unicopy(PathW,elemof(PathW),Path),W);
 if (retval!=INVALID_HANDLE_VALUE) unicopy(A,W);
 return retval;
}

BOOL WINAPI FsFindNextW(HANDLE Hdl,WIN32_FIND_DATAW&W) {
// debug("%s(%p,%p)\n","FsFindNextW",Hdl,&W);
 initfinddata(W);
 FindHandle*fh=(FindHandle*)Hdl;
 if (!fh->item) {   // generate loaded files list
  if (++fh->iItem>loadedFiles.size()) {
   SetLastError(ERROR_NO_MORE_FILES);
   return false;
  }
  W.dwFileAttributes=FILE_ATTRIBUTE_DIRECTORY;
  LoadedFile&lf=loadedFiles[fh->iItem-1];
  W.ftLastWriteTime=lf.getLastMod();
  unicopy(CP_UTF8,W.cFileName,elemof(W.cFileName),lf.displayname);
  return true;
 }
 ++fh->iItem;
 fh->item=fh->item->next->findElement();	// Textnodes dazwischen ausklammern
 if (!setFindData(fh->item,W)) {
  SetLastError(ERROR_NO_MORE_FILES);
  return false;
 }
 return true;
}

BOOL WINAPI FsFindNext(HANDLE Hdl,WIN32_FIND_DATA&A) {
 WIN32_FIND_DATAW W;
 unicopy(W,A);
 BOOL retval=FsFindNextW(Hdl,W);
 if (retval) unicopy(A,W);
 return retval;
}

int WINAPI FsFindClose(HANDLE Hdl) {
// debug("%s(%p)\n","FsFindClose",Hdl);
 FindHandle*fh=(FindHandle*)Hdl;
 delete fh;
 return 0;
}

void WINAPI FsGetDefRootName(char*DefRootName,int maxlen) {
 debug("%s(%p,%d)\n","FsGetDefRootName",DefRootName,maxlen);
 LoadString(hInstance,1,DefRootName,maxlen);	// "CD-Katalog"
}

#if 0
BOOL WINAPI FsMkDir(const char*Path) {
 WCHAR wbuf[LONGPATH_MAX];
 return FsMkDirW(unicopy(wbuf,elemof(wbuf),Path));
}

BOOL WINAPI FsMkDirW(const WCHAR*Path) {
 if (lstrlenW(Path)<pluginrootlen+2) return false;
 return CreateDirectoryT(Path+pluginrootlen,NULL);
}

int WINAPI FsExecuteFile(HWND MainWin,const char*RemoteName,const char*Verb) {
 if (lstrlen(RemoteName)<pluginrootlen+2) return FS_EXEC_ERROR;
 if (!lstrcmpi(Verb,"open")) return FS_EXEC_YOURSELF;
 if (!lstrcmpi(Verb,"properties")) {
  SHELLEXECUTEINFO shex;
  memset(&shex,0,sizeof(shex));
  shex.fMask=SEE_MASK_INVOKEIDLIST;
  shex.cbSize=sizeof(shex);
  shex.nShow=SW_SHOW;
  shex.hwnd=MainWin;
  shex.lpVerb=Verb;
  shex.lpFile=RemoteName+pluginrootlen;
  if (!ShellExecuteEx(&shex)) return FS_EXEC_ERROR;
  return FS_EXEC_OK;
 }
 return FS_EXEC_ERROR;
}

int WINAPI FsExecuteFileW(HWND MainWin,const WCHAR*RemoteName,const WCHAR*Verb) {
    SHELLEXECUTEINFOW shex;
	if (lstrlenW(RemoteName)<pluginrootlen+2)
		return FS_EXEC_ERROR;
	if (lstrcmpiW(Verb,L"open")==0) {
		return FS_EXEC_YOURSELF;
	} else if (lstrcmpiW(Verb,L"properties")==0) {
        memset(&shex,0,sizeof(shex));
		shex.fMask=SEE_MASK_INVOKEIDLIST;
        shex.cbSize=sizeof(shex);
		shex.nShow=SW_SHOW;
		shex.hwnd=MainWin;
		shex.lpVerb=Verb;
		shex.lpFile=RemoteName+pluginrootlen;
		if (!ShellExecuteExW(&shex))
			return FS_EXEC_ERROR;
		else
			return FS_EXEC_OK;

	} else
		return FS_EXEC_ERROR;
}

int WINAPI FsRenMovFile(const char*OldName,const char*NewName,BOOL Move,BOOL OverWrite,RemoteInfoStruct* ri) {
 WCHAR OldNameW[LONGPATH_MAX],NewNameW[LONGPATH_MAX];
 return FsRenMovFileW(
	unicopy(OldNameW,elemof(OldNameW),OldName),
	unicopy(NewNameW,elemof(NewNameW),NewName),
	Move,OverWrite,ri);
}

int WINAPI FsRenMovFileW(const WCHAR* OldName,const WCHAR* NewName,BOOL Move,BOOL OverWrite,RemoteInfoStruct* ri) {
	if (lstrlenW(OldName)<pluginrootlen+2 || lstrlenW(NewName)<pluginrootlen+2)
		return FS_FILE_NOTFOUND;

	int err=ProgressProcT(OldName,NewName,0);
	if (err)
		return FS_FILE_USERABORT;

	if (Move) {
		if (OverWrite)
			DeleteFileT(NewName+pluginrootlen);
		if (MoveFileT(OldName+pluginrootlen,NewName+pluginrootlen))
			return FS_FILE_OK;
	} else {
		if (CopyFileT(OldName+pluginrootlen,NewName+pluginrootlen,!OverWrite))
			return FS_FILE_OK;
	}
	switch(GetLastError()) {
		case ERROR_FILE_NOT_FOUND:
		case ERROR_PATH_NOT_FOUND:
		case ERROR_ACCESS_DENIED:
			return FS_FILE_NOTFOUND;
		case ERROR_FILE_EXISTS:
			return FS_FILE_EXISTS;
		default:
			return FS_FILE_WRITEERROR;
	}
	err=ProgressProcT(OldName,NewName,100);
	if (err)
		return FS_FILE_USERABORT;
}

int WINAPI FsGetFileW(WCHAR* RemoteName,WCHAR* LocalName,int CopyFlags,RemoteInfoStruct* ri)
{
    int err;
	BOOL ok,OverWrite,Resume,Move;

	OverWrite=CopyFlags & FS_COPYFLAGS_OVERWRITE;
	Resume=CopyFlags & FS_COPYFLAGS_RESUME;
	Move=CopyFlags & FS_COPYFLAGS_MOVE;

	if (Resume)
		return FS_FILE_NOTSUPPORTED;

	if (lstrlenW(RemoteName)<pluginrootlen+2)
		return FS_FILE_NOTFOUND;
	
	err=ProgressProcT(RemoteName,LocalName,0);
	if (err)
		return FS_FILE_USERABORT;
	if (Move) {
		if (OverWrite)
			DeleteFileT(LocalName);
		ok=MoveFileT(RemoteName+pluginrootlen,LocalName);
	} else
		ok=CopyFileT(RemoteName+pluginrootlen,LocalName,!OverWrite);
	
	if (ok) {
		ProgressProcT(RemoteName,LocalName,100);
		return FS_FILE_OK;
	} else {
		err=GetLastError();
		switch (err) {
		case 2:
		case 3:
		case 4:
		case 5:
			return FS_FILE_NOTFOUND;
		case ERROR_FILE_EXISTS:
			return FS_FILE_EXISTS;
		default:
			return FS_FILE_READERROR;
		}
	}
}

int WINAPI FsGetFile(const char*RemoteName,const char*LocalName,int CopyFlags,RemoteInfoStruct* ri) {
 WCHAR RemoteNameW[LONGPATH_MAX],LocalNameW[LONGPATH_MAX];
 return FsGetFileW(
	unicopy(RemoteNameW,elemof(RemoteNameW),RemoteName),
	unicopy(LocalNameW,elemof(LocalNameW),LocalName),
	CopyFlags,ri);
}

int WINAPI FsPutFileW(const WCHAR*LocalName,const WCHAR*RemoteName,int CopyFlags) {
 int err;
 BOOL ok,OverWrite,Resume,Move;

 OverWrite=CopyFlags & FS_COPYFLAGS_OVERWRITE;
 Resume=CopyFlags & FS_COPYFLAGS_RESUME;
 Move=CopyFlags & FS_COPYFLAGS_MOVE;
 if (Resume) return FS_FILE_NOTSUPPORTED;
 if (lstrlenW(RemoteName)<pluginrootlen+2) return FS_FILE_NOTFOUND;
	
 err=ProgressProcT(LocalName,RemoteName,0);
 if (err) return FS_FILE_USERABORT;
 if (Move) {
  if (OverWrite) DeleteFileT(RemoteName+pluginrootlen);
  ok=MoveFileT(LocalName,RemoteName+pluginrootlen);
 }else ok=CopyFileT(LocalName,RemoteName+pluginrootlen,!OverWrite);
 if (ok) {
  ProgressProcT(RemoteName,LocalName,100);
  return FS_FILE_OK;
 }else{
  err=GetLastError();
  switch (err) {
   case 2:
   case 3:
   case 4:
   case 5: return FS_FILE_NOTFOUND;
   case ERROR_FILE_EXISTS: return FS_FILE_EXISTS;
   default: return FS_FILE_READERROR;
  }
 }
}

int WINAPI FsPutFile(const char*LocalName,const char*RemoteName,int CopyFlags) {
 WCHAR LocalNameW[LONGPATH_MAX],RemoteNameW[LONGPATH_MAX];
 return FsPutFileW(
	unicopy(LocalNameW,elemof(LocalNameW),LocalName),
	unicopy(RemoteNameW,elemof(RemoteNameW),RemoteName),
	CopyFlags);
}

BOOL WINAPI FsDeleteFileW(const WCHAR*RemoteName) {
 if (lstrlenW(RemoteName)<pluginrootlen+2) return false;
 return DeleteFileT(RemoteName+pluginrootlen);	
}

BOOL WINAPI FsDeleteFile(const char*RemoteName) {
 WCHAR RemoteNameW[LONGPATH_MAX];
 return FsDeleteFileW(unicopy(RemoteNameW,elemof(RemoteNameW),RemoteName));
}

BOOL WINAPI FsRemoveDirW(const WCHAR* RemoteName) {
 if (lstrlenW(RemoteName)<pluginrootlen+2) return false;
 return RemoveDirectoryT(RemoteName+pluginrootlen);	
}

BOOL WINAPI FsRemoveDir(const char* RemoteName) {
 WCHAR RemoteNameW[LONGPATH_MAX];
 return FsRemoveDirW(unicopy(RemoteNameW,elemof(RemoteNameW),RemoteName));
}

BOOL WINAPI FsSetAttrW(const WCHAR* RemoteName,int NewAttr)
{
	if (lstrlenW(RemoteName)<pluginrootlen+2)
		return false;

	if (NewAttr==0)
		NewAttr=FILE_ATTRIBUTE_NORMAL;
	return SetFileAttributesT(RemoteName+pluginrootlen,NewAttr);	
}

BOOL WINAPI FsSetAttr(char* RemoteName,int NewAttr)
{
	WCHAR RemoteNameW[LONGPATH_MAX];
	return FsSetAttrW(unicopy(RemoteNameW,elemof(RemoteNameW),RemoteName),NewAttr);
}

BOOL WINAPI FsSetTimeW(WCHAR* RemoteName,FILETIME *CreationTime,
      FILETIME *LastAccessTime,FILETIME *LastWriteTime)
{
	if (lstrlenW(RemoteName)<pluginrootlen+2)
		return false;

	HANDLE filehandle = CreateFileT(RemoteName+pluginrootlen,	
                      GENERIC_WRITE,          // Open for writing
                      0,                      // Do not share
                      NULL,                   // No security
                      OPEN_EXISTING,          // Existing file only
                      FILE_ATTRIBUTE_NORMAL,  // Normal file
                      NULL);

	if (filehandle==INVALID_HANDLE_VALUE)
		return FALSE;

	BOOL retval=SetFileTime(filehandle,CreationTime,LastAccessTime,LastWriteTime);
    CloseHandle(filehandle);
	return retval;
}

BOOL WINAPI FsSetTime(char* RemoteName,FILETIME *CreationTime,
      FILETIME *LastAccessTime,FILETIME *LastWriteTime)
{
	WCHAR RemoteNameW[LONGPATH_MAX];
	return FsSetTimeW(unicopy(RemoteNameW,elemof(RemoteNameW),RemoteName),CreationTime,
		LastAccessTime,LastWriteTime);
}

void WINAPI FsStatusInfo(char* RemoteDir,int InfoStartEnd,int InfoOperation)
{
	// This function may be used to initialize variables and to flush buffers
	
/*	char text[LONGPATH_MAX];

	if (InfoStartEnd==FS_STATUS_START)
		strcpy(text,"Start: ");
	else
		strcpy(text,"End: ");
	
	switch (InfoOperation) {
	case FS_STATUS_OP_LIST:
		strcat(text,"Get directory list");
		break;
	case FS_STATUS_OP_GET_SINGLE:
		strcat(text,"Get single file");
		break;
	case FS_STATUS_OP_GET_MULTI:
		strcat(text,"Get multiple files");
		break;
	case FS_STATUS_OP_PUT_SINGLE:
		strcat(text,"Put single file");
		break;
	case FS_STATUS_OP_PUT_MULTI:
		strcat(text,"Put multiple files");
		break;
	case FS_STATUS_OP_RENMOV_SINGLE:
		strcat(text,"Rename/Move/Remote copy single file");
		break;
	case FS_STATUS_OP_RENMOV_MULTI:
		strcat(text,"Rename/Move/Remote copy multiple files");
		break;
	case FS_STATUS_OP_DELETE:
		strcat(text,"Delete multiple files");
		break;
	case FS_STATUS_OP_ATTRIB:
		strcat(text,"Change attributes of multiple files");
		break;
	case FS_STATUS_OP_MKDIR:
		strcat(text,"Create directory");
		break;
	case FS_STATUS_OP_EXEC:
		strcat(text,"Execute file or command line");
		break;
	case FS_STATUS_OP_CALCSIZE:
		strcat(text,"Calculate space occupied by a directory");
		break;
	case FS_STATUS_OP_SEARCH:
		strcat(text,"Search for file names");
		break;
	case FS_STATUS_OP_SEARCH_TEXT:
		strcat(text,"Search for text in files");
		break;
	case FS_STATUS_OP_SYNC_SEARCH:
		strcat(text,"Search files for sync comparison");
		break;
	case FS_STATUS_OP_SYNC_GET:
		strcat(text,"download files during sync");
		break;
	case FS_STATUS_OP_SYNC_PUT:
		strcat(text,"Upload files during sync");
		break;
	case FS_STATUS_OP_SYNC_DELETE:
		strcat(text,"Delete files during sync");
		break;
	default:
		strcat(text,"Unknown operation");
	}
	if (InfoOperation != FS_STATUS_OP_LIST)   // avoid recursion due to re-reading!
		MessageBox(0,text,RemoteDir,0);
*/
}
#endif
int WINAPI FsExtractCustomIconW(WCHAR* Path,int flags,HICON&ico) {
 const Xml::Node*n=StringToNode(Path);
 BYTE c=classify(n,eRoot|eCatalog|eMedia);
 if (c) debug("FsExtractCustomIconW(%S,%d)\n",Path,flags);
 if (c&(eRoot|eCatalog)) {
  ico=LoadIcon(hInstance,MAKEINTRESOURCE(1));
  return FS_ICON_EXTRACTED;
 }
 if (c&eMedia) {
  ico=LoadIcon(hInstance,MAKEINTRESOURCE(2));
  return FS_ICON_EXTRACTED;
 }
 return FS_ICON_USEDEFAULT;
}
#if 0
int WINAPI FsExtractCustomIcon(char* RemoteName,int ExtractFlags,HICON* TheIcon)
{
	WCHAR RemoteNameW[LONGPATH_MAX],OldNameW[LONGPATH_MAX];
	unicopy(RemoteNameW,elemof(RemoteNameW),RemoteName);
	lstrcpyW(OldNameW,RemoteNameW);
	int retval=FsExtractCustomIconW(RemoteNameW,ExtractFlags,TheIcon);
	if (lstrcmpW(OldNameW,RemoteNameW)!=0)
		unicopy(RemoteName,sizeof RemoteName,RemoteNameW);
	return retval;
}

int WINAPI FsGetPreviewBitmap(char* RemoteName,int width,int height,HBITMAP* ReturnedBitmap) {
 if (lstrlen(RemoteName)<=4) {
  if (!lstrcmp(RemoteName,"\\..\\")) return FS_BITMAP_NONE;
	// check for operating system: Windows 9x does NOT support the HALFTONE stretchblt mode!
  bool is_nt=usys();
  HBITMAP bmp_image=LoadBitmap((HINSTANCE)hInstance,"BITMAP1");
  BITMAP bmpobj;
  if (bmp_image && GetObject(bmp_image,sizeof(bmpobj),&bmpobj)) {
   int bigx=bmpobj.bmWidth;
   int bigy=bmpobj.bmHeight;
				// do we need to stretch?
   if ((bigx>=width || bigy>=height) && (bigx>0 && bigy>0)) {
    int w,h,stretchx,stretchy=MulDiv(width,bigy,bigx);
    if (stretchy<=height) {
     w=width;
     h=stretchy;
     if (h<1) h=1;
    }else{
     stretchx=MulDiv(height,bigx,bigy);
     w=stretchx;
     if (w<1) w=1;
     h=height;
    }
    HDC maindc=GetDC(GetDesktopWindow());
    HDC dc_thumbnail=CreateCompatibleDC(maindc);
    HDC dc_image=CreateCompatibleDC(maindc);
    HBITMAP bmp_thumbnail=CreateCompatibleBitmap(maindc,w,h);
    ReleaseDC(GetDesktopWindow(),maindc);
    HBITMAP oldbmp_image=(HBITMAP)SelectObject(dc_image,bmp_image);
    HBITMAP oldbmp_thumbnail=(HBITMAP)SelectObject(dc_thumbnail,bmp_thumbnail);
    if (is_nt) {
     SetStretchBltMode(dc_thumbnail,HALFTONE);
     SetBrushOrgEx(dc_thumbnail,0,0,0);
    }else{
     SetStretchBltMode(dc_thumbnail,COLORONCOLOR);
    }
    StretchBlt(dc_thumbnail,0,0,w,h,dc_image,0,0,bigx,bigy,SRCCOPY);
    SelectObject(dc_image,oldbmp_image);
    SelectObject(dc_thumbnail,oldbmp_thumbnail);
    DeleteDC(dc_image);
    DeleteDC(dc_thumbnail);
    DeleteObject(bmp_image);
    *ReturnedBitmap=bmp_thumbnail;
    return FS_BITMAP_EXTRACTED;
   }
   *ReturnedBitmap=bmp_image;
   return FS_BITMAP_EXTRACTED;
  }
  return FS_BITMAP_NONE;
 }
 memmove(RemoteName,RemoteName+pluginrootlen,lstrlen(RemoteName+pluginrootlen)+1);
 return FS_BITMAP_EXTRACT_YOURSELF | FS_BITMAP_CACHE;
}

int WINAPI FsGetPreviewBitmapW(WCHAR* RemoteName,int width,int height,HBITMAP* ReturnedBitmap)
{
	if (lstrlenW(RemoteName)<=4) {
		if (lstrcmpW(RemoteName,L"\\..\\")==0)
			return FS_BITMAP_NONE;
		else {
			return FsGetPreviewBitmap("\\",width,height,ReturnedBitmap);
		}
	} else {
		memmove(RemoteName,RemoteName+pluginrootlen,2*lstrlenW(RemoteName+pluginrootlen)+2);
		return FS_BITMAP_EXTRACT_YOURSELF | FS_BITMAP_CACHE;
	}
}
#endif
void WINAPI FsSetDefaultParams(FsDefaultParamStruct* dps) {
 lstrcpyn(inifilename,dps->DefaultIniName,elemof(inifilename));
 char buf[4000],*p=buf;
 GetPrivateProfileSection("cdcat1",buf,sizeof buf,inifilename);
// Als UTF-8 interpretieren
 LoadedFile*lf=&loadedFiles[0];
 for (int l;l=lstrlen(p);p+=l+1,lf++) {
  char*q=StrChr(p,'=');
  if (!q) continue;
  size_t l1=q-p;
  if (l1>sizeof lf->displayname-1) l1=sizeof lf->displayname-1;
  memcpy(lf->displayname,p,l1);
  lf->displayname[l1]=0;
  l1 = l-l1;	// Länge Dateiname inklusive '\0'
  if (l1>sizeof lf->filename) l1=sizeof lf->filename;
  memcpy(lf->filename,q+1,l1);
 }
 loadedFiles.size(int(lf-&loadedFiles[0]));
}
#if 0
BOOL WINAPI FsLinksToLocalFiles() {
 return true;
}

BOOL WINAPI FsGetLocalName(char* RemoteName,int maxlen) {
 if (lstrlen(RemoteName)<pluginrootlen+2) return false;
 MoveMemory (RemoteName,RemoteName+pluginrootlen,lstrlen(RemoteName+pluginrootlen)+1);
 return true;
}

BOOL WINAPI FsGetLocalNameW(WCHAR* RemoteName,int maxlen) {
 if (lstrlenW(RemoteName)<pluginrootlen+2) return false;
 MoveMemory(RemoteName,RemoteName+pluginrootlen,2*lstrlenW(RemoteName+pluginrootlen)+2);
 return true;
}

/**************************************************************************************/
/*********************** content plugin = custom columns part! ************************/
/**************************************************************************************/

#define fieldcount 6
char* fieldnames[fieldcount]={
	"size","creationdate","writedate","accessdate","size-delayed","size-ondemand"};

int fieldtypes[fieldcount]={
	ft_numeric_64,ft_datetime,ft_datetime,ft_datetime,ft_numeric_64,ft_numeric_64};

char* fieldunits_and_multiplechoicestrings[fieldcount]={
	"bytes|kbytes|Mbytes|Gbytes","","","","bytes|kbytes|Mbytes|Gbytes","bytes|kbytes|Mbytes|Gbytes"};

int fieldflags[fieldcount]={
    contflags_substsize,contflags_edit,contflags_substdatetime,contflags_edit,contflags_substsize,contflags_substsize | contflags_edit};

int sortorders[fieldcount]={-1,-1,-1,-1,-1,-1};


int WINAPI FsContentGetSupportedField(int FieldIndex,char* FieldName,char* Units,int maxlen)
{
	if (FieldIndex<0 || FieldIndex>=fieldcount)
		return ft_nomorefields;
	lstrcpyn(FieldName,fieldnames[FieldIndex],maxlen);
	lstrcpyn(Units,fieldunits_and_multiplechoicestrings[FieldIndex],maxlen);
	return fieldtypes[FieldIndex];
}

int WINAPI FsContentGetValueT(BOOL unicode,WCHAR* FileName,int FieldIndex,int UnitIndex,void* FieldValue,int maxlen,int flags) {
 HANDLE fh;
 ULONGLONG filesize;

 if (lstrlenW(FileName+pluginrootlen)<=3) return ft_fileerror;
 if (flags & CONTENT_DELAYIFSLOW) {
  if (FieldIndex==4) return ft_delayed;
  if (FieldIndex==5) return ft_ondemand;
 }
 WIN32_FIND_DATAW W;
 fh=FindFirstFileT(FileName+pluginrootlen,W);
 if (fh!=INVALID_HANDLE_VALUE) {
  FindClose(fh);
  switch (FieldIndex) {
   case 0:  // "size"
   case 4:  // "size-delayed"
   case 5:  // "size-ondemand"
   filesize=W.nFileSizeHigh;
   filesize=(filesize<<32) + W.nFileSizeLow;
   switch (UnitIndex) {
    case 1:
    filesize>>=10;
    break;
    case 2:
    filesize>>=20;
    break;
    case 3:
    filesize>>=30;
    break;
   }
   *(ULONGLONG*)FieldValue=filesize;
   break;
   case 1:  // "creationdate"
   *(FILETIME*)FieldValue=W.ftCreationTime;
   break;
   case 2:  // "writedate"
   *(FILETIME*)FieldValue=W.ftLastWriteTime;
   break;
   case 3:  // "accessdate"
   *(FILETIME*)FieldValue=W.ftLastAccessTime;
   break;
   default: return ft_nosuchfield;
  }
 }else return ft_fileerror;
 return fieldtypes[FieldIndex];  // very important!
}

int WINAPI FsContentGetValueW(WCHAR* FileName,int FieldIndex,int UnitIndex,void* FieldValue,int maxlen,int flags) {
 return FsContentGetValueT(true,FileName,FieldIndex,UnitIndex,FieldValue,maxlen,flags);
}

int WINAPI FsContentGetValue(char* FileName,int FieldIndex,int UnitIndex,void* FieldValue,int maxlen,int flags) {
 WCHAR FileNameW[LONGPATH_MAX];
 return FsContentGetValueT(false,unicopy(FileNameW,elemof(FileNameW),FileName),
	FieldIndex,UnitIndex,FieldValue,maxlen,flags);
}

int WINAPI FsContentGetSupportedFieldFlags(int FieldIndex)
{
	if (FieldIndex==-1)
		return contflags_substmask | contflags_edit;
	else if (FieldIndex<0 || FieldIndex>=fieldcount)
		return 0;
	else
		return fieldflags[FieldIndex];
}

int WINAPI FsContentGetDefaultSortOrder(int FieldIndex)
{
	if (FieldIndex<0 || FieldIndex>=fieldcount)
		return 1;
	else 
		return sortorders[FieldIndex];
}

BOOL WINAPI FsContentGetDefaultView(char* ViewContents,char* ViewHeaders,char* ViewWidths,char* ViewOptions,int maxlen)
{
	lstrcpyn(ViewContents,"[=<fs>.size.bkM2]\\n[=tc.size.bkM2]",maxlen);  // separated by backslash and n, not new lines!
	lstrcpyn(ViewHeaders,"fs-size\\ntc-size",maxlen);  // titles in ENGLISH also separated by backslash and n, not new lines!
	lstrcpyn(ViewWidths,"148,23,-35,-35",maxlen);
	lstrcpyn(ViewOptions,"-1|0",maxlen);  // auto-adjust-width, or -1 for no adjust | horizonal scrollbar flag
	return true;
}

int WINAPI FsContentSetValueW(WCHAR* FileName,int FieldIndex,int UnitIndex,int FieldType,void* FieldValue,int flags)
{
 int retval=ft_nomorefields;
 FILETIME oldcreationtime,newcreationtime;
 FILETIME *p1,*p2,*FieldTime;
 SYSTEMTIME st1,st2;
 HANDLE f;

 if (!FileName) return ft_nosuchfield;    // indicates end of operation -> may be used to flush data
 if (FieldIndex<0 || FieldIndex>=fieldcount) return ft_nosuchfield;
 if (!(fieldflags[FieldIndex]&1)) return ft_nosuchfield;
 switch (FieldIndex) {
  case 1:  // "creationdate"
  case 3:  // "accessdate"
			FieldTime=(FILETIME*)FieldValue;
			p1=NULL;p2=NULL;
			if (FieldIndex==1)
				p1=&oldcreationtime;
			else
				p2=&oldcreationtime;

			f= CreateFileT(FileName+pluginrootlen,	
                      GENERIC_READ|GENERIC_WRITE, // Open for reading+writing
                      0,                      // Do not share
                      NULL,                   // No security
                      OPEN_EXISTING,          // Existing file only
                      FILE_ATTRIBUTE_NORMAL,  // Normal file
                      NULL);
			if (flags & setflags_only_date) {
				GetFileTime(f,p1,p2,NULL);
				FileTimeToLocalFileTime(&oldcreationtime,&newcreationtime);
				FileTimeToSystemTime(&newcreationtime,&st2);
				FileTimeToLocalFileTime(FieldTime,&newcreationtime);
				FileTimeToSystemTime(&newcreationtime,&st1);
				st1.wHour=st2.wHour;
				st1.wMinute=st2.wMinute;
				st1.wSecond=st2.wSecond;
				st1.wMilliseconds=st2.wMilliseconds;
				SystemTimeToFileTime(&st1,&newcreationtime);
				LocalFileTimeToFileTime(&newcreationtime,&oldcreationtime);
			} else
				oldcreationtime=*FieldTime;
			if (!SetFileTime(f,p1,p2,NULL))
				retval=ft_fileerror;
			CloseHandle(f);
			break;
 }
 return retval;
}

int WINAPI FsContentSetValue(char* FileName,int FieldIndex,int UnitIndex,int FieldType,void* FieldValue,int flags) {
 WCHAR FileNameW[LONGPATH_MAX];
 return FsContentSetValueW(unicopy(FileNameW,elemof(FileNameW),FileName),
	FieldIndex,UnitIndex,FieldType,FieldValue,flags);
}

void WINAPI FsContentPluginUnloading() {
	// If you do something in a background thread, you may
	// wait in this function until the thread has finished
	// its work to prevent Total Commander from closing!
	// MessageBox(0,"fsplugin unloading!","Test",0);
}
#endif
Detected encoding: UTF-80