static int UnzipFile (unzFile uf, void* buffer, const size_t bufferSize, const char* outPath, const bool skipPaths, const bool overwrite, const char* password) { unz_global_info64 gi; uLong i; int res; if (unzGetGlobalInfo64(uf, &gi) != UNZ_OK) { return TRI_ERROR_INTERNAL; } for (i = 0; i < gi.number_entry; i++) { res = ExtractCurrentFile(uf, buffer, bufferSize, outPath, skipPaths, overwrite, password); if (res != TRI_ERROR_NO_ERROR) { break; } if ((i + 1) < gi.number_entry) { if (unzGoToNextFile(uf) != UNZ_OK) { res = TRI_ERROR_INTERNAL; break; } } } return res; }
extern "C" bool ArchGetFileW(HZIP hZip,WCHAR *pstrFile,byte **lpMem,int *dwSize) { bool r=false; ZIPDECOMPRESSION *p=(ZIPDECOMPRESSION *)hZip; if ((hZip) && (p->bHandleType == HT_DECOMPRESSOR) && (pstrFile) && (lpMem) && (dwSize)) { LPCSTR pFileName=UnicodeToOemEx(pstrFile,-1); unzGoToFirstFile(p->hZip); p->bExctractToMem=true; if (unzLocateFile(p->hZip,pFileName,CASESENSITIVITY) == UNZ_OK) r=(ExtractCurrentFile(hZip,true) > -1) ? true : false; else { ArchSetLastError(ARCH_FILE_NOT_FOUND); r=false; } MemFree((void*)pFileName); if (r) { *lpMem=(byte*)p->lpMem; *dwSize=p->dwSize; } } else ArchSetLastError(ARCH_INVALID_PARAMETER); return r; }
extern "C" bool ArchExtractFileW(HZIP hZip,WCHAR *pstrPath,WCHAR *lpstrFile) { bool r=false; ZIPDECOMPRESSION *p=(ZIPDECOMPRESSION*)hZip; if ((hZip) && (p->bHandleType == HT_DECOMPRESSOR) && (pstrPath) && (lpstrFile)) { LPCSTR pFileName=UnicodeToAnsiEx(lpstrFile,-1); lstrcpyW(p->strOutputDir,pstrPath); unzGoToFirstFile(p->hZip); if (unzLocateFile(p->hZip,pFileName,CASESENSITIVITY) == UNZ_OK) r=(ExtractCurrentFile(hZip,true) > -1) ? true : false; else { ArchSetLastError(ARCH_FILE_NOT_FOUND); r=false; } MemFree(lpstrFile); } else ArchSetLastError(ARCH_INVALID_PARAMETER); return r; }
int InternalEnumFiles(HZIP hZip) { int r=-1; unz_global_info64 gi; ZIPDECOMPRESSION *p=(ZIPDECOMPRESSION*)hZip; int err=unzGetGlobalInfo64(p->hZip,&gi); if (err == UNZ_OK) { for (uLong i=0; i<gi.number_entry; i++) { char filename_inzip[256]; unz_file_info64 file_info; err=unzGetCurrentFileInfo64(p->hZip,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0); if (err == UNZ_OK) { LPWSTR sourceFile=OemToUnicodeEx(filename_inzip,-1); if (p->bOnlyEnum) { if (!p->bEnumAnsi) r=(p->lpEnumProc(sourceFile)) ? 0 : -1; else { char ansi_filename_inzip[256]; OemToAnsi(filename_inzip,ansi_filename_inzip); r=(p->lpEnumProc((LPWSTR)ansi_filename_inzip)) ? 0 : -1; } } else r=ExtractCurrentFile(hZip,false); MemFree(sourceFile); } unzGoToNextFile(p->hZip); } } return r; }
static int UnzipFile(unzFile uf, void* buffer, size_t const bufferSize, char const* outPath, bool const skipPaths, bool const overwrite, char const* password, std::string& errorMessage) { unz_global_info64 gi; uLong i; int res = TRI_ERROR_NO_ERROR; int err; err = unzGetGlobalInfo64(uf, &gi); if (err != UNZ_OK) { errorMessage = "Failed to get info: " + std::to_string(err); return TRI_ERROR_INTERNAL; } for (i = 0; i < gi.number_entry; i++) { res = ExtractCurrentFile(uf, buffer, bufferSize, outPath, skipPaths, overwrite, password, errorMessage); if (res != TRI_ERROR_NO_ERROR) { break; } if ((i + 1) < gi.number_entry) { err = unzGoToNextFile(uf); if (err == UNZ_END_OF_LIST_OF_FILE) { break; } else if (err != UNZ_OK) { errorMessage = "Failed to jump to next file: " + std::to_string(err); res = TRI_ERROR_INTERNAL; break; } } } return res; }
EXTRACT_ARC_CODE CmdExtract::ExtractArchive() { Archive Arc(Cmd); if (!Arc.WOpen(ArcName)) return EXTRACT_ARC_NEXT; if (!Arc.IsArchive(true)) { #ifndef GUI mprintf(St(MNotRAR),ArcName); #endif if (CmpExt(ArcName,L"rar")) ErrHandler.SetErrorCode(RARX_WARNING); return EXTRACT_ARC_NEXT; } if (Arc.FailedHeaderDecryption) // Bad archive password. return EXTRACT_ARC_NEXT; #ifndef SFX_MODULE if (Arc.Volume && !Arc.FirstVolume) { wchar FirstVolName[NM]; VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering); // If several volume names from same volume set are specified // and current volume is not first in set and first volume is present // and specified too, let's skip the current volume. if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && Cmd->ArcNames.Search(FirstVolName,false)) return EXTRACT_ARC_NEXT; } #endif int64 VolumeSetSize=0; // Total size of volumes after the current volume. if (Arc.Volume) { // Calculate the total size of all accessible volumes. // This size is necessary to display the correct total progress indicator. wchar NextName[NM]; wcscpy(NextName,Arc.FileName); while (true) { // First volume is already added to DataIO.TotalArcSize // in initial TotalArcSize calculation in DoExtract. // So we skip it and start from second volume. NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); FindData FD; if (FindFile::FastFind(NextName,&FD)) VolumeSetSize+=FD.Size; else break; } DataIO.TotalArcSize+=VolumeSetSize; } ExtractArchiveInit(Arc); if (*Cmd->Command=='T' || *Cmd->Command=='I') Cmd->Test=true; if (*Cmd->Command=='I') { #ifndef GUI Cmd->DisablePercentage=true; #endif } else uiStartArchiveExtract(!Cmd->Test,ArcName); Arc.ViewComment(); while (1) { size_t Size=Arc.ReadHeader(); bool Repeat=false; if (!ExtractCurrentFile(Arc,Size,Repeat)) { if (Repeat) { // If we started extraction from not first volume and need to // restart it from first, we must correct DataIO.TotalArcSize // for correct total progress display. We subtract the size // of current volume and all volumes after it and add the size // of new (first) volume. FindData OldArc,NewArc; if (FindFile::FastFind(Arc.FileName,&OldArc) && FindFile::FastFind(ArcName,&NewArc)) DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size; return EXTRACT_ARC_REPEAT; } else break; } } return EXTRACT_ARC_NEXT; }
int vtUnzip::Extract(bool bFullPath, bool bOverwrite, const char *lpszDst, bool progress_callback(int)) { int iCount = 0; int iTotal = GetGlobalCount(); bool bOK = true; for (bool bContinue = GoToFirstFile(); bContinue; bContinue = GoToNextFile()) { char szFileName[MAX_PATH]; unz_file_info info; bOK = GetCurrentFileInfo(&info, szFileName, MAX_PATH); if (!bOK) break; vtString src_filename = szFileName; const char *short_fname = (const char *)src_filename; for (const char *p = short_fname; (*p) != '\0'; p++) { if (((*p)=='/') || ((*p)=='\\')) { short_fname = p+1; } } vtString short_filename = short_fname; if ((*short_filename)=='\0') { if (bFullPath) { VTLOG("creating directory: %s\n", (const char *)src_filename); vtCreateDir(src_filename); } } else { bOK = OpenCurrentFile(); if (bOK) { vtString write_filename; write_filename = vtString(lpszDst) + (bFullPath ? src_filename : short_filename); vtString strResult; VTLOG("Extracting %s ...", (const char *) write_filename); if (ExtractAccept(write_filename, bOverwrite)) { FILE* file = vtFileOpen(write_filename, "wb"); bool bWrite = (file != NULL); if (bWrite) { char buf[4096]; bWrite = ExtractCurrentFile(file, buf, 4096); fclose(file); } if (bWrite) { vtUnzip::change_file_date(write_filename,info.dosDate, info.tmu_date); iCount++; if (progress_callback != NULL) { progress_callback(iCount * 99 / iTotal); } } else { m_error_count++; OnError(write_filename); } strResult = bWrite ? "ok" : "failed"; } else { strResult = "skipped"; } VTLOG(" %s\n", (const char *) strResult); CloseCurrentFile(); } } } if (bOK) return iCount; else return -1; }
bool DialogInstall::InstallPackage() { if ((!m_MergeSkins && m_BackupSkins) || m_BackupPackage) { // Move skins into backup folder for (auto iter = m_PackageSkins.cbegin(); iter != m_PackageSkins.cend(); ++iter) { std::wstring from = g_Data.skinsPath + *iter; if (_waccess(from.c_str(), 0) == -1) { continue; } SHFILEOPSTRUCT fo = { nullptr, FO_DELETE, nullptr, nullptr, FOF_NO_UI | FOF_NOCONFIRMATION | FOF_ALLOWUNDO }; if (m_BackupPackage) { // Remove current skin from += L'\0'; fo.pFrom = from.c_str(); SHFileOperation(&fo); } else { std::wstring to = g_Data.skinsPath + L"@Backup\\"; CreateDirectory(to.c_str(), nullptr); // Delete current backup to += *iter; to += L'\0'; fo.pFrom = to.c_str(); SHFileOperation(&fo); if (!CopyFiles(from, to, true)) { m_ErrorMessage = L"Unable to move to:\n"; m_ErrorMessage += to; return false; } } } } WCHAR buffer[MAX_PATH]; // Helper to sets buffer with current file name auto getFileInfo = [&]()->bool { char cBuffer[MAX_PATH * 3]; unz_file_info ufi; if (unzGetCurrentFileInfo( m_PackageUnzFile, &ufi, cBuffer, _countof(cBuffer), nullptr, 0, nullptr, 0) == UNZ_OK) { const uLong ZIP_UTF8_FLAG = 1 << 11; const DWORD codePage = (ufi.flag & ZIP_UTF8_FLAG) ? CP_UTF8 : CP_ACP; MultiByteToWideChar(codePage, 0, cBuffer, strlen(cBuffer) + 1, buffer, MAX_PATH); while (WCHAR* pos = wcschr(buffer, L'/')) *pos = L'\\'; return true; } return false; }; unzGoToFirstFile(m_PackageUnzFile); const WCHAR* root = m_PackageRoot.c_str(); do { if (!getFileInfo()) { m_ErrorMessage = L"Error retrieving file info."; return false; } if (wcsncmp(buffer, root, m_PackageRoot.length()) != 0) { // Ignore everything that isn't in the root directory continue; } WCHAR* component = buffer + m_PackageRoot.length(); WCHAR* path = wcschr(component, L'\\'); if (path) { *path = L'\0'; ++path; } else { continue; } bool error = false; std::wstring targetPath; WCHAR* pos = wcschr(path, L'\\'); WCHAR* extension = PathFindExtension(pos ? pos : path); if (pos) { const std::wstring item(path, pos - path); if (_wcsicmp(component, L"Skins") == 0 && m_PackageSkins.find(item) != m_PackageSkins.end()) { targetPath = g_Data.skinsPath; } else if (_wcsicmp(component, L"Addons") == 0 && m_PackageFormat == PackageFormat::Old && m_PackageAddons.find(item) != m_PackageAddons.end()) { targetPath = g_Data.settingsPath; targetPath += L"Addons\\"; } else if (_wcsicmp(component, L"Plugins") == 0 && _wcsnicmp(path, IsWin32Build() ? L"32bit" : L"64bit", pos - path) == 0 && _wcsicmp(extension, L".dll") == 0 && !wcschr(pos + 1, L'\\')) { const std::wstring plugin(pos + 1); if (m_PackagePlugins.find(plugin) != m_PackagePlugins.end()) { path = pos + 1; targetPath = g_Data.settingsPath; targetPath += L"Plugins\\"; } } if (!targetPath.empty()) { targetPath += path; error = !ExtractCurrentFile(targetPath); } else if (_wcsicmp(component, m_PackageFormat == PackageFormat::New ? L"Layouts" : L"Themes") == 0 && _wcsicmp(extension, m_PackageFormat == PackageFormat::New ? L".ini" : L".thm") == 0 && m_PackageLayouts.find(item) != m_PackageLayouts.end()) { if (m_PackageFormat == PackageFormat::Old) { wcscpy_s(extension, 5, L".ini"); } targetPath = g_Data.settingsPath; targetPath += L"Layouts\\"; targetPath += path; error = !ExtractCurrentFile(targetPath); if (!error) { CleanLayoutFile(targetPath.c_str()); } } } else { if (_wcsicmp(component, L"Fonts") == 0 && m_PackageFormat == PackageFormat::Old && _wcsicmp(extension, L".ttf") == 0) { for (auto iter = m_PackageSkins.cbegin(); iter != m_PackageSkins.cend(); ++iter) { targetPath = g_Data.skinsPath; targetPath += *iter; targetPath += L"\\@Resources\\Fonts\\"; targetPath += path; error = !ExtractCurrentFile(targetPath); if (error) { break; } } } } if (error) { m_ErrorMessage = L"Unable to create file:\n"; m_ErrorMessage += targetPath; m_ErrorMessage += L"\n\nSkin Installer will now quit."; return false; } } while (unzGoToNextFile(m_PackageUnzFile) == UNZ_OK); if (!m_MergeSkins && m_BackupSkins) { KeepVariables(); } return true; }
bool DialogInstall::ReadPackage() { const WCHAR* fileName = m_PackageFileName.c_str(); const WCHAR* fileExtension = PathFindExtension(fileName); if (_wcsicmp(fileExtension, L".rmskin") == 0) { // Check if the footer is present (for new .rmskin format) PackageFooter footer = {0}; FILE* file = _wfopen(fileName, L"rb"); __int64 fileSize = 0; if (file) { fseek(file, -(long)sizeof(footer), SEEK_END); fileSize = _ftelli64(file); fread(&footer, sizeof(footer), 1, file); fclose(file); } if (strcmp(footer.key, "RMSKIN") == 0) { m_PackageFormat = PackageFormat::New; if (footer.size != fileSize) { return false; } if (footer.flags) { m_BackupPackage = !(footer.flags & PackageFlag::Backup); } } } else if (_wcsicmp(fileExtension, L".zip") != 0) { return false; } m_PackageUnzFile = unzOpen(ConvertToAscii(fileName).c_str()); if (!m_PackageUnzFile) { return false; } WCHAR buffer[MAX_PATH]; // Get temporary file to extract the options file and header bitmap GetTempPath(MAX_PATH, buffer); GetTempFileName(buffer, L"dat", 0, buffer); std::wstring tempFile = buffer; const WCHAR* tempFileSz = tempFile.c_str(); // Helper to sets buffer with current file name auto getFileInfo = [&]()->bool { char cBuffer[MAX_PATH * 3]; unz_file_info ufi; if (unzGetCurrentFileInfo( m_PackageUnzFile, &ufi, cBuffer, _countof(cBuffer), nullptr, 0, nullptr, 0) == UNZ_OK) { const uLong ZIP_UTF8_FLAG = 1 << 11; const DWORD codePage = (ufi.flag & ZIP_UTF8_FLAG) ? CP_UTF8 : CP_ACP; MultiByteToWideChar(codePage, 0, cBuffer, strlen(cBuffer) + 1, buffer, MAX_PATH); while (WCHAR* pos = wcschr(buffer, L'/')) *pos = L'\\'; return true; } return false; }; // Loop through the contents of the archive until the settings file is found WCHAR* path; bool optionsFound = false; do { if (!getFileInfo()) { return false; } path = wcsrchr(buffer, L'\\'); if (!path) { path = buffer; } else { if (m_PackageFormat == PackageFormat::New) { // New package files must be in root of archive continue; } ++path; // Skip slash } if (_wcsicmp(path, m_PackageFormat == PackageFormat::New ? L"RMSKIN.ini" : L"Rainstaller.cfg") == 0) { if (ExtractCurrentFile(tempFile)) { optionsFound = ReadOptions(tempFileSz); DeleteFile(tempFileSz); } break; } } while (unzGoToNextFile(m_PackageUnzFile) == UNZ_OK); if (!optionsFound) { return false; } // Loop through the archive a second time and find included components unzGoToFirstFile(m_PackageUnzFile); m_PackageRoot.assign(buffer, path - buffer); const WCHAR* root = m_PackageRoot.c_str(); do { if (!getFileInfo()) { return false; } if (wcsncmp(buffer, root, m_PackageRoot.length()) != 0) { // Ignore everything that isn't in the root directory continue; } WCHAR* component = buffer + m_PackageRoot.length(); path = wcschr(component, L'\\'); if (path) { *path = L'\0'; ++path; } else { if (_wcsicmp(component, m_PackageFormat == PackageFormat::New ? L"RMSKIN.bmp" : L"Rainstaller.bmp") == 0) { if (!ExtractCurrentFile(tempFile)) { return false; } m_HeaderBitmap = (HBITMAP)LoadImage(nullptr, tempFileSz, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); DeleteFile(tempFileSz); } continue; } const WCHAR* pos = wcschr(path, L'\\'); const WCHAR* extension = PathFindExtension(pos ? pos : path); if (pos) { // Component with subfolders const std::wstring item(path, pos - path); const WCHAR* itemSz = item.c_str(); if (_wcsicmp(component, L"Skins") == 0 && !IsIgnoredSkin(itemSz)) { m_PackageSkins.insert(item); } else if (_wcsicmp(component, m_PackageFormat == PackageFormat::New ? L"Layouts" : L"Themes") == 0 && _wcsicmp(extension, m_PackageFormat == PackageFormat::New ? L".ini" : L".thm") == 0 && !IsIgnoredLayout(itemSz)) { m_PackageLayouts.insert(item); } else if (_wcsicmp(component, L"Addons") == 0 && m_PackageFormat == PackageFormat::Old && !IsIgnoredAddon(itemSz)) { m_PackageAddons.insert(item); } else if (_wcsicmp(component, L"Plugins") == 0 && _wcsicmp(itemSz, IsWin32Build() ? L"32bit" : L"64bit") == 0 && _wcsicmp(extension, L".dll") == 0 && !wcschr(pos + 1, L'\\')) { const std::wstring plugin(pos + 1); if (!IsIgnoredPlugin(plugin.c_str())) { m_PackagePlugins.insert(plugin); } } } else { // Component with subfiles const std::wstring item = path; const WCHAR* itemSz = item.c_str(); if (_wcsicmp(component, L"Fonts") == 0 && m_PackageFormat == PackageFormat::Old && _wcsicmp(extension, L".ttf") == 0) { m_PackageFonts.insert(item); } } } while (unzGoToNextFile(m_PackageUnzFile) == UNZ_OK); if (m_PackageSkins.empty()) { // Fonts can be installed only with skins m_PackageFonts.clear(); } return !(m_PackageSkins.empty() && m_PackageLayouts.empty() && m_PackageAddons.empty() && m_PackageFonts.empty() && m_PackagePlugins.empty()); }
EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd) { Archive Arc(Cmd); if (!Arc.WOpen(ArcName,ArcNameW)) { ErrHandler.SetErrorCode(OPEN_ERROR); return(EXTRACT_ARC_NEXT); } if (!Arc.IsArchive(true)) { #ifndef GUI mprintf(St(MNotRAR),ArcName); #endif if (CmpExt(ArcName,"rar")) ErrHandler.SetErrorCode(WARNING); return(EXTRACT_ARC_NEXT); } // archive with corrupt encrypted header can be closed in IsArchive() call if (!Arc.IsOpened()) return(EXTRACT_ARC_NEXT); #ifndef SFX_MODULE if (Arc.Volume && Arc.NotFirstVolume) { char FirstVolName[NM]; VolNameToFirstName(ArcName,FirstVolName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)!=0); // If several volume names from same volume set are specified // and current volume is not first in set and first volume is present // and specified too, let's skip the current volume. if (stricomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && Cmd->ArcNames->Search(FirstVolName,NULL,false)) return(EXTRACT_ARC_NEXT); } #endif int64 VolumeSetSize=0; // Total size of volumes after the current volume. if (Arc.Volume) { // Calculate the total size of all accessible volumes. // This size is necessary to display the correct total progress indicator. char NextName[NM]; wchar NextNameW[NM]; strcpy(NextName,Arc.FileName); strcpyw(NextNameW,Arc.FileNameW); while (true) { // First volume is already added to DataIO.TotalArcSize // in initial TotalArcSize calculation in DoExtract. // So we skip it and start from second volume. NextVolumeName(NextName,NextNameW,ASIZE(NextName),(Arc.NewMhd.Flags & MHD_NEWNUMBERING)==0 || Arc.OldFormat); struct FindData FD; if (FindFile::FastFind(NextName,NextNameW,&FD)) VolumeSetSize+=FD.Size; else break; } DataIO.TotalArcSize+=VolumeSetSize; } ExtractArchiveInit(Cmd,Arc); if (*Cmd->Command=='T' || *Cmd->Command=='I') Cmd->Test=true; #ifndef GUI if (*Cmd->Command=='I') Cmd->DisablePercentage=true; else if (Cmd->Test) mprintf(St(MExtrTest),ArcName); else mprintf(St(MExtracting),ArcName); #endif Arc.ViewComment(); // RAR can close a corrupt encrypted archive if (!Arc.IsOpened()) return(EXTRACT_ARC_NEXT); while (1) { size_t Size=Arc.ReadHeader(); bool Repeat=false; if (!ExtractCurrentFile(Cmd,Arc,Size,Repeat)) if (Repeat) { // If we started extraction from not first volume and need to // restart it from first, we must correct DataIO.TotalArcSize // for correct total progress display. We subtract the size // of current volume and all volumes after it and add the size // of new (first) volume. struct FindData OldArc,NewArc; if (FindFile::FastFind(Arc.FileName,Arc.FileNameW,&OldArc) && FindFile::FastFind(ArcName,ArcNameW,&NewArc)) DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size; return(EXTRACT_ARC_REPEAT); } else break; } return(EXTRACT_ARC_NEXT); }
EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd) { Archive Arc(Cmd); if (!Arc.WOpen(ArcName,ArcNameW)) { ErrHandler.SetErrorCode(OPEN_ERROR); return(EXTRACT_ARC_NEXT); } if (!Arc.IsArchive(true)) { #ifndef GUI mprintf(St(MNotRAR),ArcName); #endif if (CmpExt(ArcName,"rar")) ErrHandler.SetErrorCode(WARNING); return(EXTRACT_ARC_NEXT); } if (!Arc.IsOpened()) return(EXTRACT_ARC_NEXT); #ifndef SFX_MODULE if (Arc.Volume && Arc.NotFirstVolume) { char FirstVolName[NM]; VolNameToFirstName(ArcName,FirstVolName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)); if (stricomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && Cmd->ArcNames->Search(FirstVolName,NULL,false)) return(EXTRACT_ARC_NEXT); } #endif ExtractArchiveInit(Cmd,Arc); if (*Cmd->Command=='T' || *Cmd->Command=='I') Cmd->Test=true; #ifndef GUI if (*Cmd->Command=='I') Cmd->DisablePercentage=true; else if (Cmd->Test) mprintf(St(MExtrTest),ArcName); else mprintf(St(MExtracting),ArcName); #endif Arc.ViewComment(); while (1) { int Size=Arc.ReadHeader(); bool Repeat=false; if (!ExtractCurrentFile(Cmd,Arc,Size,Repeat)) if (Repeat) { return(EXTRACT_ARC_REPEAT); } else break; } return(EXTRACT_ARC_NEXT); }