/* UNRAR plugin for Tux Commander * version 0.3.0, designed for unrar v3.8.2 * Copyright (C) 2008 Tomas Bzatek * Check for updates on tuxcmd.sourceforge.net * * Uses UNRAR sources * UnRAR - free utility for RAR archives * Copyright (C) Alexander L. Roshal * http://www.rarlab.com/ * * 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 of the License, 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include "vfs_types.h" #include "vfsutils.h" #include "strutils.h" #include "treepathutils.h" #include "treepath_vfs.h" #define _UNIX #include "unrar/version.hpp" #include "unrar/dll.hpp" // Compatibility types from headers.hpp enum HOST_SYSTEM { HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4, HOST_BEOS=5,HOST_MAX }; #define VERSION "0.3.0" #define BUILD_DATE "2008-10-05" #define DEFAULT_BLOCK_SIZE 65536 /******************************************************************************************************/ /** Auxiliary classes */ /************** ****************/ // Declaration of the global plugin object struct TVFSGlobs { TVFSLogFunc log_func; char *curr_dir; char *archive_path; gboolean need_password; gboolean passwd_callback; char *password; unsigned long block_size; struct PathTree *files; struct VfsFilelistData *vfs_filelist; u_int64_t total_size; void *extract_callback_data; TVFSCopyCallBackFunc extract_callback_func; u_int64_t extract_file_size; u_int64_t extract_done; gboolean extract_cancelled; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// // Basic initialization functions int VFSAllocNeeded() { return sizeof(struct TVFSGlobs); } void VFSInit(struct TVFSGlobs *globs, TVFSLogFunc log_func) { globs->block_size = DEFAULT_BLOCK_SIZE; globs->extract_callback_data = NULL; globs->extract_callback_func = NULL; globs->need_password = FALSE; globs->passwd_callback = FALSE; globs->log_func = log_func; if (globs->log_func != NULL) globs->log_func((char*)"unrar plugin: VFSInit"); } void VFSDestroy(struct TVFSGlobs *globs) { if (globs->log_func != NULL) globs->log_func((char*)"unrar plugin: VFSDestroy"); } int VFSVersion() { return cVFSVersion; } struct TVFSInfo VFSGetInfo() { struct TVFSInfo module_info; module_info.Name = "UNRAR plugin"; module_info.Description = "RAR archiving plugin"; char *s = (char*)malloc(255); snprintf(s, 255, "version %s, build date: %s\nusing unrar sources v%d.%d [%d-%.2d-%.2d]\n", VERSION, BUILD_DATE, RARVER_MAJOR, RARVER_MINOR, RARVER_YEAR, RARVER_MONTH, RARVER_DAY); module_info.About = strdup(s); free(s); s = (char*)malloc(255); snprintf(s, 255, "Plugin Copyright (C) 2008 Tomáš Bžatek\nUNRAR sources Copyright (C) 2002-2008 Alexander Roshal"); module_info.Copyright = strdup(s); return module_info; } char *VFSGetPrefix(struct TVFSGlobs *globs) { return globs->archive_path; // return (char*)"unrar"; } char *VFSGetExts() { return (char*)"rar;r00;r01;r02;r03;r04;r05;r06;r07;r08;r09;r10;r11;r12;r13;r14;r15;r16;r17;r18;r19;r20;r21;r22;r23;r24;r25;r26;r27;r28;r29;r30;r31;r32;r33;r34;r35;r36;r37;r38;r39;r40;r41;r42;r43;r44;r45;r46;r47;r48;r49;r50;r51;r52;r53;r54;r55;r56;r57;r58;r59;r60;r61;r62;r63;r64;r65;r66;r67;r68;r69;r70;r71;r72;r73;r74;r75;r76;r77;r78;r79;r80;r81;r82;r83;r84;r85;r86;r87;r88;r89;r90;r91;r92;r93;r94;r95;r96;r97;r98;r99"; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ int unrar_callback(UINT msg, LONG UserData, LONG P1, LONG P2) { // fprintf(stderr, "(II) unrar_callback called: msg = %d, UserData = %lx, sizeof(UserData) = %ld, P1 = %ld, P2 = %ld\n", msg, UserData, sizeof(UserData), P1, P2); // >= 0 => Weiter, -1 => Stop switch(msg) { case UCM_CHANGEVOLUME: { if (P2 == RAR_VOL_ASK) { fprintf(stderr, " (II) unrar_callback: UCM_CHANGEVOLUME message, RAR_VOL_ASK, P1 = %ld, (char*)P1 = '%s' \n", P1, (char*)P1); if (P1) if (access((char*)P1, R_OK) != 0) { fprintf(stderr, "(EE) unrar_callback: UCM_CHANGEVOLUME message, RAR_VOL_ASK: access test failed - missing part? Error = %s \n", strerror(errno)); return -1; } } else if (P2 == RAR_VOL_NOTIFY) { fprintf(stderr, " (II) unrar_callback: UCM_CHANGEVOLUME message, RAR_VOL_NOTIFY, P1 = %ld, (char*)P1 = '%s' \n", P1, (char*)P1); } break; } case UCM_PROCESSDATA: { fprintf(stderr, " (II) unrar_callback: UCM_PROCESSDATA message, P1 = %ld, P2 = %ld \n", P1, P2); struct TVFSGlobs *globs = (struct TVFSGlobs *)UserData; // printf(" (II) unrar_callback: globs = 0x%lX, UserData = 0x%lX \n", (unsigned long int)globs, UserData); if ((globs) && (globs->extract_callback_func != NULL)) { // printf(" (II) unrar_callback: globs->extract_callback_func = 0x%lX, globs->extract_callback_data = 0x%lX \n", (unsigned long int)globs->extract_callback_func, (unsigned long int)globs->extract_callback_data); // long int res = globs->extract_callback_func((u_int64_t)P1, (u_int64_t)((u_int64_t)P1 + (u_int64_t)P2), globs->extract_callback_data); globs->extract_done += P2; int res = globs->extract_callback_func(globs->extract_done, globs->extract_file_size, globs->extract_callback_data); // fprintf(stderr, " (II) unrar_callback: res = %d \n", res); if (! res ) { globs->extract_cancelled = TRUE; fprintf(stderr, "(WW) unrar_callback: received cancellation result\n"); return -1; // Cancel operation } } break; } case UCM_NEEDPASSWORD: { fprintf(stderr, " (II) unrar_callback: UCM_NEEDPASSWORD message, P1 = %ld, P2 = %ld, (char*)P1 = '%s', maxlen = %ld \n", P1, P2, (char*)P1, P2); struct TVFSGlobs *globs = (struct TVFSGlobs *)UserData; if (globs) globs->passwd_callback = TRUE; break; } } return 0; } int PASCAL unrar_changevol_proc(char *ArcName, int Mode) { fprintf(stderr, "(II) unrar_changevol_proc called: ArcName = '%s', Mode = %d\n", ArcName, Mode); return 0; } int PASCAL unrar_process_data_proc(unsigned char *Addr, int Size) { fprintf(stderr, "(II) unrar_process_data_proc called: Addr = '%s', Size = %d\n", Addr, Size); return 0; } time_t rar_time_to_unix(unsigned int FileTime) { struct tm t; t.tm_sec = (FileTime & 0x1f) * 2; t.tm_min = (FileTime >> 5) & 0x3f; t.tm_hour = (FileTime >> 11) & 0x1f; t.tm_mday = (FileTime >> 16) & 0x1f; t.tm_mon = ((FileTime >> 21) & 0x0f)-1; t.tm_year = (FileTime >> 25) + 80; t.tm_isdst = -1; // printf("date: %d-%.2d-%.2d %d:%.2d:%.2d \n", t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); return(mktime(&t)); } TVFSResult VFSOpen(struct TVFSGlobs *globs, char *sName) { TVFSResult Result = cVFS_OK; globs->files = filelist_tree_new(); globs->vfs_filelist = vfs_filelist_new(globs->files); globs->curr_dir = NULL; globs->archive_path = strdup(sName); globs->total_size = 0; globs->passwd_callback = FALSE; fprintf(stderr, "(--) VFSOpen: trying to open archive '%s'...\n", globs->archive_path); HANDLE PASCAL handle; struct RAROpenArchiveDataEx *archive_data; archive_data = (struct RAROpenArchiveDataEx*) malloc(sizeof(struct RAROpenArchiveDataEx)); memset(archive_data, 0, sizeof(struct RAROpenArchiveDataEx)); archive_data->ArcName = globs->archive_path; archive_data->CmtBuf = NULL; archive_data->CmtBufSize = 0; archive_data->OpenMode = RAR_OM_LIST; handle = RAROpenArchiveEx(archive_data); // printf(" handle = %lu \n", (unsigned long int)handle); // printf(" archive_data->OpenResult = %d \n", archive_data->OpenResult); // printf(" archive_data->CmtState = %d \n", archive_data->CmtState); // printf(" archive_data->CmtSize = %d \n", archive_data->CmtSize); if ((handle) && (! archive_data->OpenResult)) { // Set callbacks RARSetCallback(handle, unrar_callback, (LONG)globs); // RARSetChangeVolProc(handle, unrar_changevol_proc); // RARSetProcessDataProc(handle, unrar_process_data_proc); if (globs->password) { fprintf(stderr, "(II) VFSOpen: Setting password... \n"); RARSetPassword(handle, globs->password); } struct RARHeaderDataEx *header; header = (struct RARHeaderDataEx*) malloc(sizeof(struct RARHeaderDataEx)); memset(header, 0, sizeof(struct RARHeaderDataEx)); header->CmtBuf = NULL; header->CmtBufSize = 0; int PASCAL res = 0; while ((res = RARReadHeaderEx(handle, header)) == 0) { printf(" header->FileName = '%s', Flags = 0x%x\n", header->FileName, header->Flags); // Create a TVFSItem entry and fill all info struct TVFSItem *item = (struct TVFSItem*)malloc(sizeof(struct TVFSItem)); memset(item, 0, sizeof(struct TVFSItem)); item->iSize = (u_int64_t)((u_int64_t)(header->UnpSizeHigh * 0x100000000) + (u_int64_t)header->UnpSize); globs->total_size += item->iSize; if ((header->Flags & 0x00e0 /* LHD_WINDOWMASK */ ) == 0x00e0 /* LHD_DIRECTORY */) item->ItemType = vDirectory; else item->ItemType = vRegular; if ((header->Flags & 0x0004) == 0x0004) globs->need_password = TRUE; switch (header->HostOS) { case HOST_MSDOS: case HOST_OS2: case HOST_WIN32: if (header->FileAttr & 0x10) header->FileAttr = 0x41ff | header->FileAttr; else if (header->FileAttr & 1) header->FileAttr = 0x8124 | header->FileAttr; else header->FileAttr = 0x81b6 | header->FileAttr; break; } item->iMode = header->FileAttr; item->iUID = geteuid(); item->iGID = getegid(); item->m_time = rar_time_to_unix(header->FileTime); item->c_time = item->m_time; item->a_time = item->m_time; // Add item to the global list and continue with next file filelist_tree_add_item(globs->files, header->FileName, item, 0); int PASCAL res2 = RARProcessFile(handle, RAR_SKIP, NULL, NULL); if (res2) printf("RARProcessFile result = %d\n", res2); } // printf("\nRARReadHeader result = %d\n", res); if (res != ERAR_END_ARCHIVE) { fprintf(stderr, "(EE) VFSOpen: RARReadHeader result = %d\n", res); Result = cVFS_Failed; if ((res == ERAR_MISSING_PASSWORD) || ((res == ERAR_BAD_DATA) && (globs->passwd_callback))) Result = cVFS_BadPassword; } free(header); res = RARCloseArchive(handle); if (res) { fprintf(stderr, "(EE) VFSOpen: RARCloseArchive result = %d\n", res); } } else { fprintf(stderr, "(EE) VFSOpen: error occured when opening archive: OpenResult = %d\n", archive_data->OpenResult); Result = cVFS_Failed; } free(archive_data); fprintf(stderr, "(II) VFSOpen: done. \n"); if (globs->need_password) printf("Password present.\n"); printf("\n\nList of items:\n"); filelist_tree_print(globs->files); return Result; } TVFSResult VFSClose(struct TVFSGlobs *globs) { if (globs) { fprintf(stderr, "(II) VFSClose: Freeing objects...\n"); if (globs->vfs_filelist) vfs_filelist_free(globs->vfs_filelist); if (globs->files) filelist_tree_free(globs->files); if (globs->archive_path) free(globs->archive_path); if (globs->curr_dir) free(globs->curr_dir); if (globs->password) free(globs->password); } return cVFS_OK; } char *VFSGetPath(struct TVFSGlobs *globs) { return include_trailing_path_sep(globs->curr_dir); } u_int64_t VFSGetFileSystemFree(struct TVFSGlobs *globs, char *APath) { return 0; } u_int64_t VFSGetFileSystemSize(struct TVFSGlobs *globs, char *APath) { return globs->total_size; } /******************************************************************************************************/ TVFSResult VFSChangeDir(struct TVFSGlobs *globs, char *NewPath) { if (NewPath == NULL) { printf("(EE) VFSChangeDir: NewPath is NULL!\n"); return cVFS_Failed; } globs->curr_dir = vfs_filelist_change_dir(globs->vfs_filelist, NewPath); if (globs->curr_dir) return cVFS_OK; else return cVFS_Failed; } int VFSLogin(struct TVFSGlobs *globs, char *user, char *pass) { return cVFS_Not_Supported; } int VFSSetPassword(struct TVFSGlobs *globs, char *pass) { printf ("(II) VFSSetPassword: Going to set the password...\n"); if (globs->password) free(globs->password); globs->password = strdup(pass); return cVFS_OK; } int VFSGetPasswordRequired(struct TVFSGlobs *globs) { if (globs) return globs->need_password; return FALSE; } /******************************************************************************************************/ TVFSResult VFSListFirst(struct TVFSGlobs *globs, char *sDir, struct TVFSItem *Item) { if (sDir == NULL) { printf("(EE) VFSListFirst: sDir is NULL!\n"); return cVFS_Failed; } printf ("(--) VFSListFirst: Going to list all items in '%s'\n", sDir); return vfs_filelist_list_first(globs->vfs_filelist, sDir, Item); } TVFSResult VFSListNext(struct TVFSGlobs *globs, char *sDir, struct TVFSItem *Item) { return vfs_filelist_list_next(globs->vfs_filelist, sDir, Item); } TVFSResult VFSListClose(struct TVFSGlobs *globs) { return vfs_filelist_list_close(globs->vfs_filelist); } /******************************************************************************************************/ long VFSFileExists(struct TVFSGlobs *globs, const char *FileName, const long Use_lstat) { if (! globs) return FALSE; return vfs_filelist_file_exists(globs->vfs_filelist, FileName, Use_lstat); } TVFSResult VFSFileInfo(struct TVFSGlobs *globs, char *AFileName, struct TVFSItem *Item) { printf("(--) VFSFileInfo: requested info for object '%s'\n", AFileName); if (!globs) return cVFS_Failed; return vfs_filelist_file_info(globs->vfs_filelist, AFileName, Item); } /******************************************************************************************************/ /** Recursive tree size counting */ /************** ****************/ u_int64_t VFSGetDirSize(struct TVFSGlobs *globs, char *APath) { if (! globs) return 0; return vfs_filelist_get_dir_size(globs->vfs_filelist, APath); } void VFSBreakGetDirSize(struct TVFSGlobs *globs) { printf("(WW) VFSBreakGetDirSize: calling break\n"); if (globs) vfs_filelist_get_dir_size_break(globs->vfs_filelist); } /******************************************************************************************************/ /** Methods modifying the archive */ /************** ****************/ TVFSResult VFSMkDir(struct TVFSGlobs *globs, const char *sDirName) { printf("(WW) VFSMkDir: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSRemove(struct TVFSGlobs *globs, const char *APath) { printf("(WW) VFSRemove: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSRename(struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName) { printf("(WW) VFSRename: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSMakeSymLink(struct TVFSGlobs *globs, const char *NewFileName, const char *PointTo) { printf("(WW) VFSMakeSymLink: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSChmod(struct TVFSGlobs *globs, const char *FileName, const uint Mode) { printf("(WW) VFSChmod: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSChown(struct TVFSGlobs *globs, const char *FileName, const uint UID, const uint GID) { printf("(WW) VFSChown: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSChangeTimes(struct TVFSGlobs *globs, char *APath, long mtime, long atime) { printf("(WW) VFSChangeTimes: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// TVFSFileDes VFSOpenFile(struct TVFSGlobs *globs, const char *APath, int Mode, int *Error) { *Error = cVFS_Not_Supported; return (TVFSFileDes)0; } TVFSResult VFSCloseFile(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor) { return cVFS_Not_Supported; } u_int64_t VFSFileSeek(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, u_int64_t AbsoluteOffset, int *Error) { *Error = cVFS_Not_Supported; return 0; } int VFSReadFile(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, void *Buffer, int ABlockSize, int *Error) { *Error = cVFS_Not_Supported; return 0; } int VFSWriteFile(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, void *Buffer, int BytesCount, int *Error) { *Error = cVFS_Not_Supported; return 0; } void VFSSetBlockSize(struct TVFSGlobs *globs, int Value) { globs->block_size = Value; } int VFSIsOnSameFS(struct TVFSGlobs *globs, const char *Path1, const char *Path2) { printf("(WW) VFSIsOnSameFS: Not supported in UNRAR plugin.\n"); return TRUE; } int VFSTwoSameFiles(struct TVFSGlobs *globs, const char *Path1, const char *Path2) { printf("(WW) VFSTwoSameFiles: Not supported in ZIP archives, comparing by paths.\n"); return compare_two_same_files(Path1, Path2); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// TVFSResult VFSCopyOut(struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, TVFSCopyCallBackFunc pCallBackProgress, void *data, int Append) { if ((sSrcName == NULL) || (sDstName == NULL) || (strlen(sSrcName) < 1) || (strlen(sDstName) < 1)) { printf("(EE) VFSCopyOut: The value of 'sSrcName' or 'sDstName' is NULL or empty\n"); return cVFS_Failed; } printf("(II) VFSCopyOut: copying file '%s' out to '%s'\n", sSrcName, sDstName); TVFSResult Result = cVFS_OK; char *src; if (! IS_DIR_SEP(*sSrcName)) src = g_build_path("/", globs->curr_dir, sSrcName, NULL); else src = g_strdup(sSrcName); printf("(II) VFSCopyOut: new src path: '%s'\n", src); // printf("(II) VFSCopyOut: pCallBackProgress = 0x%lX, data = 0x%lX\n", (unsigned long int)pCallBackProgress, (unsigned long int)data); globs->extract_callback_data = data; globs->extract_callback_func = pCallBackProgress; HANDLE PASCAL handle; struct RAROpenArchiveDataEx *archive_data; archive_data = (struct RAROpenArchiveDataEx*) malloc(sizeof(struct RAROpenArchiveDataEx)); memset(archive_data, 0, sizeof(struct RAROpenArchiveDataEx)); archive_data->ArcName = globs->archive_path; archive_data->CmtBuf = NULL; archive_data->CmtBufSize = 0; archive_data->OpenMode = RAR_OM_EXTRACT; handle = RAROpenArchiveEx(archive_data); // printf(" handle = %lu \n", (unsigned long int)handle); // printf(" archive_data->OpenResult = %d \n", archive_data->OpenResult); // printf(" archive_data->CmtState = %d \n", archive_data->CmtState); // printf(" archive_data->CmtSize = %d \n", archive_data->CmtSize); // printf("sizeof(TVFSResult) = %ld \n", sizeof(TVFSResult)); if ((handle) && (! archive_data->OpenResult)) { // Set callbacks // printf(" setting callback: globs = 0x%lX, (unsigned long int)globs = 0x%lX, (LONG)globs = 0x%lX\n", globs, (unsigned long int)globs, (LONG)globs); RARSetCallback(handle, unrar_callback, (LONG)globs); // RARSetChangeVolProc(handle, unrar_changevol_proc); // RARSetProcessDataProc(handle, unrar_process_data_proc); if (globs->password) { printf("(II) VFSCopyOut: Setting password... \n"); RARSetPassword(handle, globs->password); } struct RARHeaderDataEx *header; header = (struct RARHeaderDataEx*) malloc(sizeof(struct RARHeaderDataEx)); memset(header, 0, sizeof(struct RARHeaderDataEx)); header->CmtBuf = NULL; header->CmtBufSize = 0; int res = 0; while ((res = RARReadHeaderEx(handle, header)) == 0) { char *ssrc = src; char *asrc = header->FileName; if (IS_DIR_SEP(*ssrc)) ssrc++; if (IS_DIR_SEP(*asrc)) asrc++; if (strcmp(ssrc, asrc) == 0) { // fprintf(stderr, "(II) VFSCopyOut: extract_file_path(sDstName) = '%s', extract_file_name(sDstName) = '%s' \n", extract_file_path(sDstName), extract_file_name(sDstName)); globs->extract_done = 0; globs->extract_file_size = (u_int64_t)((u_int64_t)(header->UnpSizeHigh * 0x100000000) + (u_int64_t)header->UnpSize); globs->extract_cancelled = FALSE; int res2 = RARProcessFile(handle, RAR_EXTRACT, NULL, (char *)sDstName); if (globs->extract_cancelled) Result = cVFS_Cancelled; else if (res2) { fprintf(stderr, "(EE) VFSCopyOut: RARProcessFile result = %d\n", res2); Result = cVFS_ReadErr; } break; } else { int res2 = RARProcessFile(handle, RAR_SKIP, NULL, NULL); if (res2) { fprintf(stderr, "(EE) VFSCopyOut: RARProcessFile result = %d\n", res2); Result = cVFS_ReadErr; } } } if ((res != ERAR_END_ARCHIVE) && (res)) { fprintf(stderr, "(EE) VFSCopyOut: RARReadHeader result = %d\n", res); switch (res) { case ERAR_NO_MEMORY: case ERAR_SMALL_BUF: Result = cVFS_mallocFailed; break; case ERAR_BAD_DATA: case ERAR_BAD_ARCHIVE: case ERAR_UNKNOWN_FORMAT: case ERAR_EOPEN: case ERAR_ECLOSE: case ERAR_EREAD: Result = cVFS_ReadErr; break; case ERAR_ECREATE: case ERAR_EWRITE: Result = cVFS_WriteErr; break; case ERAR_MISSING_PASSWORD: Result = cVFS_BadPassword; break; case ERAR_UNKNOWN: default: Result = cVFS_WriteErr; break; } } free(header); res = RARCloseArchive(handle); if (res) { fprintf(stderr, "(EE) VFSCopyOut: RARCloseArchive result = %d\n", res); Result = cVFS_ReadErr; } } else { fprintf(stderr, "(EE) VFSCopyOut: error occured when opening archive: OpenResult = %d\n", archive_data->OpenResult); Result = cVFS_ReadErr; } free(archive_data); g_free(src); fprintf(stderr, "(II) VFSCopyOut: finished. \n"); return Result; } TVFSResult VFSCopyIn(struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, TVFSCopyCallBackFunc pCallBackProgress, void *data, int Append) { printf("(WW) VFSCopyIn: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } /********** * TODO: * * - UTF-8, FName/FDisplayName and absolute/relative paths revision needed! * - no error reporting when archive is corrupted * - archive testing (needs new VFS API) * ***/