English Amiga Board


Go Back   English Amiga Board > Coders > Coders. System

 
 
Thread Tools
Old 19 July 2021, 02:09   #1
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
Question GetDeviceProc when 2 floppies share same name?

I need some help.

Outline of what I'm trying to do:
- on IDCMP_DISKREMOVED and IDCMP_DISKINSERTED, refresh the list of volumes attached to the system
- When a disk is removed, close any windows that referenced the contents of that disk (root level, but also any child directories that might have been opened)

How I tried to do it:
- on disk insert/eject event, get a new list of volumes, and compare against previous one. See which volumes got ejected, which got added.
- I used LockDosList + NextDosEntry to step through all the volume names. This gets me the human-readable names like "Work:", "Workbench:", "Games:", etc, not HD0:, HD1:, WORK:, DF0:, etc.
- Look through all the windows that are open, and check the name of an ejected disk against a property of the window, and if they matched, close the window.
- This works just fine until you put 2 disks with the same name into the system. For example, 2 disks named "my_disk" inserted in DF0 and DF1. If the user has windows for both open, all those windows gets closed when the disk is ejected.

Solution Design:
- I thought I could use both the file path (disk name) and the drive code (my term for DF0, DF1, HD0, etc. type dol_Names) to know if a window is from a given device.
- I can only get the drive code from the_dos_list->dol_Name (when the_dos_list->dol_Task == the_device_process->dvp_Port), which I get by calling GetDeviceProc with "my_disk" as the path
- The problem is that the paths for DF0:my_disk and DF1:my_disk are exactly the same ("my_path"). I don't even know if it's DF0 or DF1 until I query GetDeviceProc. So when I call GetDeviceProc(the_disk_name,NULL) for each one, it returns the SAME process for both. So I can never really get to the fssm for the 2nd disk.

Is there some easier way to build up the volume list, including device names, etc.? Rather than using GetDeviceProc, is there a way to just step through the DOS processes one by one? (I've looked through the AmigaDOS books I have, and dos.doc a few times, and it's not obvious to me, but I'm definitely not an expert in AmigaDOS).

Is there maybe some other approach altogether I should be taking to manage disk insertion/removal tracking?

I focused on floppies here, but same thing would happen with 2 hard drives with same name, or any combination.

Called on disk insert/disk remove events:
Code:
// populate the folder's List by iterating through the DOS List objects and treating each as a folder/file
boolean Folder_PopulateVolumeList(WB2KFolderObject* the_folder)
{

	// reset panel's file count, as we will be starting over from zero
	the_folder->file_count_ = 0;

	// get a date object, because we need something for File_New()
	struct DateStamp the_date = {0,0,0};

	// get the DOS list and iterate through it
	struct DosList* the_dos_list;
	the_dos_list = LockDosList(LDF_VOLUMES|LDF_READ);

	while ( (the_dos_list = NextDosEntry(the_dos_list, LDF_VOLUMES|LDF_READ)) != NULL)
	{
		// NOTE: the_dos_list->dol_Name is a BSTR!
		char	path_buffer[FILE_MAX_PATHNAME_SIZE];
		char*	the_path_buffer = path_buffer;
		char*	this_name = (char*)BADDR(the_dos_list->dol_Name);
		char*	disk_name = this_name + 1;
		
		General_Strlcpy(the_path_buffer, disk_name, FILE_MAX_PATHNAME_SIZE);
		General_Strlcat(the_path_buffer, ":", FILE_MAX_PATHNAME_SIZE);
		
		WB2KFileObject* this_file = File_New(disk_name, the_path_buffer, NULL, true, the_folder->icon_rport_, 0, the_date);

		if (this_file == NULL)
		{
			LOG_ERR(("Folder_PopulateVolumeList %d: Could not allocate memory for file object for '%s'", __LINE__, disk_name));
			goto error;
		}

		// Add this file to the list of files
		WB2KList* the_new_item = List_NewItem((void*)this_file);
		List_AddItem(the_folder->list_, the_new_item);
		the_folder->file_count_++;

	}

	UnLockDosList(LDF_VOLUMES|LDF_READ);

	// now assign all volumes with a file type
	Folder_AssignDeviceInformation(the_folder);
	
	return true;

error:
	return false;
}
Called after all volumes have been scanned once. I used to do this as I worked through each one above, but I redesigned it a bit to give it a chance to notice duplicate situations (but don't know what to do, having hit them)
Code:
// Iterate through all files in the folder, and find and assign the drive code file type. Allows for multiple devices to have same filepath.
boolean Folder_AssignDeviceInformation(WB2KFolderObject* the_folder)
{
	// LOGIC: 
	//   Challenge:
	//     GetDeviceProc() is the DOS function that lets you get to the low level stuff like DF0 vs DH0, but it takes a file path.
	//     If you have 2 floppies with same exact name, it returns after it hits the first one each time
	//     So both items in your volume list get listed as DF1 (or DF0), not one of each. 
	//   Solution:
	//     Loop through all files in the folder
	//     For each one, call GetDeviceProc. On any match, get the drive code, and check the folder for matches to that drive code
	//     If match found, then go back to NextDosEntry
	//     If no match, assign this drive code to the file and iterate to the next file

	// code for using GetDeviceProc based on this post on eab:
	// http://eab.abime.net/showthread.php?t=91496
	boolean				ok = true;
	struct DevProc*		the_device_process;
	WB2KList*			the_item = *(the_folder->list_);
	unsigned short		node_count = 0;

	while (the_item != NULL)
	{
		WB2KFileObject* this_file = (WB2KFileObject*)(the_item->payload_);

		// RAMdisk vs all others: RAMdisk will not have a device process
		if (Strnicmp((STRPTR)this_file->file_name_, (STRPTR)RAM_DISK_NAME, FILE_MAX_FILENAME_SIZE) == 0)
		{
			this_file->file_type_ = global_app->file_type_db_->disk_ram_filetype_;
			this_file->drive_code_ = General_StrlcpyWithAlloc((STRPTR)RAM_DISK_NAME, MAX_BSTR_SIZE);
		}
		else
		{
			// try to get device info
			boolean	found = false;
			the_device_process = NULL;
			
			if ((the_device_process = GetDeviceProc((STRPTR)this_file->file_path_, NULL)) != NULL)
			{
				struct DosList*				the_dos_list;
				struct FileSysStartupMsg*	fssm;
			
				the_dos_list = LockDosList (LDF_DEVICES | LDF_READ);

				while ((the_dos_list = NextDosEntry(the_dos_list, LDF_DEVICES)))
				{
					if (the_dos_list->dol_Task == the_device_process->dvp_Port)
					{
						// we have an appropriate kind of DOS list, and it is for a file that matches our filepath. 
						// need to see if this item has already been scanned or not
					
						fssm = BADDR(the_dos_list->dol_misc.dol_handler.dol_Startup);

						if (TypeOfMem(fssm) && (APTR)fssm > (APTR)1000)
						{
							WB2KList*	matched_item;
							char		temp_drive_code[MAX_BSTR_SIZE];
							char		device_name[MAX_BSTR_SIZE];
							unsigned char*	the_drive_code = temp_drive_code;
							unsigned char*	the_device_name = device_name;
						
							General_StrCpyBStr(device_name,BADDR(fssm->fssm_Device));
							General_StrCpyBStr(temp_drive_code,BADDR(the_dos_list->dol_Name));
							matched_item = Folder_FindListItemByFilePath(the_folder, this_file->file_path_, (short)FILE_MAX_PATHNAME_SIZE, true, the_drive_code);
						
							if ( matched_item == NULL)
							{
								// this is the first time we've seen this filepath + drive code combination
								this_file->drive_code_ = General_StrlcpyWithAlloc(temp_drive_code, MAX_BSTR_SIZE);
								File_DetermineDriveType(this_file, the_device_name);
								DEBUG_OUT(("Folder_AssignDeviceInformation %d: device_name '%s', drive_code='%s' assigned to file %u, '%s'", __LINE__, the_device_name, the_drive_code, node_count, this_file->file_path_));
								found = true;
								break;
							}
							else
							{
								DEBUG_OUT(("Folder_AssignDeviceInformation %d: device_name '%s', drive_code='%s' had already been assigned to file %d, '%s'", __LINE__, the_device_name, the_drive_code, node_count, this_file->file_path_));
							}
						}
					}
				}

				UnLockDosList(LDF_DEVICES | LDF_READ);

				FreeDeviceProc(the_device_process);
			}
			
			if (found == false)
			{
				// this can happen on a same-filepath, different drive situation
				DEBUG_OUT(("Folder_AssignDeviceInformation %d: file %u, '%s' couldn't get a match", __LINE__, node_count, this_file->file_path_));
				
				// err, now what??
			}
		}
		
		node_count++;
		the_item = the_item->next_item_;
	}

	return (ok);
}
Warty is offline  
Old 19 July 2021, 04:12   #2
Bruce Abbott
Registered User
 
Bruce Abbott's Avatar
 
Join Date: Mar 2018
Location: Hastings, New Zealand
Posts: 2,543
Quote:
Originally Posted by Warty View Post
Outline of what I'm trying to do:...

- When a disk is removed, close any windows that referenced the contents of that disk (root level, but also any child directories that might have been opened)
Why do you want to do this?
Bruce Abbott is offline  
Old 19 July 2021, 04:27   #3
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
Quote:
Originally Posted by Bruce Abbott View Post
Why do you want to do this?
I ask myself that question quite a lot recently.

Oh, wait, you mean "why do you want to close the windows when they eject the disk"? Mostly, I think it's confusing to have windows open when the floppy that they represent is no longer inserted. Also, I don't want to bother tracking ejected-but-still-remembered junk. If they eject it, it's gone.

Bigger picture, it's a file manager app I'm making to get up to speed on Amiga development. Tends to touch a pretty big range of the libraries, so it's been pretty effective as a learning tool. If a little frustrating at times, of course.
Attached Thumbnails
Click image for larger version

Name:	Screen Shot 2021-07-18 wb2k similar floppies.png
Views:	119
Size:	55.3 KB
ID:	72588  
Warty is offline  
Old 19 July 2021, 06:07   #4
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
<phew> got it working. Biggest help was from here:
https://forum.amiga.org/index.php?topic=73225.0

I'll post the code tomorrow after some clean-up. In essence, you go through the device list from DOSBase->dl_Root->rn_Info, look at each one with dl_Type == DLT_VOLUME to get the volume names, then pass that to another routine that goes through dvi->dvi_Type == DLT_DEVICE items, and compares dvi->dvi_Task to the dl_Task from the first function. If they match, it's the same drive/volume, and you have access to the device name (eg, DF0 and the 'driver' name (scsi.device, etc.).

I also found this little nugget, which suggests how AmigaDOS keeps them distinct:
Quote:
To distinguish disks with the same name, AmigaDOS timestamps the volume on creation and then saves the timestamp in the list structure. AmigaDOS can therefore compare the timestamps of different volumes whenever necessary.
https://wiki.amigaos.net/wiki/AmigaDOS_Data_Structures

So far my device name + volume name is adequate to keeping windows from various disks separate.
Warty is offline  
Old 20 July 2021, 02:39   #5
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
Here's the final code if anyone is looking for same thing in the future.

The flow is simple:
- Folder_PopulateVolumeList() iterates through DOSBase->dl_Root->rn_Info
- For each item it finds that is a volume, it is going to create a "file" object in my code, and then try to do 2 things:
- 1) find out what the device name (DH0:, DF0:, etc.) is. It calls General_FindMatchingDeviceName() to do this (see below)
- 2) it tries to classify the object as a floppy, RAM disk, Hard Drive, or CD-ROM. The logic should be pretty obvious. If you were going to be very complete, you'd want to add handling for other devices.
- General_FindMatchingDeviceName() takes a task, and then scans through the DOS list again, this time looking for items of type Device. For each of those, it compares to the task, and when they match, then that's the device info for the passed volume (task).


Code:
// populate the folder's List by iterating through the DOS List objects and treating each as a folder/file
boolean Folder_PopulateVolumeList(WB2KFolderObject* the_folder)
{
	// tricky stuff based on code from https://forum.amiga.org/index.php?topic=73225.0

	// reset panel's file count, as we will be starting over from zero
	the_folder->file_count_ = 0;

	// get the DOS list and iterate through it
	struct DosInfo*		the_dos_info = (struct DosInfo*)BADDR(DOSBase->dl_Root->rn_Info);
	struct DeviceList*	the_device_list;
	struct DeviceList*	this_volume = NULL;
	
	Forbid();

	for(the_device_list = (struct DeviceList *)BADDR(the_dos_info->di_DevInfo) ;
		the_device_list != NULL ;
		the_device_list = (struct DeviceList *)BADDR(the_device_list->dl_Next))
	{
		if(the_device_list->dl_Type == DLT_VOLUME)
		{
			// NOTE: the_device_list->dl_Name is a BSTR!
			unsigned char	path_buffer[FILE_MAX_PATHNAME_SIZE];
			unsigned char*	the_path_buffer = path_buffer;
			unsigned char*	this_name = (char*)BADDR(the_device_list->dl_Name);
			unsigned char*	volume_name = this_name + 1;
			unsigned char*	the_device_name;
			unsigned char*	the_driver_name;
		
			General_Strlcpy(the_path_buffer, volume_name, FILE_MAX_PATHNAME_SIZE);
			General_Strlcat(the_path_buffer, ":", FILE_MAX_PATHNAME_SIZE);

			// we have volume name, need to get a device name that matches up to the volume name, and also get the device type
			if (General_FindMatchingDeviceName(the_dos_info, the_device_list->, &the_device_name, &the_driver_name) == false)
			{
				LOG_ERR(("Folder_PopulateVolumeList %d: Could not find a device name for volume '%s'", __LINE__, the_path_buffer));
				return false;
			}
			
			//DEBUG_OUT(("Folder_PopulateVolumeList %d: device name '%s', driver name '%s'", __LINE__, the_device_name, the_driver_name));
			
			// now we can make a file object, we'll sort out device type and device name shortly
			WB2KFileObject* this_file = File_New(volume_name, the_path_buffer, the_device_name, true, the_folder->icon_rport_, 0, the_device_list->dl_VolumeDate);

			if (this_file == NULL)
			{
				LOG_ERR(("Folder_PopulateVolumeList %d: Could not allocate memory for file object for '%s'", __LINE__, volume_name));
				goto error;
			}
			
			// finally, set the file type
			// RAMdisk vs all others: RAMdisk will not have a device process; Amiga RAM Disks are always named "Ram Disk", regardless of language
			if (Strnicmp((STRPTR)this_file->file_name_, (STRPTR)RAM_DISK_NAME, FILE_MAX_FILENAME_SIZE) == 0)
			{
				this_file->file_type_ = global_app->file_type_db_->disk_ram_filetype_;
			}
			else
			{
				File_DetermineDriveType(this_file, the_driver_name);
			}
						
			// Add this file to the list of files
			WB2KList* the_new_item = List_NewItem((void*)this_file);
			List_AddItem(the_folder->list_, the_new_item);
			the_folder->file_count_++;
		}
	}
	
	Permit();

	//DEBUG
	//DEBUG_OUT(("Folder_PopulateVolumeList %d: file list:", __LINE__));
	//List_Print(the_folder->list_, &File_Print);
	
	return true;

error:
	return false;
}

Code:
// iterates through device list, looking for devices who dvi_Task matches the passed device list task. if found, sets the device name and driver name
boolean General_FindMatchingDeviceName(struct DosInfo* the_dos_info, struct MsgPort* the_device_list_task, unsigned char** the_device_name, unsigned char** the_driver_name)
{
	struct DevInfo*		dvi;
	boolean				found_device = false;

	for (dvi = (struct DevInfo *)BADDR(the_dos_info->di_DevInfo) ;
		dvi != NULL ;
		dvi = (struct DevInfo *)BADDR(dvi->dvi_Next))
	{
		if (dvi->dvi_Type == DLT_DEVICE && dvi->dvi_Task == the_device_list_task)
		{
			UBYTE*	name = BADDR(dvi->dvi_Name);
			int		name_len = name[0];
			struct FileSysStartupMsg*	fssm;
			
			fssm = BADDR(dvi->dvi_Startup);

			if (TypeOfMem(fssm) && (APTR)fssm > (APTR)1000)
			{
				UBYTE*		driver_name = BADDR(fssm->fssm_Device);
				int			driver_name_len = driver_name[0];
				
				driver_name++;
				*the_driver_name = General_StrlcpyWithAlloc(driver_name, driver_name_len + 1);
				//DEBUG_OUT(("General_FindMatchingDeviceName %d: found device driver name '%s'", __LINE__, *the_driver_name));
			}
							
			name++;
			*the_device_name = General_StrlcpyWithAlloc(name, name_len + 1);
			//DEBUG_OUT(("General_FindMatchingDeviceName %d: found device name '%s'", __LINE__, *the_device_name));
			
			found_device = true;
			break;
		}
	}

	return (found_device);
}
Warty is offline  
Old 20 July 2021, 11:26   #6
thomas
Registered User
 
thomas's Avatar
 
Join Date: Jan 2002
Location: Germany
Posts: 6,985
The thread title mentions GetDeviceProc which exists only in Kick 2.0+, so your target is not Kick 1.3, right?

In this case you should not peek DosBase for the top of the device list but rather work with LockDosList, NextDosEntry and UnlockDosList.


Edit: here is an example:
Code:
#include <proto/dos.h>

int main (void)

{
struct DosList *dl,*dev,*vol;

dl = LockDosList (LDF_DEVICES | LDF_VOLUMES | LDF_READ);

vol = dl;
while ((vol = NextDosEntry (vol,LDF_VOLUMES)))
	{
	if (vol->dol_Task)
		{
		dev = dl;
		while ((dev = NextDosEntry (dev,LDF_DEVICES)))
			{
			if (dev->dol_Task == vol->dol_Task)
				break;
			}

		if (dev)
			Printf ("%-30b %b\n",dev->dol_Name,vol->dol_Name);
		else
			Printf ("%-30s %b\n","(internal error)",vol->dol_Name);
		}
	else
		Printf ("%-30s %b\n","(not mounted)",vol->dol_Name);
	}

UnLockDosList (LDF_DEVICES | LDF_VOLUMES | LDF_READ);

return (RETURN_OK);
}

Last edited by thomas; 20 July 2021 at 15:08.
thomas is offline  
Old 21 July 2021, 04:30   #7
Warty
Registered User
 
Join Date: Aug 2018
Location: Minneapolis, USA
Posts: 301
Thanks for putting together that example Thomas! I adapted my code to that. I think I finally understand now how to traverse the list for devices and volumes. I hadn't really noticed what LDF_DEVICES and LDF_VOLUMES were doing.
Warty is offline  
 


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
Got new games to share :) dlfrsilver request.Old Rare Games 14 16 November 2015 20:38
Network share PC to AMIGA paulisthebest support.Other 3 21 September 2010 16:36
Share Your Save Games Djay Nostalgia & memories 2 10 May 2010 21:30
Have to share this :) Washac Retrogaming General Discussion 23 13 February 2010 11:14
Just thought I'd share this with everyone alkis21 Retrogaming General Discussion 5 09 September 2002 18:46

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 09:59.

Top

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Page generated in 0.21468 seconds with 16 queries