wocle

ViewerRip Source (2013/08/04)

Aug 5th, 2013
975
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 21.88 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4. #include <malloc.h>
  5.  
  6. #ifndef WIN32_LEAN_AND_MEAN
  7.     #define WIN32_LEAN_AND_MEAN
  8. #endif//WIN32_LEAN_AND_MEAN
  9. #include <Windows.h>
  10. #include <WindowsX.h>
  11. #include <tlhelp32.h>
  12.  
  13.  
  14. struct Viewer {
  15.     // The name of the viewer executable
  16.     const char* process_name;
  17.     // The minimum number of the version of the viewer this program is built for
  18.     const char* version;
  19.  
  20.     // The name of the selection for 100% zoom
  21.     const char* zoom_comboname;
  22.  
  23.     // Control identifiers for the important parts of the window
  24.     int button_parent_code;
  25.     int button_first_code;
  26.     int button_next_code;
  27.     int button_zoom_code;
  28.     int area_parent_code;
  29.     int area_main_code;
  30. };
  31.  
  32. const struct Viewer all_viewers[] = {
  33.     {
  34.         "dmmviewer.exe",    // process_name
  35.         "1.2.1.0",          // version
  36.  
  37.         "100%",             // zoom_comboname
  38.  
  39.         0xE805,             // button_parent_code
  40.         0x8023,             // button_first_code
  41.         0x8020,             // button_next_code
  42.         0x800A,             // button_zoom_code
  43.         0xE900,             // area_parent_code
  44.         0xE900              // area_main_code
  45.     },
  46.     {
  47.         "DLsiteViewer.exe", // process_name
  48.         "1.0.2.0",          // version
  49.  
  50.         "100%",             // zoom_comboname
  51.  
  52.         0xE805,             // button_parent_code
  53.         0x8023,             // button_first_code
  54.         0x8020,             // button_next_code
  55.         0x800A,             // button_zoom_code
  56.         0xE900,             // area_parent_code
  57.         0xE900              // area_main_code
  58.     }
  59. };
  60.  
  61. // Range of memory to search
  62. // Could potentially be narrowed down
  63. const LPCVOID memory_min = (LPCVOID)0x00000000;
  64. const LPCVOID memory_max = (LPCVOID)0xffffffff;
  65.  
  66. // structs for storing .bmp header information
  67. // char arrays to prevent padding, etc.
  68. struct BMPHeader {
  69.     char x[14];
  70. };
  71.  
  72. struct BMPDIB {
  73.     char x[54];
  74. };
  75.  
  76. int create_bmp_header(struct BMPHeader* header, struct BMPDIB* dib, int w, int h) {
  77.     // Create bmp headers in the given structs
  78.     // returns the number of padding bits on each horizontal line
  79.  
  80.     // Calculate the length in bytes of one line (1 byte per color channel)
  81.     int bytew = w * 3;
  82.     // Each line must be aligned to 4 bytes
  83.     if (bytew & 0x0003) {
  84.         bytew |= 0x0003;
  85.         ++bytew;
  86.     }
  87.  
  88.     memset(header->x, 0, sizeof(header->x));
  89.     memset(dib->x, 0, sizeof(dib->x));
  90.     // .bmp magic numbers
  91.     header->x[0] = 'B';
  92.     header->x[1] = 'M';
  93.  
  94.     // Store...
  95.     // The size of the file
  96.     *((int*)(&header->x[2])) = sizeof(header->x) + sizeof(dib->x) + bytew * h;
  97.     // The bitmap offset (i.e. the size of the combined headers)
  98.     *((int*)(&header->x[10])) = sizeof(header->x) + sizeof(dib->x);
  99.     // The size of the DIB
  100.     *((int*)(&dib->x[0])) = sizeof(dib->x);
  101.     // The width of the image, in pixels
  102.     *((int*)(&dib->x[4])) = w;
  103.     // The height of the image, in pixels
  104.     *((int*)(&dib->x[8])) = h;
  105.     // The number of color planes (always 1) and the number of bits per pixel
  106.     *((int*)(&dib->x[12])) = 1 | 24 << 16;
  107.     // The size of the image, in bytes
  108.     *((int*)(&dib->x[20])) = bytew * h;
  109.  
  110.     return bytew - (w * 3);
  111. }
  112.  
  113. // Compact some stuff so it can all be sent by a pointer to get_process_window
  114. struct process_window_helper {
  115.     // Set this before calling get_process_window as the process to match
  116.     DWORD pid;
  117.     // get_process_window will set this pointing to a visible window belonging to the process
  118.     HWND hwnd;
  119. };
  120.  
  121. BOOL CALLBACK get_process_window(HWND hwnd, LPARAM lParam) {
  122.     // Find all windows belonging to the process
  123.     DWORD pid;
  124.     GetWindowThreadProcessId(hwnd, &pid);
  125.     if (((struct process_window_helper*)lParam)->pid == pid) {
  126.         // Any given process may have many invisible top level windows
  127.         // DMMViewer has about 4, but should only have one visible (the main window)
  128.         if (IsWindowVisible(hwnd)) {
  129.             ((struct process_window_helper*)lParam)->hwnd = hwnd;
  130.             return FALSE;
  131.         }
  132.     }
  133.  
  134.     return TRUE;
  135. }
  136.  
  137. void windows_error(const LPTSTR function_name) {
  138.     // Retrieve the system error message for the last-error code
  139.    
  140.     LPVOID msg_buffer;
  141.     DWORD error_code = GetLastError();
  142.        
  143.     if (error_code == 0) {
  144.         // No error available
  145.         return;
  146.     }
  147.    
  148.     FormatMessage(
  149.         FORMAT_MESSAGE_ALLOCATE_BUFFER |
  150.         FORMAT_MESSAGE_FROM_SYSTEM |
  151.         FORMAT_MESSAGE_IGNORE_INSERTS,
  152.         NULL,
  153.         error_code,
  154.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  155.         (LPTSTR) &msg_buffer,
  156.         0, NULL);
  157.    
  158.     fprintf(stderr, "Error in function %s(): %lu: %s\n", function_name, error_code, (LPTSTR) &msg_buffer);
  159.    
  160.     LocalFree(msg_buffer);
  161. }
  162.  
  163. int main(int argc, char** argv) {
  164.     char* output_prefix = NULL;
  165.     int range_min = -1;
  166.     int range_max = -1;
  167.     int offset = -1;
  168.     int padwidth = -1;
  169.     BOOL debug = FALSE;
  170.     BOOL force_bitmap = FALSE;
  171.     int jpg_quality = -1;
  172.  
  173.     HANDLE system_snapshot;
  174.     PROCESSENTRY32 process;
  175.  
  176.     int target_index;
  177.  
  178.     int i;
  179.  
  180.     HMODULE freeimage = LoadLibrary("FreeImage.dll");
  181.  
  182.     typedef long(__stdcall *FreeImage_Save_Type)(int, void**, const char*, int);
  183.     typedef void(__stdcall *FreeImage_Unload_Type)(void**);
  184.     typedef void**(__stdcall *FreeImage_ConvertFromRawBits_Type)(unsigned char*, int, int, int, unsigned, unsigned, unsigned, unsigned, long);
  185.     typedef void**(__stdcall *FreeImage_ConvertTo24Bits_Type)(void**);
  186.  
  187.     FreeImage_Save_Type FreeImage_Save;
  188.     FreeImage_Unload_Type FreeImage_Unload;
  189.     FreeImage_ConvertFromRawBits_Type FreeImage_ConvertFromRawBits;
  190.     FreeImage_ConvertTo24Bits_Type FreeImage_ConvertTo24Bits;
  191.  
  192.     if (freeimage == NULL) {
  193.         windows_error("LoadLibrary");
  194.         force_bitmap = TRUE;
  195.     } else {
  196.         FreeImage_Save = (FreeImage_Save_Type)GetProcAddress(freeimage, "_FreeImage_Save@16");
  197.         if (FreeImage_Save == NULL) {
  198.             windows_error("GetProcAddress");
  199.             force_bitmap = TRUE;
  200.         }
  201.         FreeImage_Unload = (FreeImage_Unload_Type)GetProcAddress(freeimage, "_FreeImage_Unload@4");
  202.         if (FreeImage_Unload == NULL) {
  203.             windows_error("GetProcAddress");
  204.             force_bitmap = TRUE;
  205.         }
  206.         FreeImage_ConvertFromRawBits = (FreeImage_ConvertFromRawBits_Type)GetProcAddress(freeimage, "_FreeImage_ConvertFromRawBits@36");
  207.         if (FreeImage_ConvertFromRawBits == NULL) {
  208.             windows_error("GetProcAddress");
  209.             force_bitmap = TRUE;
  210.         }
  211.         FreeImage_ConvertTo24Bits = (FreeImage_ConvertTo24Bits_Type)GetProcAddress(freeimage, "_FreeImage_ConvertTo24Bits@4");
  212.         if (FreeImage_ConvertTo24Bits == NULL) {
  213.             windows_error("GetProcAddress");
  214.             force_bitmap = TRUE;
  215.         }
  216.     }
  217.  
  218.  
  219.  
  220.     // Parse command line options
  221.     // Partially untested, partially unimplemented
  222.     for (i = 1; i < argc; ++i) {
  223.         if (strcmp(argv[i], "-p") == 0 ||
  224.             strcmp(argv[i], "--prefix") == 0) {
  225.                 // Output file prefix
  226.                 // e.g. ./viewerrip -p ms1212_
  227.                 // results in
  228.                 // ms1212_001.bmp, ms1212_002.bmp, etc.
  229.                 // Default is no prefix
  230.                 if (output_prefix) {
  231.                     fprintf(stderr, "Multiple output prefixes specified\n");
  232.                 } else if (i + 1 >= argc) {
  233.                     fprintf(stderr, "Output prefix expected\n");
  234.                 } else {
  235.                     size_t prefix_length = strlen(argv[i + 1]);
  236.                     output_prefix = malloc(sizeof(char) * (prefix_length + 1));
  237.                     strcpy(output_prefix, argv[i + 1]);
  238.                 }
  239.                 ++i;
  240.         } else if (strcmp(argv[i], "-r") == 0 ||
  241.             strcmp(argv[i], "--range") == 0) {
  242.                 // Page range (inclusive, one indexed)
  243.                 // e.g. ./viewerrip -r 1 10
  244.                 // will rip the first ten pages
  245.                 // Default is entire file
  246.                 if (range_min >= 0 && range_max >= 0) {
  247.                     fprintf(stderr, "Multiple ranges specified\n");
  248.                 } else if (i + 2 >= argc) {
  249.                     fprintf(stderr, "Two range values expected\n");
  250.                 } else {
  251.                     printf("Range is not yet supported\n");
  252.                     range_min = atoi(argv[i + 1]);
  253.                     range_max = atoi(argv[i + 2]);
  254.                     if (range_min < 0 || range_max < range_min) {
  255.                         fprintf(stderr, "Invalid range specified\n");
  256.                         range_min = range_max = -1;
  257.                     }
  258.                 }
  259.                 i += 2;
  260.         } else if (strcmp(argv[i], "-o") == 0 ||
  261.             strcmp(argv[i], "--offset") == 0) {
  262.                 // Naming offset
  263.                 // e.g. ./viewerrip -p file -r 11 20 -o 1
  264.                 // will start at page 11 but name the image file001.bmp
  265.                 // Default is the first page in the range
  266.                 if (offset >= 0) {
  267.                     fprintf(stderr, "Multiple offsets specified\n");
  268.                 } else if (i + 1 >= argc) {
  269.                     fprintf(stderr, "Offset expected\n");
  270.                 } else {
  271.                     offset = atoi(argv[i + 1]);
  272.                     if (offset < 0) {
  273.                         fprintf(stderr, "Invalid offset\n");
  274.                         offset = -1;
  275.                     }
  276.                 }
  277.                 ++i;
  278.         } else if (strcmp(argv[i], "-w") == 0 ||
  279.             strcmp(argv[i], "--width") == 0) {
  280.                 // File number minimum width
  281.                 // e.g. ./viewerrip -w 5
  282.                 // will name files 00001.bmp, 00002.bmp, etc.
  283.                 // Default is 3
  284.                 if (padwidth > 0) {
  285.                     fprintf(stderr, "Multiple widths specified\n");
  286.                 } else if (i + 1 >= argc) {
  287.                     fprintf(stderr, "Width expected\n");
  288.                 } else {
  289.                     padwidth = atoi(argv[i + 1]);
  290.                     if (padwidth < 0) {
  291.                         fprintf(stderr, "Invalid width\n");
  292.                         padwidth = -1;
  293.                     }
  294.                 }
  295.                 ++i;
  296.         } else if (strcmp(argv[i], "-d") == 0 ||
  297.             strcmp(argv[i], "--debug") == 0) {
  298.                 // Enables debugging text
  299.                 if (debug) {
  300.                     fprintf(stderr, "Multiple debug declarations\n");
  301.                 } else {
  302.                     debug = TRUE;
  303.                 }
  304.         } else if (strcmp(argv[i], "-q") == 0 ||
  305.             strcmp(argv[i], "--quality") == 0) {
  306.                 // Sets jpeg file quality
  307.                 if (jpg_quality != 0) {
  308.                     fprintf(stderr, "Multiple qualities specified\n");
  309.                 } else if (i + 1 >= argc) {
  310.                     fprintf(stderr, "Quality expected\n");
  311.                 } else {
  312.                     jpg_quality = atoi(argv[i + 1]);
  313.                     if (jpg_quality < 0 || jpg_quality > 100) {
  314.                         fprintf(stderr, "Invalid quality\n");
  315.                         jpg_quality = -1;
  316.                     }
  317.                 }
  318.                 ++i;
  319.         }
  320.     }
  321.  
  322.     if (output_prefix == NULL) {
  323.         output_prefix = "";
  324.     }
  325.     if (range_min < 0) {
  326.         range_min = 1;
  327.     }
  328.     if (range_max < 0) {
  329.         range_max = -1;
  330.     }
  331.     if (offset < 0) {
  332.         offset = range_min;
  333.     }
  334.     if (padwidth < 0) {
  335.         padwidth = 3;
  336.     }
  337.     if (jpg_quality < 0) {
  338.         jpg_quality = 100;
  339.     }
  340.  
  341.     // Main program
  342.  
  343.     // Get a snapshot of all the running processes
  344.     system_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  345.     process.dwSize = sizeof(PROCESSENTRY32);
  346.  
  347.     if (system_snapshot == INVALID_HANDLE_VALUE) {
  348.         fprintf(stderr, "Couldn't get snapshot of running processes\n");
  349.         windows_error("CreateToolhelp32Snapshot");
  350.     }
  351.  
  352.     for (target_index = 0; target_index < sizeof(all_viewers) / sizeof(*all_viewers); ++target_index) {
  353.         if (Process32First(system_snapshot, &process) == TRUE) {
  354.             const struct Viewer target_viewer = all_viewers[target_index];
  355.             // Iterate through all the processes
  356.             while (Process32Next(system_snapshot, &process) == TRUE) {
  357.                 // Find the viewer process by name
  358.                 if (_stricmp(process.szExeFile, target_viewer.process_name) == 0) {
  359.                     // Open the process with permission to read memory and process info
  360.                     HANDLE viewer_process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, process.th32ProcessID);
  361.                     struct process_window_helper pwh;
  362.  
  363.                     SYSTEM_INFO system_info;
  364.                     MEMORY_BASIC_INFORMATION mbi;
  365.                     DWORD page_granularity;
  366.  
  367.                     HWND viewer_window_main;
  368.                     HWND viewer_button_parent;
  369.                     HWND viewer_button_first, viewer_button_next;
  370.                     HWND viewer_button_zoom;
  371.                     HWND viewer_area_parent;
  372.                     HWND viewer_area_main;
  373.  
  374.                     int page;
  375.                     BOOL done = FALSE;
  376.  
  377.                     RECT original_window_rect;
  378.                     WINDOWPLACEMENT original_window_placement;
  379.                     WINDOWPLACEMENT temp_window_placement;
  380.  
  381.                     int original_zoom_selection;
  382.  
  383.                     if (debug) {
  384.                         printf("Found process '%s'\n", target_viewer.process_name);
  385.                     }
  386.  
  387.                     if (viewer_process == NULL) {
  388.                         fprintf(stderr, "Could not open process %s\n", process.szExeFile);
  389.                         windows_error("OpenProcess");
  390.                         exit(EXIT_FAILURE);
  391.                     }
  392.  
  393.                     pwh.pid = process.th32ProcessID;
  394.                     pwh.hwnd = (HWND)0xdeaddead;
  395.  
  396.  
  397.                     EnumWindows(&get_process_window, (LPARAM)&pwh);
  398.  
  399.                     // Find all the subwindow by control ID
  400.                     viewer_window_main = pwh.hwnd;
  401.                     viewer_button_parent = GetDlgItem(viewer_window_main, target_viewer.button_parent_code);
  402.                     if (viewer_button_parent == NULL) {
  403.                         fprintf(stderr, "Failed to find button parent\n");
  404.                         windows_error("GetDlgItem");
  405.                     }
  406.                     viewer_button_first = GetDlgItem(viewer_button_parent, target_viewer.button_first_code);
  407.                     if (viewer_button_first == NULL) {
  408.                         fprintf(stderr, "Failed to find first page button\n");
  409.                         windows_error("GetDlgItem");
  410.                     }
  411.                     viewer_button_next = GetDlgItem(viewer_button_parent, target_viewer.button_next_code);
  412.                     if (viewer_button_next == NULL) {
  413.                         fprintf(stderr, "Failed to find next page button\n");
  414.                         windows_error("GetDlgItem");
  415.                     }
  416.                     viewer_button_zoom = GetDlgItem(viewer_button_parent, target_viewer.button_zoom_code);
  417.                     if (viewer_button_zoom == NULL) {
  418.                         fprintf(stderr, "Failed to find zoom box\n");
  419.                         windows_error("GetDlgItem");
  420.                     }
  421.                     viewer_area_parent = GetDlgItem(viewer_window_main, target_viewer.area_parent_code);
  422.                     if (viewer_area_parent == NULL) {
  423.                         fprintf(stderr, "Failed to find area parent\n");
  424.                         windows_error("GetDlgItem");
  425.                     }
  426.                     viewer_area_main = GetDlgItem(viewer_area_parent, target_viewer.area_main_code);
  427.                     if (viewer_area_main == NULL) {
  428.                         fprintf(stderr, "Failed to find main area\n");
  429.                         windows_error("GetDlgItem");
  430.                     }
  431.  
  432.                     if (debug) {
  433.                         printf("viewer_window_main: %p\n", viewer_window_main);
  434.                         printf("viewer_button_parent: %p\n", viewer_button_parent);
  435.                         printf("viewer_button_first: %p\n", viewer_button_first);
  436.                         printf("viewer_button_next: %p\n", viewer_button_next);
  437.                         printf("viewer_button_zoom: %p\n", viewer_button_zoom);
  438.                         printf("viewer_area_main: %p\n", viewer_area_main);
  439.                     }
  440.  
  441.                     // Store original window position and state
  442.                     // Window will be shrunken to ensure window size is smaller than the image
  443.                     // This is necessary so that the immage in memory will not be padded
  444.                     original_window_placement.length = sizeof(original_window_placement);
  445.                     GetWindowPlacement(viewer_window_main, &original_window_placement);
  446.                     temp_window_placement = original_window_placement;
  447.                     temp_window_placement.showCmd = SW_SHOW;
  448.                     SetWindowPlacement(viewer_window_main, &temp_window_placement);
  449.  
  450.                     // Set zoom level to 100%
  451.                     original_zoom_selection = ComboBox_GetCurSel(viewer_button_zoom);
  452.  
  453.                     if (ComboBox_SelectString(viewer_button_zoom, -1, target_viewer.zoom_comboname) == CB_ERR) {
  454.                         fprintf(stderr, "Failed to find 100% zoom selection\n");
  455.                         windows_error("ComboBox_SelectString");
  456.                     }
  457.                     // Programatically selecting a ComboBox option will not send a selection change message to the parent, so do that manually
  458.                     SendMessage(viewer_button_parent, WM_COMMAND, MAKEWPARAM(target_viewer.button_zoom_code, CBN_SELCHANGE), (LPARAM)viewer_button_zoom);
  459.  
  460.  
  461.                     GetWindowRect(viewer_window_main, &original_window_rect);
  462.                
  463.                     SetWindowPos(viewer_window_main, 0, 0, 0, 256, 256, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
  464.  
  465.                     GetSystemInfo(&system_info);
  466.  
  467.                     // Find the size of a page, so we only have to check memory on each page boundary
  468.                     page_granularity = system_info.dwAllocationGranularity;
  469.  
  470.                     // Send a virtual click to the first page button
  471.                     SendMessage(viewer_button_first, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(1, 1));
  472.                     SendMessage(viewer_button_first, WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(1, 1));
  473.  
  474.                     // It seems it takes some time for messages to propagate to other buttons, so wait until the next button is enabled
  475.                     // This will prevent the bug where if the Viewer was opened to the last page, it would only export the first image
  476.                     for (i = 0; (i < 1280000) && (GetWindowLong(viewer_button_next, GWL_STYLE) & WS_DISABLED); ++i) { }
  477.                     if (i == 1280000) {
  478.                         fprintf(stderr, "Waiting for the next button to become enabled timed out\n");
  479.                     }
  480.  
  481.                     // Continue until the next button becomes disabled, indicating the last image
  482.                     for (page = 0; !done; ++page) {
  483.                         LPVOID address = (LPVOID)memory_min;
  484.                         LPVOID image_address = address;
  485.                         DWORD image_size = 0;
  486.                         SCROLLINFO sih, siv;
  487.                         int w, h;
  488.                         char* image;
  489.  
  490.                         int number_length;
  491.                         char* output_name;
  492.                         FILE* output_file;
  493.  
  494.                         struct BMPHeader header;
  495.                         struct BMPDIB dib;
  496.  
  497.                         int buffer_width;
  498.  
  499.                         int i;
  500.  
  501.                         // In my testing, the image is always found in the largest memory block allocated
  502.                         // by the process. This isn't necessarily going to always be true, so this will likely
  503.                         // fail on small images. Fortunately, comic pages tend to be rather large.
  504.                         // We can further narrow the possibilities down by looking at other characteristics of the memory
  505.                         // The memory type is always Private and set to Read/Write protection
  506.                         while (VirtualQueryEx(viewer_process, address, &mbi, sizeof(mbi))) {
  507.                             if (mbi.State == MEM_COMMIT && mbi.Type == MEM_PRIVATE && mbi.Protect == PAGE_READWRITE && mbi.RegionSize > image_size) {
  508.                                 image_address = mbi.BaseAddress;
  509.                                 image_size = mbi.RegionSize;
  510.                             }
  511.                             address = (char*)mbi.BaseAddress + mbi.RegionSize;
  512.                         }
  513.  
  514.                         if (image_address == NULL || image_size == 0) {
  515.                             fprintf(stderr, "Failed to query virtual memory\n");
  516.                             windows_error("VirtualQueryEx");
  517.                         }
  518.  
  519.                         if (debug) {
  520.                             printf("Most likely image location at %p (%lu bytes)\n", image_address, image_size);
  521.                         }
  522.  
  523.                         // As a hack, the dimensions of the images are determined by
  524.                         // looking at the size of the scroll area
  525.                         // This requires that the image area be smaller than the image
  526.                         sih.cbSize = sizeof(sih);
  527.                         sih.fMask = SIF_ALL;
  528.                         siv.cbSize = sizeof(siv);
  529.                         siv.fMask = SIF_ALL;
  530.  
  531.                         GetScrollInfo(viewer_area_main, SB_HORZ, &sih);
  532.                         GetScrollInfo(viewer_area_main, SB_VERT, &siv);
  533.  
  534.                         w = sih.nMax - sih.nMin + 1;
  535.                         h = siv.nMax - siv.nMin + 1;
  536.  
  537.                         if (debug) {
  538.                             printf("%i x %i pixels\n", w, h);
  539.                         }
  540.  
  541.                         // Allocate memory to copy image into
  542.                         image = malloc(sizeof(int) * w * h);
  543.                         if (image == NULL) {
  544.                             fprintf(stderr, "Could not allocate memory for image\n");
  545.                             continue;
  546.                         }
  547.  
  548.                         // Read the image memory (image is stored in 4 byte pixels, 0xrrggbbFF)
  549.                         if (ReadProcessMemory(viewer_process, image_address, (LPVOID)image, w * h * 4, NULL) == 0) {
  550.                             if (debug) {   
  551.                                 printf("Image uses %i of %lu allocated bytes\n", w * h * 4, image_size);
  552.                             }
  553.                             printf("On page %i:\n", page);
  554.                             windows_error("ReadProcessMemory");
  555.                             continue;
  556.                         }
  557.                         if (debug) {   
  558.                             printf("Image uses %i of %lu allocated bytes\n", w * h * 4, image_size);
  559.                         }
  560.  
  561.                         // Once image memory is ours, 'click' to the next image
  562.                         SendMessage(viewer_button_next, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(1, 1));
  563.                         SendMessage(viewer_button_next, WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(1, 1));
  564.  
  565.                         // If the next button is disabled, we reached the end; grab one last image and finish
  566.                         done = GetWindowLong(viewer_button_next, GWL_STYLE) & WS_DISABLED;
  567.  
  568.                         number_length = (int)log10((double)page + offset);
  569.                         number_length = number_length > padwidth ? number_length : padwidth;
  570.  
  571.                         if (!force_bitmap) {
  572.                             void** rgba_data;
  573.                             void** rgb_data;
  574.  
  575.                             output_name = malloc(sizeof(char) * (strlen(output_prefix) + number_length + strlen(".jpg") + 1));
  576.                             sprintf(output_name, "%s%0*i.jpg", output_prefix, padwidth, page + offset);
  577.  
  578.                             rgba_data = FreeImage_ConvertFromRawBits((unsigned char*)image, w, h, 4 * w, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, TRUE);
  579.  
  580.                             if (rgba_data == NULL) {
  581.                                 fprintf(stderr, "Failed to create image with FreeImage, falling back to .bmp export");
  582.                                 force_bitmap = TRUE;
  583.                             } else {
  584.                                 rgb_data = FreeImage_ConvertTo24Bits(rgba_data);
  585.                                 if (rgb_data == NULL) {
  586.                                     fprintf(stderr, "Failed to create image with FreeImage, falling back to .bmp export");
  587.                                     force_bitmap = TRUE;
  588.                                 } else {
  589.                                     FreeImage_Save(2/*JPG*/, rgb_data, output_name, jpg_quality | 0x40000/*No metadata*/ | 0x20000/*Optimize*/);
  590.  
  591.                                     FreeImage_Unload(rgb_data);
  592.                                 }
  593.                                 FreeImage_Unload(rgba_data);
  594.                             }
  595.                            
  596.                             free(output_name);
  597.                         }
  598.                         if (force_bitmap) {
  599.                             output_name = malloc(sizeof(char) * (strlen(output_prefix) + number_length + strlen(".bmp") + 1));
  600.  
  601.                             sprintf(output_name, "%s%0*i.bmp", output_prefix, padwidth, page + offset);
  602.  
  603.                             output_file = fopen(output_name, "wb+");
  604.  
  605.                             if (!output_file) {
  606.                                 fprintf(stderr, "Failed to create file '%s'\n", output_name);
  607.  
  608.                                 continue;
  609.                             }
  610.                    
  611.                             buffer_width = create_bmp_header(&header, &dib, w, h);
  612.                             fwrite(header.x, sizeof(header.x), 1, output_file);
  613.                             fwrite(dib.x, sizeof(dib.x), 1, output_file);
  614.  
  615.                             // .bmps are mirrored vertically
  616.                             for (i = h - 1; i >= 0; --i) {
  617.                                 int j;
  618.                                 for (j = 0; j < w; ++j) {
  619.                                     fwrite(((char*)(image + (i * w + j) * 4)), 3, 1, output_file);
  620.                                 }
  621.                                 // Write any padding bytes
  622.                                 for (j = 0; j < buffer_width; ++j) {
  623.                                     fputc(0xff, output_file);
  624.                                 }
  625.                             }
  626.  
  627.                             fclose(output_file);
  628.  
  629.                             free(image);
  630.                         }
  631.                        
  632.                     }
  633.  
  634.                     // Restore window size and state
  635.                     SetWindowPos(viewer_window_main, 0, 0, 0, original_window_rect.right - original_window_rect.left, original_window_rect.top - original_window_rect.bottom, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
  636.                     SetWindowPlacement(viewer_window_main, &original_window_placement);
  637.  
  638.                     // Restore zoom level
  639.                     ComboBox_SetCurSel(viewer_button_zoom, original_zoom_selection);
  640.                     SendMessage(viewer_button_parent, WM_COMMAND, MAKEWPARAM(target_viewer.button_zoom_code, CBN_SELCHANGE), (LPARAM)viewer_button_zoom);
  641.  
  642.                     CloseHandle(viewer_process);
  643.  
  644.                     exit(EXIT_SUCCESS);
  645.                 }
  646.             }
  647.         } else {
  648.             windows_error("Process32First");
  649.             exit(EXIT_FAILURE);
  650.         }
  651.     }
  652.     fprintf(stderr, "Could not find a running supported viewer\n");
  653.     exit(EXIT_FAILURE);
  654. }
Advertisement
Add Comment
Please, Sign In to add comment