Description: Proper usage of file_find_first function, and the sample script to find all files under the specified directory.
Game Maker Version: 5.3 through 6.1, both registered and unregistered.
Finding files in a directory
To find files inside a directory, use file_find_first and file_find_next. Sounds simple? However, there are a few undocumented issues that we have to take into account.
- Those functions only returns the file name part (as filename_name does.) To use the result with other functions, you have to append the directory name on your own.
- If you're unsure what to put for the second argument 'attr', just put 0. Then it will find all of normal files. Note that if you specify any extra attribute, the function finds normal files plus other files those have the specified attribute. (However, specifying fa_archive has no effect because a file that has the archive attribute is considered as "normal.")
- Don't forget to call file_find_close when you finished.
var file_name;global.file_count = 0;file_name = file_find_first(working_directory + "\*.txt", 0);while (file_name != "") { global.files[global.file_count] = working_directory + "\" + file_name; global.file_count += 1; file_name = file_find_next();}file_find_close();Finding all files in subdirectories
To search in subdirectories, you first have to find directories with specifying fa_directory attribute, then continue searching within each of those directories. Again, there are a number of things to consider:
- Remember that if you specify fa_directory, it finds both of normal files and directories. You have to tell whether the returned name is a file or directory (using either file_attributes or directory_exists.)
- Every directory has two special subdirectories those don't usually show up in Windows Explorer: "." (the current directory) and ".." (the parent directory.) You have to skip those two, or it will fall into an endless loop.
The important note is that you can't use the standard recursive method here. (Because when you recursively call file_find_first during searching, Game Maker cuts off the current search and starts new search, so you can't continue searching any more when you return to the previous function.) An alternative way is as follows:
- Every time we find a directory, put it into a queue instead of searching into it immediately.
- When we finish searching files in one directory, we take another directory out of the aforementioned queue and start searching it.
- Continue it until the queue comes empty.
The following script demonstrates how it can be implemented.
// This script searches all files under the specified directory.// File names are saved in a ds_list which is supplied by the user.// argument0 = the name of the directory to start searching.// argument1 = the extension part of the files to be found (including the period, and must be in lower case.)// argument2 = ds_list which the file names are saved in.// Example:// file_list = ds_list_create();// scr_find_files("C:\Windows", ".exe", file_list);var file_name, dir_name, full_path;var subdir_queue;// Put the starting position into the queue.subdir_queue = ds_queue_create();ds_queue_enqueue(subdir_queue, argument0);while (!ds_queue_empty(subdir_queue)) { // Pop one directory out of the queue. dir_name = ds_queue_dequeue(subdir_queue); // Search all files and subdirectories under the directory. file_name = file_find_first(dir_name + '\*', fa_directory); while (file_name != '') { full_path = dir_name + '\' + file_name; if (file_attributes(full_path, fa_directory)) { // a directory // Put subdirectories into the queue. if (file_name != '.' && file_name != '..') { ds_queue_enqueue(subdir_queue, full_path); } } else { // a normal file // Add the file to the list if it has the specified extension. if (string_lower(filename_ext(file_name)) == argument1) { ds_list_add(argument2, full_path); } } file_name = file_find_next(); } file_find_close();}ds_queue_destroy(subdir_queue);Sample script for the unregistered version
The following example implements a queue with an array.
(Caution: in this one, I implemented a queue in rather lazy way, to keep the code small. It can consume a lot of memory if you have thousands of subdirectories.)
// This script searches all files under the specified directory.// File names are saved in the global array global.files,// and the number of files are saved in global.file_count.// argument0 = the name of the directory to start searching.// argument1 = the extension part of the files to be found (including the period, and must be in lower case.)// Example:// scr_find_files_unreg("C:\Windows", ".exe");var file_name, dir_name, full_path;var subdir_queue, queue_pos, queue_size;// Put the starting position into the queue.subdir_queue[0] = argument0;queue_pos = 0;queue_size = 1;global.file_count = 0;while (queue_pos < queue_size) { // Pop one directory out of the queue. dir_name = subdir_queue[queue_pos]; queue_pos += 1; // Search all files and subdirectories under the directory. file_name = file_find_first(dir_name + '\*', fa_directory); while (file_name != '') { full_path = dir_name + '\' + file_name; if (file_attributes(full_path, fa_directory)) { // a directory // Put subdirectories into the queue. if (file_name != '.' && file_name != '..') { subdir_queue[queue_size] = full_path; queue_size += 1; } } else { // a normal file // Add the file to the list if it has the specified extension. if (string_lower(filename_ext(file_name)) == argument1) { global.files[global.file_count] = full_path; global.file_count += 1; } } file_name = file_find_next(); } file_find_close();}Edit: the important description and some of example code have been vanished for some reason (probably on transiting to the new board.) I repaired them from memory but they might not be the same as before; sorry for inconvenience.
Edited by torigara, 26 July 2010 - 02:11 AM.











