Доброго времени суток, Уважаемые! Не знал в какой раздел написать свой вопрос. Прошу помощи кодом, ссылкой, советом. Кому не жалко. Необходимо обойти каталог (включая подкаталоги) и заменить в именах файлов недопустимые символы, на указанный(е). Не представляю как подойти к данному вопросу. На php все было просто (грубый пример): list(); if (is_dir($file)) then list($file) else ... Здесь же мне разбираться полгода. Начиная с рекурсии по файлам и папкам и заканчивая заменой символа. Если у кого есть ссылки или на работки, буду премного благодарен. Решение подойдет на асме, на VB, на BAT.. хоть на чем-нибудь, кроме PHP (нет возможности стартовать Denwer, нужно независимый скрипт\приложение).
hack_virii Пример на fasm. EnumFilesCallback, правда, написана очень стрёмно, но её всё равно надо своей заменить. А EnumFilesFrom делает рекурсивный обход и вполне себе реюзабельна. Для переименования (замены символов) используйте MoveFile внутри своей EnumFilesCallback.
l_inc, спасибо, добрый человек! Ну накрутил!) Уже 2 часа верчу. Пока безрезультатно. А fasm компилит! Буду разбираться. Спасибо!
Уважаемый l_inc, помогите, пожалуйста, с функцией перебора файлов и папок. fasm'овский синтаксис мне чужд, я долго догонял, что stdcall это вызов функции. Или ее объявление?Оо В общем, мой навык асма в NNN раз ниже Вашего, прошу помощи. Думаю, с поиском и заменой символа в имени файла я смогу разобраться. Благодарю за отклик.
hack_virii Эм... Так я ж с ней и помог. EnumFilesFrom перебирает все файлы и для каждого файла вызывает функцию, указатель на которую передан третьим параметром (callback). В данном случае это функция EnumFilesCallback. Первым параметром (path) передаётся указатель путь к папке (без слеша на конце), в которой нужно перебирать содержимое. Вторым параметром (maxlevel) передаётся максимально допустимый уровень вложенности (если нуль, то вхождения во вложенные папки не будет, т.е. без рекурсии). Третьим параметром (callback) передаётся указатель на callback. Четвёртым параметром (custom) можно передать любое пользовательское значение. Оно будет каждый раз передаваться внутрь callback'а. Это может быть, например, хендл файла, в который внутри callback'а пишутся результаты перебора. Или указатель на некоторую более сложную структуру. stdcall — это вызов функции по указателю на функцию. А invoke — вызов по указателю на указатель (чаще всего применяется для вызова API). Ладно. Всё равно я с тех пор исправил баги и слегка улучшил поведение EnumFilesFrom (хотя можно ещё улучшать и улучшать... например, можно значительно понизить расход памяти), поэтому rename_files.asm целиком: Код (Text): format PE GUI 4.0 include 'win32a.inc' entry start proc start uses ebx edi local curDir[MAX_PATH]:BYTE lea ebx,[curDir] ;ebx - current directory pointer invoke GetCurrentDirectory,MAX_PATH,ebx lea edi,[ebx+eax] stdcall EnumFilesFrom,ebx,-1,EnumFilesCallback,0 cmp eax,-1 jz .error invoke MessageBox,NULL,msgCompleteText,msgCompleteTitle,MB_OK ret .error: invoke MessageBox,NULL,msgFileErrText,msgFileErrTitle,MB_OK ret endp ;returns postition of the found char or -1 if not found proc strrchr uses esi edi, string, char mov esi,dword[string] mov edi,dword[string] .next_char: lodsb cmp al,byte[char] jnz .not_found mov edi,esi .not_found: test al,al jnz .next_char lea eax,[edi-1] sub eax,dword[string] sbb eax,eax not eax and eax,edi add eax,-1 ret endp ;returns number of copied characters, including null-character proc strcpy uses esi edi, dest, src, destlen mov edi,dword[dest] mov esi,dword[src] mov ecx,dword[destlen] test ecx,ecx jz .exit .next_char: lodsb stosb add ecx,-1 jz .exit test al,al jnz .next_char .exit: xor al,al mov byte[edi-1],al mov eax,dword[destlen] sub eax,ecx ret endp ;returns 0 to continue file enumeration ;returns -1 to stop enumeration within current directory ;returns anything else to stop enumeration completely proc EnumFilesCallback uses esi edi ebx, filePath, fileAttributes, custom ;do not change directories names test dword[fileAttributes],FILE_ATTRIBUTE_DIRECTORY jnz .exit_continue ;find file path length stdcall strrchr,dword[filePath],0 sub eax,dword[filePath] lea edi,[eax+1] ;edi - file path length (including null-character) ;allocate some space for new file path invoke GetProcessHeap invoke HeapAlloc,eax,0,edi test eax,eax jz .exit_continue ;copy original file path mov ebx,eax ;ebx - new file path pointer stdcall strcpy,ebx,dword[filePath],edi ;find a pointer to the file name stdcall strrchr,ebx,'\' mov esi,eax ;esi - pointer to the character right before the file name ;modify file name .next_char: add esi,1 cmp byte[esi],'a' je .replace_a cmp byte[esi],'b' je .replace_b cmp byte[esi],'c' je .replace_c cmp byte[esi],0 je .rename_file jmp .next_char .replace_a: mov byte[esi],'x' jmp .next_char .replace_b: mov byte[esi],'y' jmp .next_char .replace_c: mov byte[esi],'z' jmp .next_char .rename_file: invoke MoveFile,dword[filePath],ebx ;free resources invoke GetProcessHeap invoke HeapFree,eax,0,ebx .exit_continue: xor eax,eax ret endp ;returns 0 if full enumeration accomplished or ; -1 on error (call GetLastError to get extended info) or ; any other stop-value, returned by the callback proc EnumFilesFrom uses ebx esi edi, path, maxdepth, callback, custom locals fileData WIN32_FIND_DATA <> rb (3-($-$$+3) mod 4) ;4 bytes alignment dwCurFileMaxLen dd ? endl ;find initial path length (excluding null-character) stdcall strrchr,dword[path],0 sub eax,dword[path] ;allocate some more (reserve for future file names to possibly prevent HeapReAlloc's) add eax,33 mov dword[dwCurFileMaxLen],eax invoke GetProcessHeap invoke HeapAlloc,eax,0,dword[dwCurFileMaxLen] mov edi,eax ;edi - path start position ;return error if HeapAlloc returned 0 cmp eax,1 sbb eax,eax jc .exit ;make a local copy of the initial path stdcall strcpy,edi,dword[path],dword[dwCurFileMaxLen] lea esi,[edi+eax] ;esi - path end position (right after '\'-character) mov dword[edi+eax-1],'\*' ;start files enumeration lea edx,[fileData] invoke FindFirstFile,edi,edx ;return error if FindFirstFile returned INVALID_HANDLE_VALUE cmp eax,INVALID_HANDLE_VALUE jnz .enumerationSuccessful invoke GetProcessHeap invoke HeapFree,eax,0,edi mov eax,-1 jmp .exit .enumerationSuccessful: mov ebx,eax ;ebx - file search handle .process_file: lea edx,[fileData.cFileName] ;find null-character pointer stdcall strrchr,edx,0 ;calculate how much is needed for path + current file name + null-character ;(esi-edi)+(eax-fileData.cFileName)+1 add eax,esi lea edx,[fileData.cFileName+edi-1] sub eax,edx ;reallocate if more space required cmp dword[dwCurFileMaxLen],eax jae .enoughSpace mov dword[dwCurFileMaxLen],eax invoke GetProcessHeap invoke HeapReAlloc,eax,0,edi,dword[dwCurFileMaxLen] ;return error (-1) if HeapAlloc returned 0 cmp eax,1 sbb edx,edx jc .exitFreeResources sub esi,edi mov edi,eax ;save new path start position add esi,eax ;save new path end position .enoughSpace: ;append file name to the end of current path lea edx,[fileData.cFileName] stdcall strcpy,esi,edx,dword[dwCurFileMaxLen] ;call the callback function ; if 0 returned, continue enumeration ; if -1 returned, stop enumeration within current directory ; else stop enumeration completely invoke callback,edi,dword[fileData.dwFileAttributes],dword[custom] test eax,eax mov edx,eax jnz .exitFreeResources ;check, whether current file is subdirectory test dword[fileData.dwFileAttributes],FILE_ATTRIBUTE_DIRECTORY jz .next_file cmp dword[maxdepth],0 jz .next_file cmp byte[fileData.cFileName],'.' jnz .validSubdir cmp byte[fileData.cFileName+1],0 jz .next_file cmp byte[fileData.cFileName+1],'.' jnz .validSubdir cmp byte[fileData.cFileName+2],0 jz .next_file .validSubdir: ;go to subdirectory mov edx,dword[maxdepth] add edx,-1 stdcall EnumFilesFrom,edi,edx,dword[callback],dword[custom] ;stop further enumeration if stopped by callback (not 0 and not -1), ;i.e. continue enumeration if EnumFilesFrom returned error (-1) lea edx,[eax+1] sub edx,1 ja .exitFreeResources .next_file: ;continue files enumeration lea edx,[fileData] invoke FindNextFile,ebx,edx test eax,eax jnz .process_file xor edx,edx ;free resources .exitFreeResources: push edx invoke GetProcessHeap invoke HeapFree,eax,0,edi invoke FindClose,ebx pop eax .exit: ret endp data import library kernel32,'kernel32.dll',\ user32,'user32.dll' import kernel32,\ FindFirstFile,'FindFirstFileA',\ FindNextFile,'FindNextFileA',\ FindClose,'FindClose',\ GetCurrentDirectory,'GetCurrentDirectoryA',\ GetProcessHeap,'GetProcessHeap',\ HeapAlloc,'HeapAlloc',\ HeapReAlloc,'HeapReAlloc',\ HeapFree,'HeapFree',\ MoveFile,'MoveFileA' import user32,\ MessageBox,'MessageBoxA' end data msgFileErrTitle db 'Error',0 msgFileErrText db 'Could not enumerate files',0 msgCompleteTitle db 'Info',0 msgCompleteText db 'Files renaming successfully complete',0 Я думаю, понятно, что нужно изменить, чтобы выбрать, какие символы на какие менять (см. комментарий modify file name). Это для простейшего случая один символ на другой символ. Для более изощрённых замен алгоритм придётся усложнить. А если ещё и bat-файлик сообразить, то можно нужную папку (или файл из этой папки) просто мышкой на него сбрасывать: Код (Text): @echo off cd /d "%~1" start %~dp0\rename_files.exe
Ух! Потрясающе офигительно! Магия! l_inc, Человечище!!! ОГРОМНЕЙШЕЕ СПАСИБО!!! В каком городе живешь? Давай пивом угощу! В одном месте кода (LEA EAX,DWORD PTR DS:[EDI-1]), OllyDbg поместил в EAX значение \xDD - издевается, сволочь.) Разбираюсь. Постараюсь не доставать по пустякам, только если будет реальная запара. Спасибо!
Все-таки запара обнаружилась. Необходимо заменить любые точки в имени файла, кроме расширения. Помогите, пожалуйста, как-то рационально сделать проверку. Пока что получается так: Код (Text): ;modify file name next_char: add esi,1 cmp byte[esi],'~' je replace_a cmp byte[esi],'#' je replace_a cmp byte[esi],'%' je replace_a cmp byte[esi],'&' je replace_a cmp byte[esi],'*' je replace_a cmp byte[esi],'{' je replace_a cmp byte[esi],'}' je replace_a cmp byte[esi],':' je replace_a cmp byte[esi],'<' je replace_a cmp byte[esi],'>' je replace_a cmp byte[esi],'?' je replace_a cmp byte[esi],'+' je replace_a cmp byte[esi],'|' je replace_a cmp byte[esi],'"' je replace_a cmp byte[esi],"'" je replace_a cmp byte[esi],"$" je replace_a cmp byte[esi],"@" je replace_a cmp byte[esi],"=" je replace_a cmp byte[esi],'.' jnz L2 ;do not change directories names test dword[fileAttributes],FILE_ATTRIBUTE_DIRECTORY jz L4 jmp replace_a L4: cmp byte[esi+3],0 jne replace_a L2: cmp byte[esi],0 je rename_file jmp next_char replace_a: mov byte[esi],'_' jmp next_char rename_file: invoke MoveFile,dword[filePath],ebx Затыка в L4. Кол-во символов в расширении может быть 2-4 символа. Как их проверить наиболее компактно?
Расширение может быть любой длины, не только 2, 3 или 4 символа. Правильнее в данном случае проходить имя файла с конца и при обнаружении первой точки устанавливать некий флаг, говорящий о том, что все следующие встречаемые точки следует заменить. Кстати, имя файла не может содержать знаки Код (Text): /\:*?"<>| , на них можно проверку не делать.
Ezrah, спасибо! Лишние символы убрал (действительно, винда же не даст задать подобное имя). А с точкой.. Если я так все переверну, то еще неделю возиться буду. А надо срочно. Чтоб работало. Потом обязательно оптимизирую. Актуально все-таки. Пока что сделал через ***, но вроде работает.) Пускай и быдлокод. Все приходит с опытом.(с) Код (Text): format PE GUI 4.0 include 'include/win32a.inc' entry start proc start uses ebx edi L1: local curDir[MAX_PATH]:BYTE lea ebx,[curDir] ;ebx - current directory pointer invoke GetCurrentDirectory,MAX_PATH,ebx lea edi,[ebx+eax] stdcall EnumFilesFrom,ebx,25,EnumFilesCallback,0 cmp eax,-1 jz .error sub byte[Counter],1 cmp byte[Counter],0 ja L1 invoke MessageBox,NULL,msgCompleteText,msgCompleteTitle,MB_OK ret .error: invoke MessageBox,NULL,msgFileErrText,msgFileErrTitle,MB_OK ret endp ;returns postition of the found char or -1 if not found proc strrchr uses esi edi, string, char mov esi,dword[string] mov edi,dword[string] .next_char: lodsb cmp al,byte[char] jnz .not_found mov edi,esi .not_found: test al,al jnz .next_char lea eax,[edi-1] sub eax,dword[string] sbb eax,eax not eax and eax,edi add eax,-1 ret endp ;returns number of copied characters, including null-character proc strcpy uses esi edi, dest, src, destlen mov edi,dword[dest] mov esi,dword[src] mov ecx,dword[destlen] test ecx,ecx jz .exit .next_char: lodsb stosb add ecx,-1 jz .exit test al,al jnz .next_char .exit: xor al,al mov byte[edi-1],al mov eax,dword[destlen] sub eax,ecx ret endp ;returns 0 to continue file enumeration ;returns -1 to stop enumeration within current directory ;returns anything else to stop enumeration completely proc EnumFilesCallback uses esi edi ebx, filePath, fileAttributes, custom ;find file path length stdcall strrchr,dword[filePath],0 sub eax,dword[filePath] lea edi,[eax+1] ;edi - file path length (including null-character) ;allocate some space for new file path invoke GetProcessHeap invoke HeapAlloc,eax,0,edi test eax,eax jz exit_continue L3: ;copy original file path mov ebx,eax ;ebx - new file path pointer stdcall strcpy,ebx,dword[filePath],edi ;find a pointer to the file name stdcall strrchr,ebx,'\' mov esi,eax ;esi - pointer to the character right before the file name ;modify file name next_char: add esi,1 cmp byte[esi],'~' je replace_a cmp byte[esi],'#' je replace_a cmp byte[esi],'%' je replace_a cmp byte[esi],'&' je replace_a cmp byte[esi],'{' je replace_a cmp byte[esi],'}' je replace_a cmp byte[esi],'+' je replace_a cmp byte[esi],"'" je replace_a cmp byte[esi],"$" je replace_a cmp byte[esi],"@" je replace_a cmp byte[esi],"=" je replace_a cmp byte[esi],'.' jnz L2 ;do not change directories names test dword[fileAttributes],FILE_ATTRIBUTE_DIRECTORY jz L4 jmp replace_a L4: push esi push edi push eax push ecx mov dword[Address],esi mov edi,esi inc edi mov al,'.' mov ecx,25 cld repne scasb jnz Quit ;dec edi mov edi,dword[Address] mov byte[edi],'_' jmp Quit Quit: pop ecx pop eax pop edi pop esi jmp L2 L2: cmp byte[esi],0 je rename_file jmp next_char replace_a: mov byte[esi],'_' jmp next_char rename_file: invoke MoveFile,dword[filePath],ebx ;free resources invoke GetProcessHeap invoke HeapFree,eax,0,ebx exit_continue: xor eax,eax ret endp ;returns 0 if full enumeration accomplished or ; -1 on error (call GetLastError to get extended info) or ; any other stop-value, returned by the callback proc EnumFilesFrom uses ebx esi edi, path, maxdepth, callback, custom locals fileData WIN32_FIND_DATA <> rb (3-($-$$+3) mod 4) ;4 bytes alignment dwCurFileMaxLen dd ? endl ;find initial path length (excluding null-character) stdcall strrchr,dword[path],0 sub eax,dword[path] ;allocate some more (reserve for future file names to possibly prevent HeapReAlloc's) add eax,33 mov dword[dwCurFileMaxLen],eax invoke GetProcessHeap invoke HeapAlloc,eax,0,dword[dwCurFileMaxLen] mov edi,eax ;edi - path start position ;return error if HeapAlloc returned 0 cmp eax,1 sbb eax,eax jc .exit ;make a local copy of the initial path stdcall strcpy,edi,dword[path],dword[dwCurFileMaxLen] lea esi,[edi+eax] ;esi - path end position (right after '\'-character) mov dword[edi+eax-1],'\*' ;start files enumeration lea edx,[fileData] invoke FindFirstFile,edi,edx ;return error if FindFirstFile returned INVALID_HANDLE_VALUE cmp eax,INVALID_HANDLE_VALUE jnz .enumerationSuccessful invoke GetProcessHeap invoke HeapFree,eax,0,edi mov eax,-1 jmp .exit .enumerationSuccessful: mov ebx,eax ;ebx - file search handle .process_file: lea edx,[fileData.cFileName] ;find null-character pointer stdcall strrchr,edx,0 ;calculate how much is needed for path + current file name + null-character ;(esi-edi)+(eax-fileData.cFileName)+1 add eax,esi lea edx,[fileData.cFileName+edi-1] sub eax,edx ;reallocate if more space required cmp dword[dwCurFileMaxLen],eax jae .enoughSpace mov dword[dwCurFileMaxLen],eax invoke GetProcessHeap invoke HeapReAlloc,eax,0,edi,dword[dwCurFileMaxLen] ;return error (-1) if HeapAlloc returned 0 cmp eax,1 sbb edx,edx jc .exitFreeResources sub esi,edi mov edi,eax ;save new path start position add esi,eax ;save new path end position .enoughSpace: ;append file name to the end of current path lea edx,[fileData.cFileName] stdcall strcpy,esi,edx,dword[dwCurFileMaxLen] ;call the callback function ; if 0 returned, continue enumeration ; if -1 returned, stop enumeration within current directory ; else stop enumeration completely invoke callback,edi,dword[fileData.dwFileAttributes],dword[custom] test eax,eax mov edx,eax jnz .exitFreeResources ;check, whether current file is subdirectory test dword[fileData.dwFileAttributes],FILE_ATTRIBUTE_DIRECTORY jz .next_file cmp dword[maxdepth],0 jz .next_file cmp byte[fileData.cFileName],'.' jnz .validSubdir cmp byte[fileData.cFileName+1],0 jz .next_file cmp byte[fileData.cFileName+1],'.' jnz .validSubdir cmp byte[fileData.cFileName+2],0 jz .next_file .validSubdir: ;go to subdirectory mov edx,dword[maxdepth] add edx,-1 stdcall EnumFilesFrom,edi,edx,dword[callback],dword[custom] ;stop further enumeration if stopped by callback (not 0 and not -1), ;i.e. continue enumeration if EnumFilesFrom returned error (-1) lea edx,[eax+1] sub edx,1 ja .exitFreeResources .next_file: ;continue files enumeration lea edx,[fileData] invoke FindNextFile,ebx,edx test eax,eax jnz .process_file xor edx,edx ;free resources .exitFreeResources: push edx invoke GetProcessHeap invoke HeapFree,eax,0,edi invoke FindClose,ebx pop eax .exit: ret endp data import library kernel32,'kernel32.dll',\ user32,'user32.dll' import kernel32,\ FindFirstFile,'FindFirstFileA',\ FindNextFile,'FindNextFileA',\ FindClose,'FindClose',\ GetCurrentDirectory,'GetCurrentDirectoryA',\ GetProcessHeap,'GetProcessHeap',\ HeapAlloc,'HeapAlloc',\ HeapReAlloc,'HeapReAlloc',\ HeapFree,'HeapFree',\ MoveFile,'MoveFileA' import user32,\ MessageBox,'MessageBoxA' end data msgFileErrTitle db 'Error',0 msgFileErrText db 'Could not enumerate files',0 msgCompleteTitle db 'Info',0 msgCompleteText db 'Files renaming successfully complete',0 Counter db 10 Address dd ? l_inc, спасибо большое! Выручил! На авторство сего кода, естественно, не претендую. Он твой (Ваш). Ezrah, благодарю за совет! Если господа модераторы не закроют тему, я еще отпишусь, как удалось оптимизировать.
Шайтан! Ezrah прав. Нужен альтернативный способ поиска точек. Неправильный подход сканить память до ближайшей точки после текущей. Ошибка в этом куске: Код (Text): push eax push ecx mov dword[Address],esi mov edi,esi inc edi mov al,'.' mov ecx,25 cld repne scasb jnz Quit ;dec edi mov edi,dword[Address] mov byte[edi],'_' jmp Quit А именно: mov ecx,25. Поди угадай, что в памяти лежит и где будет след. точка. 25 символов на имя файла - это я лихо. Сорри! Надеюсь никто не пострадал.)
hack_virii Ну с учётом наличия ф-ии strrchr сделать это крайне просто: Код (Text): ... ;find a pointer to the file name stdcall strrchr,ebx,'\' mov esi,eax ;esi - pointer to the character right before the file name ;find position of the last dot stdcall strrchr,esi,'.' mov edx,eax ;edx - position of the last dot or -1 ;modify file name .next_char: add esi,1 cmp esi,edx je .next_char cmp byte[esi],'~' je replace_a cmp byte[esi],'#' ... Добавлено, как видите, всего четыре строчки. Кстати, имена всех локальных меток крайне желательно начинать с точки. Иначе нарвётесь на неожиданные проблемы. Да и все прочие извращения, добавленные Вами, похоже, можно убрать. Не Бог весть какой код. Можете претендовать, если хотите.