Mercurial > vba-clojure
view src/win32/7zip/OpenArchive.cpp @ 5:8fe0c57e53d2
concentrating on lua first
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 03 Mar 2012 10:39:40 -0600 |
parents | f9f4f1b99eed |
children |
line wrap: on
line source
1 #include "../stdafx.h"2 #include "../resource.h"3 #include <windows.h>4 #include <mmsystem.h>5 #include <cstdio>6 #include <cerrno>7 #include <cassert>8 #include <cstring>9 #include <map>10 #include <vector>11 #include <algorithm>12 #include "7zip.h"13 //#include "G_main.h"14 //#include "G_dsound.h"15 #include "OpenArchive.h"17 LRESULT CALLBACK ArchiveFileChooser(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);18 static int s_archiveFileChooserResult = -1;20 static HWND s_parentHWND = NULL;21 void SetArchiveParentHWND(void* hwnd) { s_parentHWND = (HWND)hwnd; }22 static HWND GetArchiveParentHWND() { return s_parentHWND ? s_parentHWND : AfxGetApp()->m_pMainWnd->GetSafeHwnd(); }24 static char Str_Tmp [2048];26 struct ArchiveFileChooserInfo27 {28 ArchiveFileChooserInfo(ArchiveFile& theArchive, const char** ignoreExtensions, int& numIgnoreExtensions) : archive(theArchive)29 {30 tryagain:31 int numItems = archive.GetNumItems();32 for(int i = 0; i < numItems; i++)33 {34 if(archive.GetItemSize(i))35 {36 const char* name = archive.GetItemName(i);37 const char* ext = strrchr(name, '.');38 bool ok = true;39 if(ext++)40 {41 for(int j = 0; j < numIgnoreExtensions; j++)42 {43 const char* ext2 = ignoreExtensions[j];44 const char* wild = strchr(ext2, '*');45 if(!wild)46 {47 if(!_stricmp(ext, ext2))48 {49 ok = false;50 break;51 }52 }53 else // very limited (end only) wildcard support54 {55 if(!_strnicmp(ext, ext2, wild - ext2))56 {57 ok = false;58 break;59 }60 }61 }62 }63 if(ok)64 {65 ArchiveFileChooserInfo::FileInfo fi = { name, i };66 files.push_back(fi);67 }68 }69 }71 if(files.empty() && numIgnoreExtensions)72 {73 // try again without any exclusions if we excluded everything in the archive74 numIgnoreExtensions = 0;75 goto tryagain;76 }78 // strip away prefix paths that are common to all the files79 bool stripping = !files.empty();80 while(stripping)81 {82 const char* firstName = files[0].name.c_str();83 const char* slash = strchr(firstName, '\\');84 const char* slash2 = strchr(firstName, '/');85 slash = max(slash, slash2);86 if(!slash++)87 break;88 for(size_t i = 1; i < files.size(); i++)89 if(strncmp(firstName, files[i].name.c_str(), slash - firstName))90 stripping = false;91 if(stripping)92 for(size_t i = 0; i < files.size(); i++)93 files[i].name = files[i].name.substr(slash - firstName, files[i].name.length() - (slash - firstName));94 }96 // sort by filename97 std::sort(files.begin(), files.end(), FileInfo::Sort);98 }100 //protected:102 struct FileInfo103 {104 std::string name;105 int itemIndex;107 static bool Sort(const FileInfo& elem1, const FileInfo& elem2)108 {109 int comp = elem1.name.compare(elem2.name);110 return comp ? (comp < 0) : (elem1.itemIndex < elem2.itemIndex);111 }112 };114 ArchiveFile& archive;115 std::vector<FileInfo> files;116 };118 static void ClearLayoutStates();120 int ChooseItemFromArchive(ArchiveFile& archive, bool autoChooseIfOnly1, const char** ignoreExtensions, int numIgnoreExtensions)121 {122 int prevNumIgnoreExtensions = numIgnoreExtensions;123 archive.m_userMadeSelection = false;125 // prepare a list of files to choose from the archive126 ArchiveFileChooserInfo info (archive, ignoreExtensions, numIgnoreExtensions);128 // based on our list, decide which item in the archive to choose130 // check if there's nothing131 if(info.files.size() < 1)132 {133 MessageBox(GetArchiveParentHWND(), "The archive is either empty or encrypted.", "Nothing to load!", MB_OK | MB_ICONWARNING);134 return -1;135 }137 // warn if all the files in the archive have extensions we should ignore138 if(numIgnoreExtensions != prevNumIgnoreExtensions)139 {140 CString msg;141 msg.Format("The archive appears to only contain the wrong type of files.\n\n(in \"%s\")", archive.GetArchiveFileName());142 int answer = MessageBox(GetArchiveParentHWND(), msg, "Warning", MB_OKCANCEL | MB_ICONWARNING | MB_DEFBUTTON2);143 if(answer == IDCANCEL)144 return -1;145 }147 // if there's only 1 item, choose it148 if(info.files.size() == 1 && autoChooseIfOnly1 && numIgnoreExtensions == prevNumIgnoreExtensions)149 return info.files[0].itemIndex;151 // bring up a dialog to choose the index if there's more than 1152 DialogBoxParam(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDD_ARCHIVEFILECHOOSER), GetArchiveParentHWND(), (DLGPROC) ArchiveFileChooser,(LPARAM) &info);153 archive.m_userMadeSelection = (s_archiveFileChooserResult != -1);154 return s_archiveFileChooserResult;155 }160 #define DEFAULT_EXTENSION ".tmp"161 #define DEFAULT_CATEGORY "vba"163 static struct TempFiles164 {165 struct TemporaryFile166 {167 TemporaryFile(const char* cat, const char* ext)168 {169 if(!ext || !*ext) ext = DEFAULT_EXTENSION;170 if(!cat || !*cat) cat = DEFAULT_CATEGORY;171 category = cat;173 char tempPath [2048];174 GetTempPath(2048, tempPath);175 //GetTempFileName(tempPath, cat, 0, filename, ext); // alas177 char*const fname = tempPath + strlen(tempPath);178 unsigned short start = (unsigned short)(timeGetTime() & 0xFFFF);179 unsigned short n = start + 1;180 while(n != start)181 {182 _snprintf(fname, 2048 - (fname - tempPath), "%s%04X%s", cat, n, ext);183 FILE* file = fopen(tempPath, "wb");184 if(file)185 {186 // mark the temporary file as read-only and (whatever this does) temporary187 DWORD attributes = GetFileAttributes(tempPath);188 attributes |= FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_TEMPORARY;189 SetFileAttributes(tempPath, attributes);191 fclose(file);193 // add it to our registry of files that need to be deleted, in case we fail to terminate properly194 TempFiles::AddEntryToGarbageRegistry(tempPath);196 break;197 }198 n++;199 }200 strcpy(filename, tempPath);201 }202 TemporaryFile(const TemporaryFile& copy)203 {204 strcpy(filename, copy.filename);205 category = copy.category;206 }207 TemporaryFile()208 {209 filename[0] = 0;210 // category[0] = 0; // error211 }212 bool Delete(bool returnFalseOnRegistryRemovalFailure=false)213 {214 if(!*filename)215 return true; // guess it already didn't exist217 // remove read-only attribute so Windows will let us delete it218 // (our temporary files are read-only to discourage other apps from tampering)219 DWORD attributes = GetFileAttributes(filename);220 if(attributes & FILE_ATTRIBUTE_READONLY)221 SetFileAttributes(filename, attributes & ~FILE_ATTRIBUTE_READONLY);223 if(_unlink(filename) == 0 || errno != EACCES)224 {225 // remove it from our registry of files that need to be deleted, to reduce accumulation226 bool removed = TempFiles::RemoveEntryFromGarbageRegistry(filename);228 *filename = '\0';229 return removed || !returnFalseOnRegistryRemovalFailure; // successfully deleted or already didn't exist, return true unless registry removal failure notification was requested and that failed230 }232 // restore read-only if we couldn't delete it (not sure if this ever succeeds or matters though)233 if(attributes & FILE_ATTRIBUTE_READONLY)234 SetFileAttributes(filename, attributes);236 return false; // failed to delete read-only or in-use file237 }238 char filename [MAX_PATH];239 std::string category;240 };242 std::vector<TemporaryFile> tempFiles;244 const char* GetFile(const char* category, const char* extension)245 {246 tempFiles.push_back(TemporaryFile(category, extension));247 return tempFiles.back().filename;248 }250 void ReleaseFile(const char* filename)251 {252 for(int i = (int)tempFiles.size()-1; i >= 0; i--)253 {254 if(!strcmp(filename, tempFiles[i].filename))255 {256 if(tempFiles[i].Delete())257 tempFiles.erase(tempFiles.begin() + i);258 }259 }260 }262 void ReleaseCategory(const char* cat, const char* exceptionFilename)263 {264 for(int i = (int)tempFiles.size()-1; i >= 0; i--)265 {266 if(!strcmp(cat, tempFiles[i].category.c_str()) &&267 (!exceptionFilename ||268 strcmp(exceptionFilename, tempFiles[i].filename)))269 {270 if(tempFiles[i].Delete())271 tempFiles.erase(tempFiles.begin() + i);272 }273 }274 }276 // delete all temporary files on shutdown277 ~TempFiles()278 {279 for(size_t i = 0; i < tempFiles.size(); i++)280 {281 tempFiles[i].Delete();282 }284 TempFiles::CleanOutGarbageRegistry();285 }287 // run this on startup to delete any files that we failed to delete last time288 // in case we crashed or were forcefully terminated289 TempFiles()290 {291 TempFiles::CleanOutGarbageRegistry();292 }294 static void AddEntryToGarbageRegistry(const char* filename)295 {296 char gbgFile[2048];297 GetTempPath(2048, gbgFile);298 strcat(gbgFile, "VBATempFileRecords");299 char key[64];300 int i = 0;301 while(true)302 {303 sprintf(key, "File%d", i);304 GetPrivateProfileString("Files", key, "", Str_Tmp, 2048, gbgFile);305 if(!*Str_Tmp)306 break;307 i++;308 }309 WritePrivateProfileString("Files", key, filename, gbgFile);310 }311 static bool RemoveEntryFromGarbageRegistry(const char* filename)312 {313 char gbgFile[2048];314 GetTempPath(2048, gbgFile);315 strcat(gbgFile, "VBATempFileRecords");316 char key[64];317 int i = 0;318 int deleteSlot = -1;319 while(true)320 {321 sprintf(key, "File%d", i);322 GetPrivateProfileString("Files", key, "", Str_Tmp, 2048, gbgFile);323 if(!*Str_Tmp)324 break;325 if(!strcmp(Str_Tmp, filename))326 deleteSlot = i;327 i++;328 }329 --i;330 if(i >= 0 && deleteSlot >= 0)331 {332 if(i != deleteSlot)333 {334 sprintf(key, "File%d", i);335 GetPrivateProfileString("Files", key, "", Str_Tmp, 2048, gbgFile);336 sprintf(key, "File%d", deleteSlot);337 WritePrivateProfileString("Files", key, Str_Tmp, gbgFile);338 }339 sprintf(key, "File%d", i);340 if(0 == WritePrivateProfileString("Files", key, NULL, gbgFile))341 return false;342 }343 if(i <= 0 && deleteSlot == 0)344 _unlink(gbgFile);345 return true;346 }348 private:349 static void CleanOutGarbageRegistry()350 {351 char gbgFile[2048];352 GetTempPath(2048, gbgFile);353 strcat(gbgFile, "VBATempFileRecords");355 char key[64];356 int i = 0;357 while(true)358 {359 sprintf(key, "File%d", i);360 GetPrivateProfileString("Files", key, "", Str_Tmp, 2048, gbgFile);361 if(!*Str_Tmp)362 break;363 TemporaryFile temp;364 strcpy(temp.filename, Str_Tmp);365 if(!temp.Delete(true))366 i++;367 }368 }370 } s_tempFiles;373 const char* GetTempFile(const char* category, const char* extension)374 {375 return s_tempFiles.GetFile(category, extension);376 }377 void ReleaseTempFile(const char* filename)378 {379 s_tempFiles.ReleaseFile(filename);380 }381 void ReleaseTempFileCategory(const char* cat, const char* exceptionFilename)382 {383 if(!cat || !*cat) cat = DEFAULT_CATEGORY;384 s_tempFiles.ReleaseCategory(cat, exceptionFilename);385 }389 // example input Name: "C:\games.zip"390 // example output LogicalName: "C:\games.zip|Metroid.gba"391 // example output PhysicalName: "C:\Documents and Settings\User\Local Settings\Temp\VBA\rom7A37.gba"392 // assumes arguments are character buffers with 2048 bytes each393 bool ObtainFile(const char* Name, char *const & LogicalName, char *const & PhysicalName, const char* category, const char** ignoreExtensions, int numIgnoreExtensions)394 {395 restart:396 char ArchivePaths [2048];397 strcpy(LogicalName, Name);398 strcpy(PhysicalName, Name);399 strcpy(ArchivePaths, Name);400 char* bar = strchr(ArchivePaths, '|');401 if(bar)402 {403 PhysicalName[bar - ArchivePaths] = 0; // doesn't belong in the physical name404 LogicalName[bar - ArchivePaths] = 0; // we'll reconstruct the logical name as we go405 *bar++ = 0; // bar becomes the next logical archive path component406 }408 bool userSelected = false;410 while(true)411 {412 ArchiveFile archive (PhysicalName, LogicalName);413 if(!archive.IsCompressed())414 {415 if(archive.GetNumItems() > 0)416 return true;417 else418 {419 // failed or cancelled... backtrack to outermost archive if not already there420 char* div = NULL;421 if(LogicalName[strlen(LogicalName)-1] == '|')422 {423 LogicalName[strlen(LogicalName)-1] = '\0';424 div = strrchr(LogicalName, '|');425 }426 if(div && userSelected)427 goto restart;428 else429 return false;430 }431 }432 else433 {434 int item = -1;435 bool forceManual = false;436 if(bar && *bar) // try following the in-archive part of the logical path437 {438 char* bar2 = strchr(bar, '|');439 if(bar2) *bar2++ = 0;440 int numItems = archive.GetNumItems();441 for(int i = 0; i < numItems; i++)442 {443 if(archive.GetItemSize(i))444 {445 const char* itemName = archive.GetItemName(i);446 if(!_stricmp(itemName, bar))447 {448 item = i; // match found, now we'll auto-follow the path449 break;450 }451 }452 }453 if(item < 0)454 {455 forceManual = true; // we don't want it choosing something else without user permission456 bar = NULL; // remaining archive path is invalid457 }458 else459 bar = bar2; // advance to next archive path part460 }461 if(item < 0)462 item = ChooseItemFromArchive(archive, !forceManual, ignoreExtensions, numIgnoreExtensions);464 userSelected |= archive.m_userMadeSelection;466 const char* TempFileName = s_tempFiles.GetFile(category, strrchr(archive.GetItemName(item), '.'));467 if(!archive.ExtractItem(item, TempFileName))468 s_tempFiles.ReleaseFile(TempFileName);469 s_tempFiles.ReleaseFile(PhysicalName);470 strcpy(PhysicalName, TempFileName);471 _snprintf(LogicalName + strlen(LogicalName), 2048 - (strlen(LogicalName)+1), "|%s", archive.GetItemName(item));472 }473 }474 }478 struct ControlLayoutInfo479 {480 int controlID;482 enum LayoutType // what to do when the containing window resizes483 {484 NONE, // leave the control where it was485 RESIZE_END, // resize the control486 MOVE_START, // move the control487 };488 LayoutType horizontalLayout;489 LayoutType verticalLayout;490 };491 struct ControlLayoutState492 {493 int x,y,width,height;494 bool valid;495 ControlLayoutState() : valid(false) {}496 };498 static ControlLayoutInfo controlLayoutInfos [] = {499 {IDC_LIST1, ControlLayoutInfo::RESIZE_END, ControlLayoutInfo::RESIZE_END},500 {IDOK, ControlLayoutInfo::MOVE_START, ControlLayoutInfo::MOVE_START},501 {ID_CANCEL, ControlLayoutInfo::MOVE_START, ControlLayoutInfo::MOVE_START},502 };503 static const int numControlLayoutInfos = sizeof(controlLayoutInfos)/sizeof(*controlLayoutInfos);505 static ControlLayoutState s_layoutState [numControlLayoutInfos];506 static int s_windowWidth = 182, s_windowHeight = 113;509 LRESULT CALLBACK ArchiveFileChooser(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)510 {511 RECT r, r2;512 int dx1, dy1, dx2, dy2;513 static std::map<int,int> s_listToItemsMap;515 switch(uMsg)516 {517 case WM_INITDIALOG:518 {519 //Clear_Sound_Buffer();521 //if(Full_Screen)522 //{523 // while (ShowCursor(false) >= 0);524 // while (ShowCursor(true) < 0);525 //}527 for(int i = 0; i < numControlLayoutInfos; i++)528 s_layoutState[i].valid = false;530 GetWindowRect(AfxGetApp()->m_pMainWnd->GetSafeHwnd(), &r);531 dx1 = (r.right - r.left) / 2;532 dy1 = (r.bottom - r.top) / 2;534 GetWindowRect(hDlg, &r2);535 dx2 = (r2.right - r2.left) / 2;536 dy2 = (r2.bottom - r2.top) / 2;538 //SetWindowPos(hDlg, NULL, max(0, r.left + (dx1 - dx2)), max(0, r.top + (dy1 - dy2)), NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);539 SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);541 ArchiveFileChooserInfo& info = *(ArchiveFileChooserInfo*)lParam;542 std::vector<ArchiveFileChooserInfo::FileInfo>& files = info.files;543 ArchiveFile& archive = info.archive;545 std::string title = "Choose File in ";546 title += archive.GetArchiveTypeName();547 title += " Archive";548 SetWindowText(hDlg, title.c_str());550 // populate list551 for(size_t i = 0; i < files.size(); i++)552 {553 int listIndex = SendDlgItemMessage(hDlg, IDC_LIST1, LB_ADDSTRING, (WPARAM) 0, (LONG) (LPTSTR) files[i].name.c_str());554 s_listToItemsMap[listIndex] = files[i].itemIndex;555 }557 SendDlgItemMessage(hDlg, IDC_LIST1, LB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);559 {560 RECT r3;561 GetClientRect(hDlg, &r3);562 s_windowWidth = r3.right - r3.left;563 s_windowHeight = r3.bottom - r3.top;564 }566 return true;567 } break;569 case WM_SIZING:570 {571 // enforce a minimum window size573 LPRECT r = (LPRECT) lParam;574 int minimumWidth = 281;575 int minimumHeight = 117;576 if(r->right - r->left < minimumWidth)577 if(wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT)578 r->left = r->right - minimumWidth;579 else580 r->right = r->left + minimumWidth;581 if(r->bottom - r->top < minimumHeight)582 if(wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT)583 r->top = r->bottom - minimumHeight;584 else585 r->bottom = r->top + minimumHeight;586 return TRUE;587 }589 case WM_SIZE:590 {591 // resize or move controls in the window as necessary when the window is resized593 int prevDlgWidth = s_windowWidth;594 int prevDlgHeight = s_windowHeight;596 int dlgWidth = LOWORD(lParam);597 int dlgHeight = HIWORD(lParam);599 int deltaWidth = dlgWidth - prevDlgWidth;600 int deltaHeight = dlgHeight - prevDlgHeight;602 for(int i = 0; i < numControlLayoutInfos; i++)603 {604 ControlLayoutInfo layoutInfo = controlLayoutInfos[i];605 ControlLayoutState& layoutState = s_layoutState[i];607 HWND hCtrl = GetDlgItem(hDlg,layoutInfo.controlID);609 int x,y,width,height;610 if(layoutState.valid)611 {612 x = layoutState.x;613 y = layoutState.y;614 width = layoutState.width;615 height = layoutState.height;616 }617 else618 {619 RECT r;620 GetWindowRect(hCtrl, &r);621 POINT p = {r.left, r.top};622 ScreenToClient(hDlg, &p);623 x = p.x;624 y = p.y;625 width = r.right - r.left;626 height = r.bottom - r.top;627 }629 switch(layoutInfo.horizontalLayout)630 {631 case ControlLayoutInfo::RESIZE_END: width += deltaWidth; break;632 case ControlLayoutInfo::MOVE_START: x += deltaWidth; break;633 default: break;634 }635 switch(layoutInfo.verticalLayout)636 {637 case ControlLayoutInfo::RESIZE_END: height += deltaHeight; break;638 case ControlLayoutInfo::MOVE_START: y += deltaHeight; break;639 default: break;640 }642 SetWindowPos(hCtrl, 0, x,y, width,height, 0);644 layoutState.x = x;645 layoutState.y = y;646 layoutState.width = width;647 layoutState.height = height;648 layoutState.valid = true;649 }651 s_windowWidth = dlgWidth;652 s_windowHeight = dlgHeight;654 RedrawWindow(hDlg, NULL, NULL, RDW_INVALIDATE);655 }656 break;658 case WM_COMMAND:659 switch(LOWORD(wParam))660 {661 case IDC_LIST1:662 if(HIWORD(wParam) == LBN_DBLCLK)663 {664 POINT pos;665 GetCursorPos(&pos);666 int clickedItem = LBItemFromPt(GetDlgItem(hDlg, IDC_LIST1), pos, FALSE);667 if(clickedItem != -1)668 {669 SendMessage(hDlg, WM_COMMAND, IDOK, 0);670 }671 }672 return TRUE;674 case IDOK:675 {676 int listIndex = SendDlgItemMessage(hDlg, IDC_LIST1, LB_GETCURSEL, (WPARAM) 0, (LPARAM) 0);677 s_archiveFileChooserResult = s_listToItemsMap[listIndex];678 s_listToItemsMap.clear();679 //if(Full_Screen)680 //{681 // while (ShowCursor(true) < 0);682 // while (ShowCursor(false) >= 0);683 //}684 EndDialog(hDlg, false);685 } return TRUE;687 case ID_CANCEL:688 case IDCANCEL:689 s_archiveFileChooserResult = -1;690 s_listToItemsMap.clear();691 //if(Full_Screen)692 //{693 // while (ShowCursor(true) < 0);694 // while (ShowCursor(false) >= 0);695 //}696 EndDialog(hDlg, false);697 return TRUE;698 }700 case WM_CLOSE:701 s_archiveFileChooserResult = -1;702 s_listToItemsMap.clear();703 //if(Full_Screen)704 //{705 // while (ShowCursor(true) < 0);706 // while (ShowCursor(false) >= 0);707 //}708 EndDialog(hDlg, false);709 return TRUE;710 }712 return false;713 }