mirror of https://gitlab.com/nakst/essence
				
				
				
			general: use contentType field for file types
This commit is contained in:
		
							parent
							
								
									4f9b5194cd
								
							
						
					
					
						commit
						910bc1bbb0
					
				| 
						 | 
					@ -24,11 +24,9 @@ You can download and test the latest nightly build from https://github.com/nakst
 | 
				
			||||||
 | 
					
 | 
				
			||||||
These builds are configured to run on emulators only, to make testing easier. Builds for real hardware are coming soon :)
 | 
					These builds are configured to run on emulators only, to make testing easier. Builds for real hardware are coming soon :)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Source
 | 
					## Building
 | 
				
			||||||
 | 
					
 | 
				
			||||||
See `help/Building.md` for a description of how to build and test the system from source.
 | 
					See `help/Building.md` for a description of how to build and test the system.
 | 
				
			||||||
 | 
					 | 
				
			||||||
For an overview of the files in the source tree, see `help/Source Map.md`.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Features
 | 
					## Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,108 +13,131 @@ background_service=1
 | 
				
			||||||
source=apps/file_manager/main.cpp
 | 
					source=apps/file_manager/main.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=esx
 | 
					match=ext:esx
 | 
				
			||||||
name=Executable
 | 
					name=Executable
 | 
				
			||||||
icon=icon_application_default_icon
 | 
					icon=icon_application_default_icon
 | 
				
			||||||
 | 
					uuid=BF-88-E4-DD-71-E8-5F-DE-16-C5-AF-33-41-C7-A2-96
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=ini
 | 
					match=ext:ini
 | 
				
			||||||
name=Configuration file
 | 
					name=Configuration file
 | 
				
			||||||
icon=icon_application_x_desktop
 | 
					icon=icon_application_x_desktop
 | 
				
			||||||
 | 
					uuid=81-72-43-29-E5-37-89-51-8E-22-A0-67-A5-B9-42-2C
 | 
				
			||||||
 | 
					textual=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=esx_symbols
 | 
					match=ext:png
 | 
				
			||||||
name=Debugging data
 | 
					 | 
				
			||||||
icon=icon_extension
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[file_type]
 | 
					 | 
				
			||||||
extension=exe
 | 
					 | 
				
			||||||
name=PC executable
 | 
					 | 
				
			||||||
icon=icon_application_x_ms_dos_executable
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[file_type]
 | 
					 | 
				
			||||||
extension=png
 | 
					 | 
				
			||||||
name=PNG image
 | 
					name=PNG image
 | 
				
			||||||
icon=icon_image_x_generic
 | 
					icon=icon_image_x_generic
 | 
				
			||||||
has_thumbnail_generator=1
 | 
					has_thumbnail_generator=1
 | 
				
			||||||
 | 
					uuid=59-21-05-4D-34-40-AB-61-EC-F9-7D-5C-6E-04-96-AE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=jpg
 | 
					match=ext:jpg,ext:jpeg
 | 
				
			||||||
name=JPG image
 | 
					name=JPG image
 | 
				
			||||||
icon=icon_image_x_generic
 | 
					icon=icon_image_x_generic
 | 
				
			||||||
has_thumbnail_generator=1
 | 
					has_thumbnail_generator=1
 | 
				
			||||||
 | 
					uuid=D8-C2-13-B0-53-64-82-11-48-7B-5B-64-0F-92-B9-38
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=ttf
 | 
					match=ext:tga
 | 
				
			||||||
 | 
					name=TGA image
 | 
				
			||||||
 | 
					icon=icon_image_x_generic
 | 
				
			||||||
 | 
					has_thumbnail_generator=1
 | 
				
			||||||
 | 
					uuid=69-62-4E-28-A1-E1-7B-35-64-2E-36-01-65-91-BE-A1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[file_type]
 | 
				
			||||||
 | 
					match=ext:bmp
 | 
				
			||||||
 | 
					name=BMP image
 | 
				
			||||||
 | 
					icon=icon_image_x_generic
 | 
				
			||||||
 | 
					has_thumbnail_generator=1
 | 
				
			||||||
 | 
					uuid=40-15-B7-82-99-6D-D5-41-96-D5-3B-6D-A6-5F-07-34
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[file_type]
 | 
				
			||||||
 | 
					match=ext:ttf
 | 
				
			||||||
name=TrueType font
 | 
					name=TrueType font
 | 
				
			||||||
icon=icon_font_x_generic
 | 
					icon=icon_font_x_generic
 | 
				
			||||||
 | 
					uuid=DA-BF-EC-06-31-8A-67-B6-E3-95-BC-D1-92-D2-9A-56
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=otf
 | 
					match=ext:otf
 | 
				
			||||||
name=OpenType font
 | 
					name=OpenType font
 | 
				
			||||||
icon=icon_font_x_generic
 | 
					icon=icon_font_x_generic
 | 
				
			||||||
 | 
					uuid=E7-2B-BB-E7-FF-75-32-E0-4E-75-41-C0-D2-ED-80-56
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=a
 | 
					match=ext:a
 | 
				
			||||||
name=Static library
 | 
					name=Static library
 | 
				
			||||||
icon=icon_extension
 | 
					icon=icon_extension
 | 
				
			||||||
 | 
					uuid=7D-39-BF-18-9E-07-BC-D3-4F-9F-87-EC-6F-70-65-5D
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=dat
 | 
					match=ext:dat
 | 
				
			||||||
name=Database
 | 
					name=Application data
 | 
				
			||||||
icon=icon_office_database
 | 
					icon=icon_office_database
 | 
				
			||||||
 | 
					uuid=B5-32-22-14-01-CB-D0-1D-A2-55-08-C7-0D-46-86-53
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=icon_pack
 | 
					match=ext:ekm
 | 
				
			||||||
name=Icon pack
 | 
					 | 
				
			||||||
icon=icon_package_x_generic
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[file_type]
 | 
					 | 
				
			||||||
extension=ekm
 | 
					 | 
				
			||||||
name=Kernel module
 | 
					name=Kernel module
 | 
				
			||||||
icon=icon_extension
 | 
					icon=icon_extension
 | 
				
			||||||
 | 
					uuid=80-6D-8E-D6-C7-45-AD-A7-03-5B-B3-64-C5-0A-EE-C6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=o
 | 
					match=ext:o
 | 
				
			||||||
name=Binary object
 | 
					name=Binary object
 | 
				
			||||||
icon=icon_extension
 | 
					icon=icon_extension
 | 
				
			||||||
 | 
					uuid=B5-19-F2-0D-AD-4F-D4-C9-A4-BF-97-E4-5E-4C-55-00
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=c
 | 
					match=ext:c
 | 
				
			||||||
name=C source
 | 
					name=C source
 | 
				
			||||||
icon=icon_text_x_csrc
 | 
					icon=icon_text_x_csrc
 | 
				
			||||||
 | 
					uuid=36-02-D3-AC-6C-DE-8D-31-FA-70-B2-DA-FA-81-53-69
 | 
				
			||||||
 | 
					textual=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=cpp
 | 
					match=ext:cpp
 | 
				
			||||||
name=C++ source
 | 
					name=C++ source
 | 
				
			||||||
icon=icon_text_x_csrc
 | 
					icon=icon_text_x_csrc
 | 
				
			||||||
 | 
					uuid=35-84-A6-0E-52-28-BA-AB-CA-74-14-F4-E1-15-CA-5F
 | 
				
			||||||
 | 
					textual=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=h
 | 
					match=ext:h
 | 
				
			||||||
name=C header
 | 
					name=C header
 | 
				
			||||||
icon=icon_text_x_csrc
 | 
					icon=icon_text_x_csrc
 | 
				
			||||||
 | 
					uuid=D1-16-C4-C1-7B-C0-ED-4F-CA-AC-18-05-32-1D-56-32
 | 
				
			||||||
 | 
					textual=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=txt
 | 
					match=ext:txt
 | 
				
			||||||
name=Text file
 | 
					name=Text file
 | 
				
			||||||
icon=icon_text
 | 
					icon=icon_text
 | 
				
			||||||
 | 
					uuid=25-65-4C-89-E7-29-EA-9E-B5-BE-B5-CA-A7-60-BD-3D
 | 
				
			||||||
 | 
					textual=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=md
 | 
					match=ext:md
 | 
				
			||||||
name=Markdown file
 | 
					name=Markdown file
 | 
				
			||||||
icon=icon_text_markdown
 | 
					icon=icon_text_markdown
 | 
				
			||||||
 | 
					uuid=B1-C3-9E-56-C9-B0-85-D6-AF-98-12-1C-2E-29-8D-24
 | 
				
			||||||
 | 
					textual=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=ogg
 | 
					match=ext:ogg
 | 
				
			||||||
name=OGG audio
 | 
					name=OGG audio
 | 
				
			||||||
icon=icon_audio_x_generic
 | 
					icon=icon_audio_x_generic
 | 
				
			||||||
 | 
					uuid=E5-14-14-ED-4D-67-C0-D0-62-A1-A4-D4-F5-82-A7-88
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=mp4
 | 
					match=ext:mp4
 | 
				
			||||||
name=MP4 video
 | 
					name=MP4 video
 | 
				
			||||||
icon=icon_view_list_video_symbolic
 | 
					icon=icon_view_list_video_symbolic
 | 
				
			||||||
 | 
					uuid=02-76-E1-41-B8-54-19-A9-05-8D-C1-3F-6E-8F-D7-9E
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=wav
 | 
					match=ext:wav
 | 
				
			||||||
name=Wave audio
 | 
					name=Wave audio
 | 
				
			||||||
icon=icon_audio_x_generic
 | 
					icon=icon_audio_x_generic
 | 
				
			||||||
 | 
					uuid=87-BC-A1-A1-25-76-26-5C-8F-94-D6-D8-35-B6-7A-9F
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,10 +22,11 @@ void CommandRename(Instance *instance, EsElement *, EsCommand *) {
 | 
				
			||||||
	instance->rename.index = index;
 | 
						instance->rename.index = index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	FolderEntry *entry = instance->listContents[index].entry;
 | 
						FolderEntry *entry = instance->listContents[index].entry;
 | 
				
			||||||
 | 
						ptrdiff_t extensionOffset = PathGetExtension(entry->GetName()).text - entry->name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (entry->extensionOffset != entry->nameBytes) {
 | 
						if (extensionOffset != entry->nameBytes) {
 | 
				
			||||||
		// Don't include the file extension in the initial selection.
 | 
							// Don't include the file extension in the initial selection.
 | 
				
			||||||
		EsTextboxSetSelection(instance->rename.textbox, 0, 0, 0, entry->extensionOffset - 1);
 | 
							EsTextboxSetSelection(instance->rename.textbox, 0, 0, 0, extensionOffset - 1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	instance->rename.textbox->messageUser = [] (EsElement *element, EsMessage *message) {
 | 
						instance->rename.textbox->messageUser = [] (EsElement *element, EsMessage *message) {
 | 
				
			||||||
| 
						 | 
					@ -499,23 +500,32 @@ void InstanceRegisterCommands(Instance *instance) {
 | 
				
			||||||
	EsCommandRegister(&instance->commandRename, instance, INTERFACE_STRING(FileManagerRenameAction), CommandRename, stableCommandID++, "F2");
 | 
						EsCommandRegister(&instance->commandRename, instance, INTERFACE_STRING(FileManagerRenameAction), CommandRename, stableCommandID++, "F2");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EsCommandRegister(&instance->commandViewDetails, instance, INTERFACE_STRING(CommonListViewTypeDetails), [] (Instance *instance, EsElement *, EsCommand *) {
 | 
						EsCommandRegister(&instance->commandViewDetails, instance, INTERFACE_STRING(CommonListViewTypeDetails), [] (Instance *instance, EsElement *, EsCommand *) {
 | 
				
			||||||
		uint8_t old = instance->viewSettings.viewType;
 | 
							if (instance->viewSettings.viewType != VIEW_DETAILS) {
 | 
				
			||||||
 | 
								EsElementStartTransition(instance->list, ES_TRANSITION_FADE, ES_ELEMENT_TRANSITION_CONTENT_ONLY, 1.0f);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		instance->viewSettings.viewType = VIEW_DETAILS;
 | 
							instance->viewSettings.viewType = VIEW_DETAILS;
 | 
				
			||||||
		InstanceRefreshViewType(instance, old != instance->viewSettings.viewType);
 | 
							InstanceRefreshViewType(instance);
 | 
				
			||||||
		InstanceViewSettingsUpdated(instance);
 | 
							InstanceViewSettingsUpdated(instance);
 | 
				
			||||||
	}, stableCommandID++);
 | 
						}, stableCommandID++);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EsCommandRegister(&instance->commandViewTiles, instance, INTERFACE_STRING(CommonListViewTypeTiles), [] (Instance *instance, EsElement *, EsCommand *) {
 | 
						EsCommandRegister(&instance->commandViewTiles, instance, INTERFACE_STRING(CommonListViewTypeTiles), [] (Instance *instance, EsElement *, EsCommand *) {
 | 
				
			||||||
		uint8_t old = instance->viewSettings.viewType;
 | 
							if (instance->viewSettings.viewType != VIEW_TILES) {
 | 
				
			||||||
 | 
								EsElementStartTransition(instance->list, ES_TRANSITION_FADE, ES_ELEMENT_TRANSITION_CONTENT_ONLY, 1.0f);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		instance->viewSettings.viewType = VIEW_TILES;
 | 
							instance->viewSettings.viewType = VIEW_TILES;
 | 
				
			||||||
		InstanceRefreshViewType(instance, old != instance->viewSettings.viewType);
 | 
							InstanceRefreshViewType(instance);
 | 
				
			||||||
		InstanceViewSettingsUpdated(instance);
 | 
							InstanceViewSettingsUpdated(instance);
 | 
				
			||||||
	}, stableCommandID++);
 | 
						}, stableCommandID++);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EsCommandRegister(&instance->commandViewThumbnails, instance, INTERFACE_STRING(CommonListViewTypeThumbnails), [] (Instance *instance, EsElement *, EsCommand *) {
 | 
						EsCommandRegister(&instance->commandViewThumbnails, instance, INTERFACE_STRING(CommonListViewTypeThumbnails), [] (Instance *instance, EsElement *, EsCommand *) {
 | 
				
			||||||
		uint8_t old = instance->viewSettings.viewType;
 | 
							if (instance->viewSettings.viewType != VIEW_THUMBNAILS) {
 | 
				
			||||||
 | 
								EsElementStartTransition(instance->list, ES_TRANSITION_FADE, ES_ELEMENT_TRANSITION_CONTENT_ONLY, 1.0f);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		instance->viewSettings.viewType = VIEW_THUMBNAILS;
 | 
							instance->viewSettings.viewType = VIEW_THUMBNAILS;
 | 
				
			||||||
		InstanceRefreshViewType(instance, old != instance->viewSettings.viewType);
 | 
							InstanceRefreshViewType(instance);
 | 
				
			||||||
		InstanceViewSettingsUpdated(instance);
 | 
							InstanceViewSettingsUpdated(instance);
 | 
				
			||||||
	}, stableCommandID++);
 | 
						}, stableCommandID++);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -394,7 +394,6 @@ FolderEntry *FolderAddEntry(Folder *folder, const char *_name, size_t nameBytes,
 | 
				
			||||||
		entry->handles = 1; 
 | 
							entry->handles = 1; 
 | 
				
			||||||
		entry->name = name;
 | 
							entry->name = name;
 | 
				
			||||||
		entry->nameBytes = nameBytes;
 | 
							entry->nameBytes = nameBytes;
 | 
				
			||||||
		entry->extensionOffset = PathGetExtension(entry->GetName()).text - name;
 | 
					 | 
				
			||||||
		entry->internalName = name;
 | 
							entry->internalName = name;
 | 
				
			||||||
		entry->internalNameBytes = nameBytes;
 | 
							entry->internalNameBytes = nameBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -417,6 +416,7 @@ FolderEntry *FolderAddEntry(Folder *folder, const char *_name, size_t nameBytes,
 | 
				
			||||||
	entry->size = information->fileSize;
 | 
						entry->size = information->fileSize;
 | 
				
			||||||
	entry->isFolder = information->type == ES_NODE_DIRECTORY;
 | 
						entry->isFolder = information->type == ES_NODE_DIRECTORY;
 | 
				
			||||||
	entry->sizeUnknown = entry->isFolder && information->directoryChildren == ES_DIRECTORY_CHILDREN_UNKNOWN;
 | 
						entry->sizeUnknown = entry->isFolder && information->directoryChildren == ES_DIRECTORY_CHILDREN_UNKNOWN;
 | 
				
			||||||
 | 
						entry->contentType = information->contentType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return entry;
 | 
						return entry;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -459,12 +459,26 @@ uint64_t FolderRemoveEntryAndUpdateInstances(Folder *folder, const char *name, s
 | 
				
			||||||
	return id;
 | 
						return id;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void FolderFileUpdatedAtPath(String path, Instance *instance) {
 | 
					void FolderFileUpdatedAtPath(String path, Instance *instance, bool setGuessedContentType = false) {
 | 
				
			||||||
	path = PathRemoveTrailingSlash(path);
 | 
						path = PathRemoveTrailingSlash(path);
 | 
				
			||||||
	String file = PathGetName(path);
 | 
						String file = PathGetName(path);
 | 
				
			||||||
	String folder = PathGetParent(path);
 | 
						String folder = PathGetParent(path);
 | 
				
			||||||
	EsDirectoryChild information = {};
 | 
						EsDirectoryChild information = {};
 | 
				
			||||||
	bool add = ES_SUCCESS == EsPathQueryInformation(STRING(path), &information);
 | 
						bool add = ES_SUCCESS == EsPathQueryInformation(STRING(path), &information);
 | 
				
			||||||
 | 
						EsUniqueIdentifier zeroIdentifier = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (setGuessedContentType && 0 == EsMemoryCompare(&information.contentType, &zeroIdentifier, sizeof(EsUniqueIdentifier))) {
 | 
				
			||||||
 | 
							EsUniqueIdentifier identifier = FileTypeMatchByExtension(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (EsMemoryCompare(&identifier, &zeroIdentifier, sizeof(EsUniqueIdentifier))) {
 | 
				
			||||||
 | 
								EsFileInformation information = EsFileOpen(STRING(path), ES_FILE_WRITE_SHARED | ES_NODE_FAIL_IF_NOT_FOUND);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (information.error == ES_SUCCESS) {
 | 
				
			||||||
 | 
									EsFileControl(information.handle, ES_FILE_CONTROL_SET_CONTENT_TYPE, &identifier, sizeof(identifier));
 | 
				
			||||||
 | 
									EsHandleClose(information.handle);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (uintptr_t i = 0; i < loadedFolders.Length(); i++) {
 | 
						for (uintptr_t i = 0; i < loadedFolders.Length(); i++) {
 | 
				
			||||||
		if (loadedFolders[i]->itemHandler->type != NAMESPACE_HANDLER_FILE_SYSTEM) continue;
 | 
							if (loadedFolders[i]->itemHandler->type != NAMESPACE_HANDLER_FILE_SYSTEM) continue;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,12 +53,14 @@ const char *errorTypeStrings[] = {
 | 
				
			||||||
#define LOAD_FOLDER_NO_FOCUS (1 << 8)
 | 
					#define LOAD_FOLDER_NO_FOCUS (1 << 8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct FolderEntry {
 | 
					struct FolderEntry {
 | 
				
			||||||
 | 
						// TODO Can this structure be made smaller?
 | 
				
			||||||
	uint16_t handles;
 | 
						uint16_t handles;
 | 
				
			||||||
	bool isFolder, sizeUnknown;
 | 
						bool isFolder, sizeUnknown, guessedContentType;
 | 
				
			||||||
	uint8_t nameBytes, extensionOffset, internalNameBytes; // 0 -> 256.
 | 
						uint8_t nameBytes, internalNameBytes; // 0 -> 256.
 | 
				
			||||||
	char *name, *internalName;
 | 
						char *name, *internalName;
 | 
				
			||||||
	EsFileOffset size, previousSize;
 | 
						EsFileOffset size, previousSize;
 | 
				
			||||||
	uint64_t id;
 | 
						uint64_t id;
 | 
				
			||||||
 | 
						EsUniqueIdentifier contentType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inline String GetName() { 
 | 
						inline String GetName() { 
 | 
				
			||||||
		return { .text = name, .bytes = nameBytes ?: 256u, .allocated = nameBytes ?: 256u }; 
 | 
							return { .text = name, .bytes = nameBytes ?: 256u, .allocated = nameBytes ?: 256u }; 
 | 
				
			||||||
| 
						 | 
					@ -67,11 +69,6 @@ struct FolderEntry {
 | 
				
			||||||
	inline String GetInternalName() { 
 | 
						inline String GetInternalName() { 
 | 
				
			||||||
		return { .text = internalName, .bytes = internalNameBytes ?: 256u, .allocated = internalNameBytes ?: 256u }; 
 | 
							return { .text = internalName, .bytes = internalNameBytes ?: 256u, .allocated = internalNameBytes ?: 256u }; 
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	inline String GetExtension() { 
 | 
					 | 
				
			||||||
		uintptr_t offset = extensionOffset ?: 256u;
 | 
					 | 
				
			||||||
		return { .text = name + offset, .bytes = (nameBytes ?: 256u) - offset }; 
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ListEntry {
 | 
					struct ListEntry {
 | 
				
			||||||
| 
						 | 
					@ -227,7 +224,7 @@ void InstanceReportError(struct Instance *instance, int error, EsError code);
 | 
				
			||||||
bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, int historyMode = 0);
 | 
					bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, int historyMode = 0);
 | 
				
			||||||
void InstanceUpdateStatusString(Instance *instance);
 | 
					void InstanceUpdateStatusString(Instance *instance);
 | 
				
			||||||
void InstanceViewSettingsUpdated(Instance *instance);
 | 
					void InstanceViewSettingsUpdated(Instance *instance);
 | 
				
			||||||
void InstanceRefreshViewType(Instance *instance, bool startTransition);
 | 
					void InstanceRefreshViewType(Instance *instance);
 | 
				
			||||||
void InstanceFolderPathChanged(Instance *instance, bool fromLoadFolder);
 | 
					void InstanceFolderPathChanged(Instance *instance, bool fromLoadFolder);
 | 
				
			||||||
void InstanceAddContents(struct Instance *instance, HashTable *newEntries);
 | 
					void InstanceAddContents(struct Instance *instance, HashTable *newEntries);
 | 
				
			||||||
void InstanceAddSingle(struct Instance *instance, ListEntry newEntry);
 | 
					void InstanceAddSingle(struct Instance *instance, ListEntry newEntry);
 | 
				
			||||||
| 
						 | 
					@ -583,6 +580,10 @@ void _start() {
 | 
				
			||||||
				StringDestroy(&openDocuments[i]);
 | 
									StringDestroy(&openDocuments[i]);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (uintptr_t i = 0; i < knownFileTypes.Length(); i++) {
 | 
				
			||||||
 | 
									knownFileTypes[i].applicationEntries.Free();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			EsAssert(!instances.Length());
 | 
								EsAssert(!instances.Length());
 | 
				
			||||||
			EsHeapFree(fileTypesBuffer.out);
 | 
								EsHeapFree(fileTypesBuffer.out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -608,7 +609,7 @@ void _start() {
 | 
				
			||||||
			size_t pathSectionCount = PathCountSections(fullPath);
 | 
								size_t pathSectionCount = PathCountSections(fullPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for (uintptr_t i = 0; i < pathSectionCount; i++) {
 | 
								for (uintptr_t i = 0; i < pathSectionCount; i++) {
 | 
				
			||||||
				FolderFileUpdatedAtPath(PathGetParent(fullPath, i + 1), nullptr);
 | 
									FolderFileUpdatedAtPath(PathGetParent(fullPath, i + 1), nullptr, true);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			EsHandleClose(message->user.context1.u);
 | 
								EsHandleClose(message->user.context1.u);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,101 +2,242 @@
 | 
				
			||||||
// It is released under the terms of the MIT license -- see LICENSE.md.
 | 
					// It is released under the terms of the MIT license -- see LICENSE.md.
 | 
				
			||||||
// Written by: nakst.
 | 
					// Written by: nakst.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct FileTypeApplicationEntry {
 | 
				
			||||||
 | 
						int64_t application;
 | 
				
			||||||
 | 
						bool open;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct FileType {
 | 
					struct FileType {
 | 
				
			||||||
 | 
						Array<FileTypeApplicationEntry> applicationEntries; // One per application that is involved with this type.
 | 
				
			||||||
 | 
						EsUniqueIdentifier identifier;
 | 
				
			||||||
	char *name;
 | 
						char *name;
 | 
				
			||||||
	size_t nameBytes;
 | 
						size_t nameBytes;
 | 
				
			||||||
	char *extension;
 | 
					 | 
				
			||||||
	size_t extensionBytes;
 | 
					 | 
				
			||||||
	uint32_t iconID;
 | 
						uint32_t iconID;
 | 
				
			||||||
	int64_t openHandler;
 | 
						bool textual;
 | 
				
			||||||
 | 
						bool hasThumbnailGenerator; // TODO Allow applications to register their own thumbnail generators.
 | 
				
			||||||
	// TODO Allow applications to register their own thumbnail generators.
 | 
					 | 
				
			||||||
	bool hasThumbnailGenerator;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Array<FileType> knownFileTypes; 
 | 
					Array<FileType> knownFileTypes; 
 | 
				
			||||||
HashStore<char, uintptr_t /* index into knownFileTypes */> knownFileTypesByExtension;
 | 
					HashStore<char, EsUniqueIdentifier> knownFileTypesByExtension;
 | 
				
			||||||
EsBuffer fileTypesBuffer;
 | 
					EsBuffer fileTypesBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void AddKnownFileTypes() {
 | 
					void AddKnownFileTypes() {
 | 
				
			||||||
#define ADD_FILE_TYPE(_extension, _name, _iconID) \
 | 
					#define ADD_FILE_TYPE(_name, _iconID) \
 | 
				
			||||||
	{ \
 | 
						{ \
 | 
				
			||||||
		FileType type = {}; \
 | 
							FileType type = {}; \
 | 
				
			||||||
		type.name = (char *) _name; \
 | 
							type.name = (char *) _name; \
 | 
				
			||||||
		type.nameBytes = EsCStringLength(_name); \
 | 
							type.nameBytes = EsCStringLength(_name); \
 | 
				
			||||||
		type.extension = (char *) _extension; \
 | 
					 | 
				
			||||||
		type.extensionBytes = EsCStringLength(_extension); \
 | 
					 | 
				
			||||||
		type.iconID = _iconID; \
 | 
							type.iconID = _iconID; \
 | 
				
			||||||
		knownFileTypes.Add(type); \
 | 
							knownFileTypes.Add(type); \
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define KNOWN_FILE_TYPE_DIRECTORY (0)
 | 
					#define KNOWN_FILE_TYPE_DIRECTORY (0)
 | 
				
			||||||
	ADD_FILE_TYPE("", interfaceString_CommonItemFolder, ES_ICON_FOLDER);
 | 
						ADD_FILE_TYPE(interfaceString_CommonItemFolder, ES_ICON_FOLDER);
 | 
				
			||||||
#define KNOWN_FILE_TYPE_UNKNOWN (1)
 | 
					#define KNOWN_FILE_TYPE_UNKNOWN (1)
 | 
				
			||||||
	ADD_FILE_TYPE("", interfaceString_CommonItemFile, ES_ICON_UNKNOWN);
 | 
						ADD_FILE_TYPE(interfaceString_CommonItemFile, ES_ICON_UNKNOWN);
 | 
				
			||||||
#define KNOWN_FILE_TYPE_DRIVE_HDD (2)
 | 
					#define KNOWN_FILE_TYPE_DRIVE_HDD (2)
 | 
				
			||||||
	ADD_FILE_TYPE("", interfaceString_CommonDriveHDD, ES_ICON_DRIVE_HARDDISK);
 | 
						ADD_FILE_TYPE(interfaceString_CommonDriveHDD, ES_ICON_DRIVE_HARDDISK);
 | 
				
			||||||
#define KNOWN_FILE_TYPE_DRIVE_SSD (3)
 | 
					#define KNOWN_FILE_TYPE_DRIVE_SSD (3)
 | 
				
			||||||
	ADD_FILE_TYPE("", interfaceString_CommonDriveSSD, ES_ICON_DRIVE_HARDDISK_SOLIDSTATE);
 | 
						ADD_FILE_TYPE(interfaceString_CommonDriveSSD, ES_ICON_DRIVE_HARDDISK_SOLIDSTATE);
 | 
				
			||||||
#define KNOWN_FILE_TYPE_DRIVE_CDROM (4)
 | 
					#define KNOWN_FILE_TYPE_DRIVE_CDROM (4)
 | 
				
			||||||
	ADD_FILE_TYPE("", interfaceString_CommonDriveCDROM, ES_ICON_MEDIA_OPTICAL);
 | 
						ADD_FILE_TYPE(interfaceString_CommonDriveCDROM, ES_ICON_MEDIA_OPTICAL);
 | 
				
			||||||
#define KNOWN_FILE_TYPE_DRIVE_USB_MASS_STORAGE (5)
 | 
					#define KNOWN_FILE_TYPE_DRIVE_USB_MASS_STORAGE (5)
 | 
				
			||||||
	ADD_FILE_TYPE("", interfaceString_CommonDriveUSBMassStorage, ES_ICON_DRIVE_REMOVABLE_MEDIA_USB);
 | 
						ADD_FILE_TYPE(interfaceString_CommonDriveUSBMassStorage, ES_ICON_DRIVE_REMOVABLE_MEDIA_USB);
 | 
				
			||||||
#define KNOWN_FILE_TYPE_DRIVES_PAGE (6)
 | 
					#define KNOWN_FILE_TYPE_DRIVES_PAGE (6)
 | 
				
			||||||
	ADD_FILE_TYPE("", interfaceString_FileManagerDrivesPage, ES_ICON_COMPUTER_LAPTOP);
 | 
						ADD_FILE_TYPE(interfaceString_FileManagerDrivesPage, ES_ICON_COMPUTER_LAPTOP);
 | 
				
			||||||
 | 
					#define KNOWN_FILE_TYPE_SPECIAL_COUNT (7)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fileTypesBuffer = { .canGrow = true };
 | 
						fileTypesBuffer = { .canGrow = true };
 | 
				
			||||||
	EsSystemConfigurationReadFileTypes(&fileTypesBuffer);
 | 
						EsSystemConfigurationReadFileTypes(&fileTypesBuffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EsINIState s = { .buffer = (char *) fileTypesBuffer.out, .bytes = fileTypesBuffer.bytes };
 | 
						EsINIState s = { .buffer = (char *) fileTypesBuffer.out, .bytes = fileTypesBuffer.bytes };
 | 
				
			||||||
	FileType type = {};
 | 
						FileType type = {};
 | 
				
			||||||
 | 
						FileTypeApplicationEntry applicationEntry = {};
 | 
				
			||||||
 | 
						bool specifiedTextual = false, specifiedHasThumbnailGenerator = false, hasUUID = false;
 | 
				
			||||||
 | 
						const char *matchValue;
 | 
				
			||||||
 | 
						size_t matchValueBytes = 0;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	while (EsINIParse(&s)) {
 | 
						while (EsINIParse(&s)) {
 | 
				
			||||||
		if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("name"))) {
 | 
							if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("name"))) {
 | 
				
			||||||
			type.name = s.value, type.nameBytes = s.valueBytes;
 | 
								type.name = s.value, type.nameBytes = s.valueBytes;
 | 
				
			||||||
		} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("extension"))) {
 | 
							} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("uuid"))) {
 | 
				
			||||||
			type.extension = s.value, type.extensionBytes = s.valueBytes;
 | 
								type.identifier = EsUniqueIdentifierParse(s.value, s.valueBytes);
 | 
				
			||||||
		} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("open"))) {
 | 
								hasUUID = true;
 | 
				
			||||||
			type.openHandler = EsIntegerParse(s.value, s.valueBytes);
 | 
					 | 
				
			||||||
		} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("icon"))) {
 | 
							} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("icon"))) {
 | 
				
			||||||
			type.iconID = EsIconIDFromString(s.value, s.valueBytes);
 | 
								type.iconID = EsIconIDFromString(s.value, s.valueBytes);
 | 
				
			||||||
		} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("has_thumbnail_generator")) && EsIntegerParse(s.value, s.valueBytes)) {
 | 
							} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("has_thumbnail_generator"))) {
 | 
				
			||||||
			// TODO Proper thumbnail generator registrations.
 | 
								// TODO Proper thumbnail generator registrations.
 | 
				
			||||||
			type.hasThumbnailGenerator = true;
 | 
								type.hasThumbnailGenerator = EsIntegerParse(s.value, s.valueBytes) != 0;
 | 
				
			||||||
 | 
							} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("textual"))) {
 | 
				
			||||||
 | 
								type.textual = EsIntegerParse(s.value, s.valueBytes) != 0;
 | 
				
			||||||
 | 
							} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("application"))) {
 | 
				
			||||||
 | 
								applicationEntry.application = EsIntegerParse(s.value, s.valueBytes);
 | 
				
			||||||
 | 
							} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("actions"))) {
 | 
				
			||||||
 | 
								uintptr_t p = 0, q = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								while (q <= s.valueBytes) {
 | 
				
			||||||
 | 
									if (q == s.valueBytes || s.value[q] == ',') {
 | 
				
			||||||
 | 
										String string = StringFromLiteralWithSize(&s.value[p], q - p);
 | 
				
			||||||
 | 
										p = q + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (StringEquals(string, StringFromLiteral("open"))) {
 | 
				
			||||||
 | 
											applicationEntry.open = true;
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											EsPrint("File Manager: Unknown file type entry action '%s'.\n", STRFMT(string));
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									q++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("match"))) {
 | 
				
			||||||
 | 
								matchValue = s.value;
 | 
				
			||||||
 | 
								matchValueBytes = s.valueBytes;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!EsINIPeek(&s) || !s.keyBytes) {
 | 
							if (!EsINIPeek(&s) || !s.keyBytes) {
 | 
				
			||||||
			uintptr_t index = knownFileTypes.Length();
 | 
								EsUniqueIdentifier zeroIdentifier = {};
 | 
				
			||||||
			knownFileTypes.Add(type);
 | 
					
 | 
				
			||||||
			*knownFileTypesByExtension.Put(type.extension, type.extensionBytes) = index;
 | 
								if (EsMemoryCompare(&type.identifier, &zeroIdentifier, sizeof(EsUniqueIdentifier))) {
 | 
				
			||||||
 | 
									bool typeFound = false;
 | 
				
			||||||
 | 
									uint32_t typeIndex = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ES_MACRO_SEARCH(knownFileTypes.Length(), { 
 | 
				
			||||||
 | 
										result = EsMemoryCompare(&type.identifier, &knownFileTypes[index].identifier, sizeof(EsUniqueIdentifier));
 | 
				
			||||||
 | 
									}, typeIndex, typeFound);
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
									EsAssert(typeIndex >= KNOWN_FILE_TYPE_SPECIAL_COUNT);
 | 
				
			||||||
 | 
									FileType *existing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (!typeFound) {
 | 
				
			||||||
 | 
										existing = knownFileTypes.Insert(type, typeIndex);
 | 
				
			||||||
 | 
										EsPrint("File Manager: Type %I registered by application %d with name '%s'.\n",
 | 
				
			||||||
 | 
												type.identifier, applicationEntry.application, type.nameBytes, type.name);
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										// TODO Priority system.
 | 
				
			||||||
 | 
										existing = &knownFileTypes[typeIndex];
 | 
				
			||||||
 | 
										EsAssert(0 == EsMemoryCompare(&existing->identifier, &type.identifier, sizeof(EsUniqueIdentifier)));
 | 
				
			||||||
 | 
										if (type.nameBytes) existing->name = type.name, existing->nameBytes = type.nameBytes;
 | 
				
			||||||
 | 
										if (type.iconID) existing->iconID = type.iconID;
 | 
				
			||||||
 | 
										if (specifiedTextual) existing->textual = type.textual;
 | 
				
			||||||
 | 
										if (specifiedHasThumbnailGenerator) existing->hasThumbnailGenerator = type.hasThumbnailGenerator;
 | 
				
			||||||
 | 
										EsPrint("File Manager: Type %I updated by application %d with name '%s'.\n",
 | 
				
			||||||
 | 
												type.identifier, applicationEntry.application, type.nameBytes, type.name);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									existing->applicationEntries.Add(applicationEntry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (matchValueBytes) {
 | 
				
			||||||
 | 
										uintptr_t p = 0, q = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										while (q <= matchValueBytes) {
 | 
				
			||||||
 | 
											if (q == matchValueBytes || matchValue[q] == ',') {
 | 
				
			||||||
 | 
												String string = StringFromLiteralWithSize(&matchValue[p], q - p);
 | 
				
			||||||
 | 
												p = q + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												if (StringStartsWith(string, StringFromLiteral("ext:"))) {
 | 
				
			||||||
 | 
													String extension = StringSlice(string, 4, -1);
 | 
				
			||||||
 | 
													bool isLower = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													for (uintptr_t i = 0; i < extension.bytes; i++) {
 | 
				
			||||||
 | 
														if (EsCRTisupper(extension.text[i])) {
 | 
				
			||||||
 | 
															isLower = false;
 | 
				
			||||||
 | 
														}
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													if (extension.bytes && isLower) {
 | 
				
			||||||
 | 
														EsPrint("File Manager: Matching extension '%s' as %I.\n",
 | 
				
			||||||
 | 
																STRFMT(string), type.identifier);
 | 
				
			||||||
 | 
														*knownFileTypesByExtension.Put(STRING(extension)) = type.identifier;
 | 
				
			||||||
 | 
													} else {
 | 
				
			||||||
 | 
														EsPrint("File Manager: Invalid file type entry extension match '%s'.\n", 
 | 
				
			||||||
 | 
																STRFMT(string));
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
												} else {
 | 
				
			||||||
 | 
													EsPrint("File Manager: Unknown file type entry match '%s'.\n", STRFMT(string));
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											q++;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// The UUID is invalid, ignore the entry.
 | 
				
			||||||
 | 
									if (hasUUID) EsPrint("File Manager: Discarding file type entry with invalid UUID.\n");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			EsMemoryZero(&type, sizeof(type));
 | 
								EsMemoryZero(&type, sizeof(type));
 | 
				
			||||||
 | 
								EsMemoryZero(&applicationEntry, sizeof(applicationEntry));
 | 
				
			||||||
 | 
								specifiedTextual = false, specifiedHasThumbnailGenerator = false, hasUUID = false;
 | 
				
			||||||
 | 
								matchValueBytes = 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s = { .buffer = (char *) fileTypesBuffer.out, .bytes = fileTypesBuffer.bytes };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (EsINIParse(&s)) {
 | 
				
			||||||
 | 
							if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("application"))) {
 | 
				
			||||||
 | 
								applicationEntry.application = EsIntegerParse(s.value, s.valueBytes);
 | 
				
			||||||
 | 
							} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("opens_any_textual_file"))) {
 | 
				
			||||||
 | 
								applicationEntry.open = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!EsINIPeek(&s) || !s.keyBytes) {
 | 
				
			||||||
 | 
								if (applicationEntry.open) {
 | 
				
			||||||
 | 
									for (uintptr_t i = 0; i < knownFileTypes.Length(); i++) {
 | 
				
			||||||
 | 
										if (knownFileTypes[i].textual) {
 | 
				
			||||||
 | 
											knownFileTypes[i].applicationEntries.Insert(applicationEntry, 0);
 | 
				
			||||||
 | 
											EsPrint("File Manager: Type %I is textual, so application %d can open it.\n", 
 | 
				
			||||||
 | 
													knownFileTypes[i].identifier, applicationEntry.application);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								EsMemoryZero(&applicationEntry, sizeof(applicationEntry));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EsUniqueIdentifier FileTypeMatchByExtension(String name) {
 | 
				
			||||||
 | 
						String extension = PathGetExtension(name);
 | 
				
			||||||
 | 
						char buffer[32];
 | 
				
			||||||
 | 
						uintptr_t i = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (; i < extension.bytes && i < 32; i++) {
 | 
				
			||||||
 | 
							buffer[i] = EsCRTtolower(extension.text[i]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return knownFileTypesByExtension.Get1(buffer, i);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FileType *FolderEntryGetType(Folder *folder, FolderEntry *entry) {
 | 
					FileType *FolderEntryGetType(Folder *folder, FolderEntry *entry) {
 | 
				
			||||||
 | 
						EsUniqueIdentifier zeroIdentifier = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (entry->isFolder) {
 | 
						if (entry->isFolder) {
 | 
				
			||||||
		if (folder->itemHandler->getFileType != NamespaceDefaultGetFileType) {
 | 
							if (folder->itemHandler->getFileType != NamespaceDefaultGetFileType) {
 | 
				
			||||||
			String path = StringAllocateAndFormat("%s%s", STRFMT(folder->path), STRFMT(entry->GetInternalName()));
 | 
								String path = StringAllocateAndFormat("%s%s", STRFMT(folder->path), STRFMT(entry->GetInternalName()));
 | 
				
			||||||
			FileType *type = &knownFileTypes[folder->itemHandler->getFileType(path)];
 | 
								FileType *type = &knownFileTypes[folder->itemHandler->getFileType(path)];
 | 
				
			||||||
			StringDestroy(&path);
 | 
								StringDestroy(&path);
 | 
				
			||||||
			return type;
 | 
								return type;
 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			return &knownFileTypes[KNOWN_FILE_TYPE_DIRECTORY];
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		String extension = entry->GetExtension();
 | 
					 | 
				
			||||||
		char buffer[32];
 | 
					 | 
				
			||||||
		uintptr_t i = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (; i < extension.bytes && i < 32; i++) {
 | 
					 | 
				
			||||||
			if (EsCRTisupper(extension.text[i])) {
 | 
					 | 
				
			||||||
				buffer[i] = EsCRTtolower(extension.text[i]);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				buffer[i] = extension.text[i];
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		uintptr_t index = knownFileTypesByExtension.Get1(buffer, i);
 | 
							return &knownFileTypes[KNOWN_FILE_TYPE_DIRECTORY];
 | 
				
			||||||
		return &knownFileTypes[index ? index : KNOWN_FILE_TYPE_UNKNOWN];
 | 
					 | 
				
			||||||
	} 
 | 
						} 
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if (0 == EsMemoryCompare(&entry->contentType, &zeroIdentifier, sizeof(EsUniqueIdentifier))) {
 | 
				
			||||||
 | 
							entry->contentType = FileTypeMatchByExtension(entry->GetName());
 | 
				
			||||||
 | 
							entry->guessedContentType = true;
 | 
				
			||||||
 | 
						} 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (0 == EsMemoryCompare(&entry->contentType, &zeroIdentifier, sizeof(EsUniqueIdentifier))) {
 | 
				
			||||||
 | 
							return &knownFileTypes[KNOWN_FILE_TYPE_UNKNOWN];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool typeFound = false;
 | 
				
			||||||
 | 
						uint32_t typeIndex = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ES_MACRO_SEARCH(knownFileTypes.Length(), { 
 | 
				
			||||||
 | 
							result = EsMemoryCompare(&entry->contentType, &knownFileTypes[index].identifier, sizeof(EsUniqueIdentifier));
 | 
				
			||||||
 | 
						}, typeIndex, typeFound);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &knownFileTypes[typeFound ? typeIndex : KNOWN_FILE_TYPE_UNKNOWN];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -153,7 +153,7 @@ bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, i
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		InstanceRefreshViewType(instance, false);
 | 
							InstanceRefreshViewType(instance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Update the user interface.
 | 
							// Update the user interface.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -168,11 +168,7 @@ bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, i
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void InstanceRefreshViewType(Instance *instance, bool startTransition) {
 | 
					void InstanceRefreshViewType(Instance *instance) {
 | 
				
			||||||
	if (startTransition) {
 | 
					 | 
				
			||||||
		EsElementStartTransition(instance->list, ES_TRANSITION_FADE, ES_ELEMENT_TRANSITION_CONTENT_ONLY, 1.0f);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	EsCommandSetCheck(&instance->commandViewDetails,    instance->viewSettings.viewType == VIEW_DETAILS    ? ES_CHECK_CHECKED : ES_CHECK_UNCHECKED, false);
 | 
						EsCommandSetCheck(&instance->commandViewDetails,    instance->viewSettings.viewType == VIEW_DETAILS    ? ES_CHECK_CHECKED : ES_CHECK_UNCHECKED, false);
 | 
				
			||||||
	EsCommandSetCheck(&instance->commandViewTiles,      instance->viewSettings.viewType == VIEW_TILES      ? ES_CHECK_CHECKED : ES_CHECK_UNCHECKED, false);
 | 
						EsCommandSetCheck(&instance->commandViewTiles,      instance->viewSettings.viewType == VIEW_TILES      ? ES_CHECK_CHECKED : ES_CHECK_UNCHECKED, false);
 | 
				
			||||||
	EsCommandSetCheck(&instance->commandViewThumbnails, instance->viewSettings.viewType == VIEW_THUMBNAILS ? ES_CHECK_CHECKED : ES_CHECK_UNCHECKED, false);
 | 
						EsCommandSetCheck(&instance->commandViewThumbnails, instance->viewSettings.viewType == VIEW_THUMBNAILS ? ES_CHECK_CHECKED : ES_CHECK_UNCHECKED, false);
 | 
				
			||||||
| 
						 | 
					@ -222,7 +218,11 @@ int InstanceCompareFolderEntries(FolderEntry *left, FolderEntry *right, uint16_t
 | 
				
			||||||
		if (sortColumn == COLUMN_NAME) {
 | 
							if (sortColumn == COLUMN_NAME) {
 | 
				
			||||||
			result = EsStringCompare(STRING(left->GetName()), STRING(right->GetName()));
 | 
								result = EsStringCompare(STRING(left->GetName()), STRING(right->GetName()));
 | 
				
			||||||
		} else if (sortColumn == COLUMN_TYPE) {
 | 
							} else if (sortColumn == COLUMN_TYPE) {
 | 
				
			||||||
			result = EsStringCompare(STRING(left->GetExtension()), STRING(right->GetExtension()));
 | 
								if (!left->isFolder) {
 | 
				
			||||||
 | 
									FileType *leftType = FolderEntryGetType(nullptr, left);
 | 
				
			||||||
 | 
									FileType *rightType = FolderEntryGetType(nullptr, right);
 | 
				
			||||||
 | 
									result = EsStringCompare(leftType->name, leftType->nameBytes, rightType->name, rightType->nameBytes);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		} else if (sortColumn == COLUMN_SIZE) {
 | 
							} else if (sortColumn == COLUMN_SIZE) {
 | 
				
			||||||
			if (right->size < left->size) result = 1;
 | 
								if (right->size < left->size) result = 1;
 | 
				
			||||||
			else if (right->size > left->size) result = -1;
 | 
								else if (right->size > left->size) result = -1;
 | 
				
			||||||
| 
						 | 
					@ -385,6 +385,8 @@ void InstanceAddContents(Instance *instance, HashTable *newEntries) {
 | 
				
			||||||
		EsListViewRemove(instance->list, 0, 0, oldListEntryCount);
 | 
							EsListViewRemove(instance->list, 0, 0, oldListEntryCount);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO Optimize for each sorting mode.
 | 
				
			||||||
 | 
						// 	For example, sorting by type is pretty bad because it has to lookup file entry types to do each comparison.
 | 
				
			||||||
	InstanceSortListContents(instance->listContents.array, instance->listContents.Length(), instance->viewSettings.sortColumn);
 | 
						InstanceSortListContents(instance->listContents.array, instance->listContents.Length(), instance->viewSettings.sortColumn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (instance->listContents.Length()) {
 | 
						if (instance->listContents.Length()) {
 | 
				
			||||||
| 
						 | 
					@ -835,6 +837,8 @@ int ListCallback(EsElement *element, EsMessage *message) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (listEntry) {
 | 
							if (listEntry) {
 | 
				
			||||||
			FolderEntry *entry = listEntry->entry;
 | 
								FolderEntry *entry = listEntry->entry;
 | 
				
			||||||
 | 
								EsUniqueIdentifier applicationContentType = (EsUniqueIdentifier) {{ 0xBF, 0x88, 0xE4, 0xDD, 0x71, 0xE8, 0x5F, 0xDE, 
 | 
				
			||||||
 | 
									0x16, 0xC5, 0xAF, 0x33, 0x41, 0xC7, 0xA2, 0x96 }};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (entry->isFolder) {
 | 
								if (entry->isFolder) {
 | 
				
			||||||
				String path = instance->folder->itemHandler->getPathForChild(instance->folder, entry);
 | 
									String path = instance->folder->itemHandler->getPathForChild(instance->folder, entry);
 | 
				
			||||||
| 
						 | 
					@ -850,7 +854,7 @@ int ListCallback(EsElement *element, EsMessage *message) {
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					InstanceLoadFolder(instance, path);
 | 
										InstanceLoadFolder(instance, path);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} else if (StringEquals(entry->GetExtension(), StringFromLiteral("esx"))) {
 | 
								} else if (0 == EsMemoryCompare(&entry->contentType, &applicationContentType, sizeof(EsUniqueIdentifier))) {
 | 
				
			||||||
				// TODO Temporary.
 | 
									// TODO Temporary.
 | 
				
			||||||
				String path = StringAllocateAndFormat("%s%s", STRFMT(instance->folder->path), STRFMT(entry->GetInternalName()));
 | 
									String path = StringAllocateAndFormat("%s%s", STRFMT(instance->folder->path), STRFMT(entry->GetInternalName()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -866,11 +870,18 @@ int ListCallback(EsElement *element, EsMessage *message) {
 | 
				
			||||||
				StringDestroy(&path);
 | 
									StringDestroy(&path);
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				FileType *fileType = FolderEntryGetType(instance->folder, entry);
 | 
									FileType *fileType = FolderEntryGetType(instance->folder, entry);
 | 
				
			||||||
 | 
									FileTypeApplicationEntry *typeEntry = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (fileType->openHandler) {
 | 
									for (uintptr_t i = 0; i < fileType->applicationEntries.Length(); i++) {
 | 
				
			||||||
 | 
										if (fileType->applicationEntries[i].open) {
 | 
				
			||||||
 | 
											typeEntry = &fileType->applicationEntries[i];
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (typeEntry) {
 | 
				
			||||||
					String path = StringAllocateAndFormat("%s%s", STRFMT(instance->folder->path), STRFMT(entry->GetInternalName()));
 | 
										String path = StringAllocateAndFormat("%s%s", STRFMT(instance->folder->path), STRFMT(entry->GetInternalName()));
 | 
				
			||||||
					EsApplicationStartupRequest request = {};
 | 
										EsApplicationStartupRequest request = {};
 | 
				
			||||||
					request.id = fileType->openHandler;
 | 
										request.id = typeEntry->application;
 | 
				
			||||||
					request.filePath = path.text;
 | 
										request.filePath = path.text;
 | 
				
			||||||
					request.filePathBytes = path.bytes;
 | 
										request.filePathBytes = path.bytes;
 | 
				
			||||||
					request.flags = EsKeyboardIsCtrlHeld() || message->chooseItem.source == ES_LIST_VIEW_CHOOSE_ITEM_MIDDLE_CLICK 
 | 
										request.flags = EsKeyboardIsCtrlHeld() || message->chooseItem.source == ES_LIST_VIEW_CHOOSE_ITEM_MIDDLE_CLICK 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,10 +5,10 @@ icon=icon_applications_fonts
 | 
				
			||||||
[build]
 | 
					[build]
 | 
				
			||||||
source=apps/font_book.cpp
 | 
					source=apps/font_book.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[handler]
 | 
					[file_type]
 | 
				
			||||||
extension=ttf
 | 
					actions=open
 | 
				
			||||||
action=open
 | 
					uuid=DA-BF-EC-06-31-8A-67-B6-E3-95-BC-D1-92-D2-9A-56
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[handler]
 | 
					[file_type]
 | 
				
			||||||
extension=otf
 | 
					actions=open
 | 
				
			||||||
action=open
 | 
					uuid=E7-2B-BB-E7-FF-75-32-E0-4E-75-41-C0-D2-ED-80-56
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -736,14 +736,27 @@ int InstanceCallback(Instance *instance, EsMessage *message) {
 | 
				
			||||||
		EsBuffer buffer = { .out = _buffer, .bytes = _bufferBytes };
 | 
							EsBuffer buffer = { .out = _buffer, .bytes = _bufferBytes };
 | 
				
			||||||
		buffer.fileStore = message->instanceSave.file;
 | 
							buffer.fileStore = message->instanceSave.file;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							EsUniqueIdentifier typeJPG = (EsUniqueIdentifier) 
 | 
				
			||||||
 | 
								{{ 0xD8, 0xC2, 0x13, 0xB0, 0x53, 0x64, 0x82, 0x11, 0x48, 0x7B, 0x5B, 0x64, 0x0F, 0x92, 0xB9, 0x38 }};
 | 
				
			||||||
 | 
							EsUniqueIdentifier typeBMP = (EsUniqueIdentifier) 
 | 
				
			||||||
 | 
								{{ 0x40, 0x15, 0xB7, 0x82, 0x99, 0x6D, 0xD5, 0x41, 0x96, 0xD5, 0x3B, 0x6D, 0xA6, 0x5F, 0x07, 0x34 }};
 | 
				
			||||||
 | 
							EsUniqueIdentifier typeTGA = (EsUniqueIdentifier) 
 | 
				
			||||||
 | 
								{{ 0x69, 0x62, 0x4E, 0x28, 0xA1, 0xE1, 0x7B, 0x35, 0x64, 0x2E, 0x36, 0x01, 0x65, 0x91, 0xBE, 0xA1 }};
 | 
				
			||||||
 | 
							EsUniqueIdentifier typePNG = (EsUniqueIdentifier) 
 | 
				
			||||||
 | 
								{{ 0x59, 0x21, 0x05, 0x4D, 0x34, 0x40, 0xAB, 0x61, 0xEC, 0xF9, 0x7D, 0x5C, 0x6E, 0x04, 0x96, 0xAE }};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (0 == EsStringCompare(extension, extensionBytes, EsLiteral("jpg"))
 | 
							if (0 == EsStringCompare(extension, extensionBytes, EsLiteral("jpg"))
 | 
				
			||||||
				|| 0 == EsStringCompare(extension, extensionBytes, EsLiteral("jpeg"))) {
 | 
									|| 0 == EsStringCompare(extension, extensionBytes, EsLiteral("jpeg"))) {
 | 
				
			||||||
 | 
								EsFileStoreSetContentType(message->instanceSave.file, typeJPG);
 | 
				
			||||||
			stbi_write_jpg_to_func(WriteCallback, &buffer, width, height, 4, bits, 90);
 | 
								stbi_write_jpg_to_func(WriteCallback, &buffer, width, height, 4, bits, 90);
 | 
				
			||||||
		} else if (0 == EsStringCompare(extension, extensionBytes, EsLiteral("bmp"))) {
 | 
							} else if (0 == EsStringCompare(extension, extensionBytes, EsLiteral("bmp"))) {
 | 
				
			||||||
 | 
								EsFileStoreSetContentType(message->instanceSave.file, typeBMP);
 | 
				
			||||||
			stbi_write_bmp_to_func(WriteCallback, &buffer, width, height, 4, bits);
 | 
								stbi_write_bmp_to_func(WriteCallback, &buffer, width, height, 4, bits);
 | 
				
			||||||
		} else if (0 == EsStringCompare(extension, extensionBytes, EsLiteral("tga"))) {
 | 
							} else if (0 == EsStringCompare(extension, extensionBytes, EsLiteral("tga"))) {
 | 
				
			||||||
 | 
								EsFileStoreSetContentType(message->instanceSave.file, typeTGA);
 | 
				
			||||||
			stbi_write_tga_to_func(WriteCallback, &buffer, width, height, 4, bits);
 | 
								stbi_write_tga_to_func(WriteCallback, &buffer, width, height, 4, bits);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 | 
								EsFileStoreSetContentType(message->instanceSave.file, typePNG);
 | 
				
			||||||
			stbi_write_png_to_func(WriteCallback, &buffer, width, height, 4, bits, stride);
 | 
								stbi_write_png_to_func(WriteCallback, &buffer, width, height, 4, bits, stride);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,10 +6,18 @@ use_single_process=1
 | 
				
			||||||
[build]
 | 
					[build]
 | 
				
			||||||
source=apps/image_editor.cpp
 | 
					source=apps/image_editor.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[handler]
 | 
					[file_type]
 | 
				
			||||||
extension=jpg
 | 
					actions=open
 | 
				
			||||||
action=open
 | 
					uuid=D8-C2-13-B0-53-64-82-11-48-7B-5B-64-0F-92-B9-38
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[handler]
 | 
					[file_type]
 | 
				
			||||||
extension=png
 | 
					actions=open
 | 
				
			||||||
action=open
 | 
					uuid=59-21-05-4D-34-40-AB-61-EC-F9-7D-5C-6E-04-96-AE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[file_type]
 | 
				
			||||||
 | 
					actions=open
 | 
				
			||||||
 | 
					uuid=40-15-B7-82-99-6D-D5-41-96-D5-3B-6D-A6-5F-07-34
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[file_type]
 | 
				
			||||||
 | 
					actions=open
 | 
				
			||||||
 | 
					uuid=69-62-4E-28-A1-E1-7B-35-64-2E-36-01-65-91-BE-A1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,6 @@ hidden=1
 | 
				
			||||||
[build]
 | 
					[build]
 | 
				
			||||||
source=apps/markdown_viewer.cpp
 | 
					source=apps/markdown_viewer.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[handler]
 | 
					[file_type]
 | 
				
			||||||
extension=md
 | 
					actions=open
 | 
				
			||||||
action=open
 | 
					uuid=B1-C3-9E-56-C9-B0-85-D6-AF-98-12-1C-2E-29-8D-24
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -269,6 +269,23 @@ int InstanceCallback(Instance *instance, EsMessage *message) {
 | 
				
			||||||
		size_t byteCount;
 | 
							size_t byteCount;
 | 
				
			||||||
		char *contents = EsTextboxGetContents(instance->textboxDocument, &byteCount);
 | 
							char *contents = EsTextboxGetContents(instance->textboxDocument, &byteCount);
 | 
				
			||||||
		EsFileStoreWriteAll(message->instanceSave.file, contents, byteCount);
 | 
							EsFileStoreWriteAll(message->instanceSave.file, contents, byteCount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bool fileNameContainsExtension = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (uintptr_t i = 0; i < (uintptr_t) message->instanceSave.nameOrPathBytes && i < 5; i++) {
 | 
				
			||||||
 | 
								if (message->instanceSave.nameOrPath[message->instanceSave.nameOrPathBytes - i - 1] == '.') {
 | 
				
			||||||
 | 
									fileNameContainsExtension = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (fileNameContainsExtension) {
 | 
				
			||||||
 | 
								// Don't set a content type, it should be matched from the file extension.
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								EsUniqueIdentifier plainText = (EsUniqueIdentifier) 
 | 
				
			||||||
 | 
									{{ 0x25, 0x65, 0x4C, 0x89, 0xE7, 0x29, 0xEA, 0x9E, 0xB5, 0xBE, 0xB5, 0xCA, 0xA7, 0x60, 0xBD, 0x3D }};
 | 
				
			||||||
 | 
								EsFileStoreSetContentType(message->instanceSave.file, plainText);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		EsHeapFree(contents);
 | 
							EsHeapFree(contents);
 | 
				
			||||||
		EsInstanceSaveComplete(instance, message->instanceSave.file, true);
 | 
							EsInstanceSaveComplete(instance, message->instanceSave.file, true);
 | 
				
			||||||
	} else if (message->type == ES_MSG_INSTANCE_OPEN) {
 | 
						} else if (message->type == ES_MSG_INSTANCE_OPEN) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,25 +3,8 @@ name=Text Editor
 | 
				
			||||||
icon=icon_accessories_text_editor
 | 
					icon=icon_accessories_text_editor
 | 
				
			||||||
use_single_process=1
 | 
					use_single_process=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[file_type]
 | 
				
			||||||
 | 
					opens_any_textual_file=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[build]
 | 
					[build]
 | 
				
			||||||
source=apps/text_editor.cpp
 | 
					source=apps/text_editor.cpp
 | 
				
			||||||
 | 
					 | 
				
			||||||
[handler]
 | 
					 | 
				
			||||||
extension=txt
 | 
					 | 
				
			||||||
action=open
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[handler]
 | 
					 | 
				
			||||||
extension=cpp
 | 
					 | 
				
			||||||
action=open
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[handler]
 | 
					 | 
				
			||||||
extension=h
 | 
					 | 
				
			||||||
action=open
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[handler]
 | 
					 | 
				
			||||||
extension=c
 | 
					 | 
				
			||||||
action=open
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[handler]
 | 
					 | 
				
			||||||
extension=ini
 | 
					 | 
				
			||||||
action=open
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1132,6 +1132,10 @@ EsMessage *EsMessageReceive() {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if (type == ES_MSG_INSTANCE_OPEN_DELAYED) {
 | 
							} else if (type == ES_MSG_INSTANCE_OPEN_DELAYED) {
 | 
				
			||||||
			InstanceSendOpenMessage((EsInstance *) message.message._argument, false);
 | 
								InstanceSendOpenMessage((EsInstance *) message.message._argument, false);
 | 
				
			||||||
 | 
							} else if (type == ES_MSG_INSTANCE_SAVE_COMPLETE_DELAYED) {
 | 
				
			||||||
 | 
								char buffer[1];
 | 
				
			||||||
 | 
								buffer[0] = DESKTOP_MSG_COMPLETE_SAVE;
 | 
				
			||||||
 | 
								MessageDesktop(buffer, 1, ((EsInstance *) message.message._argument)->window->handle);
 | 
				
			||||||
		} else if (type == ES_MSG_PRIMARY_CLIPBOARD_UPDATED) {
 | 
							} else if (type == ES_MSG_PRIMARY_CLIPBOARD_UPDATED) {
 | 
				
			||||||
			EsInstance *instance = InstanceFromWindowID(message.message.tabOperation.id);
 | 
								EsInstance *instance = InstanceFromWindowID(message.message.tabOperation.id);
 | 
				
			||||||
			if (instance) UIRefreshPrimaryClipboard(instance->window);
 | 
								if (instance) UIRefreshPrimaryClipboard(instance->window);
 | 
				
			||||||
| 
						 | 
					@ -1237,9 +1241,9 @@ void EsInstanceSaveComplete(EsInstance *instance, EsFileStore *file, bool succes
 | 
				
			||||||
	APIInstance *apiInstance = (APIInstance *) instance->_private;
 | 
						APIInstance *apiInstance = (APIInstance *) instance->_private;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (instance) {
 | 
						if (instance) {
 | 
				
			||||||
		char buffer[1];
 | 
							// HACK Post this message so that our handle to the file is (hopefully) closed first.
 | 
				
			||||||
		buffer[0] = DESKTOP_MSG_COMPLETE_SAVE;
 | 
							EsMessage m = { .type = ES_MSG_INSTANCE_SAVE_COMPLETE_DELAYED, ._argument = instance };
 | 
				
			||||||
		MessageDesktop(buffer, 1, instance->window->handle);
 | 
							EsMessagePost(nullptr, &m); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (success) {
 | 
							if (success) {
 | 
				
			||||||
			const char *name;
 | 
								const char *name;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,6 +98,7 @@ struct OpenDocument {
 | 
				
			||||||
	size_t pathBytes;
 | 
						size_t pathBytes;
 | 
				
			||||||
	char *temporarySavePath;
 | 
						char *temporarySavePath;
 | 
				
			||||||
	size_t temporarySavePathBytes;
 | 
						size_t temporarySavePathBytes;
 | 
				
			||||||
 | 
						EsUniqueIdentifier temporarySavePreviousContentType; // The previous content type. Used as the default if the save operation does not set a new content type. This is useful for text/hex editors, where there is no intrinsic content type.
 | 
				
			||||||
	EsHandle readHandle;
 | 
						EsHandle readHandle;
 | 
				
			||||||
	EsObjectID id;
 | 
						EsObjectID id;
 | 
				
			||||||
	EsObjectID currentWriter;
 | 
						EsObjectID currentWriter;
 | 
				
			||||||
| 
						 | 
					@ -2312,6 +2313,11 @@ void ApplicationInstanceRequestSave(ApplicationInstance *instance, const char *n
 | 
				
			||||||
		EsHeapFree(document->temporarySavePath);
 | 
							EsHeapFree(document->temporarySavePath);
 | 
				
			||||||
		document->temporarySavePath = nullptr;
 | 
							document->temporarySavePath = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (ES_SUCCESS != EsFileControl(document->readHandle, ES_FILE_CONTROL_GET_CONTENT_TYPE, 
 | 
				
			||||||
 | 
										&document->temporarySavePreviousContentType, sizeof(EsUniqueIdentifier))) {
 | 
				
			||||||
 | 
								document->temporarySavePreviousContentType = {};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		EsHandle fileHandle;
 | 
							EsHandle fileHandle;
 | 
				
			||||||
		m.tabOperation.error = TemporaryFileCreate(&fileHandle, &document->temporarySavePath, &document->temporarySavePathBytes, ES_FILE_WRITE);
 | 
							m.tabOperation.error = TemporaryFileCreate(&fileHandle, &document->temporarySavePath, &document->temporarySavePathBytes, ES_FILE_WRITE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2410,6 +2416,22 @@ void ApplicationInstanceCompleteSave(ApplicationInstance *fromInstance) {
 | 
				
			||||||
		document->readHandle = file.handle;
 | 
							document->readHandle = file.handle;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EsUniqueIdentifier newContentType = {}, zeroContentType = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ES_SUCCESS == EsFileControl(document->readHandle, ES_FILE_CONTROL_GET_CONTENT_TYPE, &newContentType, sizeof(EsUniqueIdentifier))) {
 | 
				
			||||||
 | 
							if (0 == EsMemoryCompare(&newContentType, &zeroContentType, sizeof(EsUniqueIdentifier))
 | 
				
			||||||
 | 
									&& EsMemoryCompare(&document->temporarySavePreviousContentType, &zeroContentType, sizeof(EsUniqueIdentifier))) {
 | 
				
			||||||
 | 
								// The application did not set a content type, so use the previous content type of the file.
 | 
				
			||||||
 | 
								EsFileInformation write = EsFileOpen(document->path, document->pathBytes, ES_FILE_WRITE_SHARED | ES_NODE_FAIL_IF_NOT_FOUND);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (write.error == ES_SUCCESS) {
 | 
				
			||||||
 | 
									EsFileControl(write.handle, ES_FILE_CONTROL_SET_CONTENT_TYPE, 
 | 
				
			||||||
 | 
											&document->temporarySavePreviousContentType, sizeof(EsUniqueIdentifier));
 | 
				
			||||||
 | 
									EsHandleClose(write.handle);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	document->currentWriter = 0;
 | 
						document->currentWriter = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (desktop.fileManager && desktop.fileManager->singleProcess) {
 | 
						if (desktop.fileManager && desktop.fileManager->singleProcess) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -95,7 +95,7 @@ EsError EsPathCreate(const char *path, ptrdiff_t pathBytes, EsNodeType type, boo
 | 
				
			||||||
	return ES_SUCCESS;
 | 
						return ES_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EsError EsFileControl(EsHandle file, EsFileControlOperation operation, const void *data, size_t dataBytes) {
 | 
					EsError EsFileControl(EsHandle file, EsFileControlOperation operation, void *data, size_t dataBytes) {
 | 
				
			||||||
	return EsSyscall(ES_SYSCALL_FILE_CONTROL, file, operation, (uintptr_t) data, dataBytes);
 | 
						return EsSyscall(ES_SYSCALL_FILE_CONTROL, file, operation, (uintptr_t) data, dataBytes);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -278,6 +278,21 @@ void *EsFileStoreMap(EsFileStore *file, size_t *fileSize, uint32_t flags) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EsFileStoreSetContentType(EsFileStore *file, EsUniqueIdentifier identifier) {
 | 
				
			||||||
 | 
						if (file->type == FILE_STORE_HANDLE) {
 | 
				
			||||||
 | 
							EsFileControl(file->handle, ES_FILE_CONTROL_SET_CONTENT_TYPE, &identifier, sizeof(identifier));
 | 
				
			||||||
 | 
						} else if (file->type == FILE_STORE_PATH) {
 | 
				
			||||||
 | 
							EsFileInformation information = EsFileOpen(file->path, file->pathBytes, ES_FILE_WRITE | ES_NODE_CREATE_DIRECTORIES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (information.error == ES_SUCCESS) {
 | 
				
			||||||
 | 
								EsFileControl(file->handle, ES_FILE_CONTROL_SET_CONTENT_TYPE, &identifier, sizeof(identifier));
 | 
				
			||||||
 | 
								EsHandleClose(information.handle);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// The file store backend doesn't support this operation.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EsError MountPointAdd(const char *prefix, size_t prefixBytes, EsHandle base, bool addedByApplication) {
 | 
					EsError MountPointAdd(const char *prefix, size_t prefixBytes, EsHandle base, bool addedByApplication) {
 | 
				
			||||||
	EsMutexAcquire(&api.mountPointsMutex);
 | 
						EsMutexAcquire(&api.mountPointsMutex);
 | 
				
			||||||
	bool duplicate = NodeFindMountPoint(prefix, prefixBytes, nullptr, true);
 | 
						bool duplicate = NodeFindMountPoint(prefix, prefixBytes, nullptr, true);
 | 
				
			||||||
| 
						 | 
					@ -545,7 +560,7 @@ EsError EsFileWriteAllGather(const char *filePath, ptrdiff_t filePathLength, con
 | 
				
			||||||
		filePathLength = EsCStringLength(filePath);
 | 
							filePathLength = EsCStringLength(filePath);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EsFileInformation information = EsFileOpen((char *) filePath, filePathLength, ES_FILE_WRITE | ES_NODE_CREATE_DIRECTORIES);
 | 
						EsFileInformation information = EsFileOpen(filePath, filePathLength, ES_FILE_WRITE | ES_NODE_CREATE_DIRECTORIES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ES_SUCCESS != information.error) {
 | 
						if (ES_SUCCESS != information.error) {
 | 
				
			||||||
		return information.error;
 | 
							return information.error;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -763,6 +763,7 @@ inttype EsConnectionOpenFlags uint32_t none {
 | 
				
			||||||
inttype EsFileControlOperation uint32_t none {
 | 
					inttype EsFileControlOperation uint32_t none {
 | 
				
			||||||
	ES_FILE_CONTROL_FLUSH = 0 // data/dataBytes ignored
 | 
						ES_FILE_CONTROL_FLUSH = 0 // data/dataBytes ignored
 | 
				
			||||||
	ES_FILE_CONTROL_SET_CONTENT_TYPE = 1 // data EsUniqueIdentifier; dataBytes ignored
 | 
						ES_FILE_CONTROL_SET_CONTENT_TYPE = 1 // data EsUniqueIdentifier; dataBytes ignored
 | 
				
			||||||
 | 
						ES_FILE_CONTROL_GET_CONTENT_TYPE = 2 // data EsUniqueIdentifier; dataBytes ignored
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inttype EsElementUpdateContentFlags uint32_t none {
 | 
					inttype EsElementUpdateContentFlags uint32_t none {
 | 
				
			||||||
| 
						 | 
					@ -2142,7 +2143,7 @@ function EsError EsFileWriteAllGatherFromHandle(EsHandle handle, const void **da
 | 
				
			||||||
function void *EsFileMap(STRING filePath, size_t *fileSize, uint32_t flags) @native();
 | 
					function void *EsFileMap(STRING filePath, size_t *fileSize, uint32_t flags) @native();
 | 
				
			||||||
function EsError EsFileCopy(STRING source, STRING destination, void **copyBuffer = ES_NULL, EsFileCopyCallback callback = ES_NULL, EsGeneric data = ES_NULL) @todo(); // If you are copying lots of files, you can reuse the temporary copy buffer by storing the output copyBuffer; call EsHeapFree on it after the last copy.
 | 
					function EsError EsFileCopy(STRING source, STRING destination, void **copyBuffer = ES_NULL, EsFileCopyCallback callback = ES_NULL, EsGeneric data = ES_NULL) @todo(); // If you are copying lots of files, you can reuse the temporary copy buffer by storing the output copyBuffer; call EsHeapFree on it after the last copy.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function EsError EsFileControl(EsHandle file, EsFileControlOperation operation, const void *data, size_t dataBytes) @buffer_in(data, dataBytes); 
 | 
					function EsError EsFileControl(EsHandle file, EsFileControlOperation operation, void *data, size_t dataBytes) @buffer_out(data, dataBytes); // TODO This annotation is incorrect, it might be in or out depending on the operation.
 | 
				
			||||||
function EsFileInformation EsFileOpen(STRING path, EsFileOpenFlags flags);
 | 
					function EsFileInformation EsFileOpen(STRING path, EsFileOpenFlags flags);
 | 
				
			||||||
function EsFileOffset EsFileGetSize(EsHandle handle);
 | 
					function EsFileOffset EsFileGetSize(EsHandle handle);
 | 
				
			||||||
function size_t EsFileReadSync(EsHandle file, EsFileOffset offset, size_t size, void *buffer) @buffer_out(buffer, size);
 | 
					function size_t EsFileReadSync(EsHandle file, EsFileOffset offset, size_t size, void *buffer) @buffer_out(buffer, size);
 | 
				
			||||||
| 
						 | 
					@ -2162,6 +2163,7 @@ function bool EsFileStoreWriteAll(EsFileStore *file, const void *data, size_t da
 | 
				
			||||||
function bool EsFileStoreAppend(EsFileStore *file, const void *data, size_t dataBytes) @buffer_in(data, dataBytes);
 | 
					function bool EsFileStoreAppend(EsFileStore *file, const void *data, size_t dataBytes) @buffer_in(data, dataBytes);
 | 
				
			||||||
function EsFileOffsetDifference EsFileStoreGetSize(EsFileStore *file); // Returns -1 on error.
 | 
					function EsFileOffsetDifference EsFileStoreGetSize(EsFileStore *file); // Returns -1 on error.
 | 
				
			||||||
function void *EsFileStoreMap(EsFileStore *file, size_t *fileSize, uint32_t flags) @native();
 | 
					function void *EsFileStoreMap(EsFileStore *file, size_t *fileSize, uint32_t flags) @native();
 | 
				
			||||||
 | 
					function void EsFileStoreSetContentType(EsFileStore *file, EsUniqueIdentifier identifier);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// These calls require permission_all_files.
 | 
					// These calls require permission_all_files.
 | 
				
			||||||
function bool EsMountPointGetVolumeInformation(STRING prefix, EsVolumeInformation *information) @out(information); // Returns false if the mount point does not exist.
 | 
					function bool EsMountPointGetVolumeInformation(STRING prefix, EsVolumeInformation *information) @out(information); // Returns false if the mount point does not exist.
 | 
				
			||||||
| 
						 | 
					@ -2394,6 +2396,7 @@ function bool EsUTF8IsValid(STRING input); // Does not check for surrogate chara
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function double EsDoubleParse(STRING string, char **endptr) @native(); 
 | 
					function double EsDoubleParse(STRING string, char **endptr) @native(); 
 | 
				
			||||||
function int64_t EsIntegerParse(STRING text); // Parses in hexadecimal if the first two characters are '0x'.
 | 
					function int64_t EsIntegerParse(STRING text); // Parses in hexadecimal if the first two characters are '0x'.
 | 
				
			||||||
 | 
					function EsUniqueIdentifier EsUniqueIdentifierParse(STRING text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CRT functions.
 | 
					// CRT functions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -425,6 +425,7 @@ extern "C" void *EsBufferWrite(EsBuffer *buffer, const void *source, size_t writ
 | 
				
			||||||
#define ES_MSG_PING				((EsMessageType) (ES_MSG_SYSTEM_START + 0x203)) /* Sent by Desktop to check processes are processing messages. */
 | 
					#define ES_MSG_PING				((EsMessageType) (ES_MSG_SYSTEM_START + 0x203)) /* Sent by Desktop to check processes are processing messages. */
 | 
				
			||||||
#define ES_MSG_WAKEUP				((EsMessageType) (ES_MSG_SYSTEM_START + 0x204)) /* Sent to wakeup the message thread, so that it can process locally posted messages. */
 | 
					#define ES_MSG_WAKEUP				((EsMessageType) (ES_MSG_SYSTEM_START + 0x204)) /* Sent to wakeup the message thread, so that it can process locally posted messages. */
 | 
				
			||||||
#define ES_MSG_INSTANCE_OPEN_DELAYED		((EsMessageType) (ES_MSG_SYSTEM_START + 0x205))
 | 
					#define ES_MSG_INSTANCE_OPEN_DELAYED		((EsMessageType) (ES_MSG_SYSTEM_START + 0x205))
 | 
				
			||||||
 | 
					#define ES_MSG_INSTANCE_SAVE_COMPLETE_DELAYED   ((EsMessageType) (ES_MSG_SYSTEM_START + 0x206))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,7 +87,7 @@ struct TextPiece {
 | 
				
			||||||
	uintptr_t glyphOffset;
 | 
						uintptr_t glyphOffset;
 | 
				
			||||||
	size_t glyphCount;
 | 
						size_t glyphCount;
 | 
				
			||||||
	uintptr_t start, end;
 | 
						uintptr_t start, end;
 | 
				
			||||||
	bool isTabPiece;
 | 
						bool isTabPiece, isEllipsisPiece;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct TextLine {
 | 
					struct TextLine {
 | 
				
			||||||
| 
						 | 
					@ -1829,6 +1829,7 @@ void TextAddEllipsis(EsTextPlan *plan, int32_t maximumLineWidth, bool needFinalE
 | 
				
			||||||
		piece.glyphOffset = plan->glyphInfos.Length();
 | 
							piece.glyphOffset = plan->glyphInfos.Length();
 | 
				
			||||||
		piece.ascent  =  FontGetAscent (&plan->font) + plan->currentTextStyle->baselineOffset, 
 | 
							piece.ascent  =  FontGetAscent (&plan->font) + plan->currentTextStyle->baselineOffset, 
 | 
				
			||||||
		piece.descent = -FontGetDescent(&plan->font) - plan->currentTextStyle->baselineOffset;
 | 
							piece.descent = -FontGetDescent(&plan->font) - plan->currentTextStyle->baselineOffset;
 | 
				
			||||||
 | 
							piece.isEllipsisPiece = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (uintptr_t i = 0; i < glyphCount; i++) {
 | 
							for (uintptr_t i = 0; i < glyphCount; i++) {
 | 
				
			||||||
			if (!plan->glyphInfos.Add(glyphInfos[i])) break;
 | 
								if (!plan->glyphInfos.Add(glyphInfos[i])) break;
 | 
				
			||||||
| 
						 | 
					@ -2295,7 +2296,7 @@ void DrawTextPiece(EsPainter *painter, EsTextPlan *plan, TextPiece *piece, TextL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Draw the selection background.
 | 
						// Draw the selection background.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (selection->caret0 != selection->caret1 && !selection->hideCaret) {
 | 
						if (selection->caret0 != selection->caret1 && !selection->hideCaret && !piece->isEllipsisPiece) {
 | 
				
			||||||
		int sCursorX = cursorX, selectionStartX = -1, selectionEndX = -1;
 | 
							int sCursorX = cursorX, selectionStartX = -1, selectionEndX = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (uintptr_t i = 0; i < piece->glyphCount; i++) {
 | 
							for (uintptr_t i = 0; i < piece->glyphCount; i++) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,15 +97,13 @@ In the `[embed]` section there is a list of files that should be embedded into t
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Each `[file_type]` section provides information about a file type.
 | 
					Each `[file_type]` section provides information about a file type.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `extension` Gives the file name extension for the file type.
 | 
					- `match` Gives the file name extensions to match for the file type.
 | 
				
			||||||
- `name` Gives the readable name of the file type, which will be shown to the user. TODO Translations.
 | 
					- `name` Gives the readable name of the file type, which will be shown to the user. TODO Translations.
 | 
				
			||||||
- `icon` Gives the name of the icon from `desktop/icons.header` to show for files of this type. TODO Bundled icons.
 | 
					- `icon` Gives the name of the icon from `desktop/icons.header` to show for files of this type. TODO Bundled icons.
 | 
				
			||||||
- `has_thumbnail_generator` Set to 1 if the file type has a thumbnail generator. Only images are supported at the moment. TODO Custom thumbnail generators.
 | 
					- `has_thumbnail_generator` Set to 1 if the file type has a thumbnail generator. Only images are supported at the moment. TODO Custom thumbnail generators.
 | 
				
			||||||
 | 
					- `uuid` The EsUniqueIdentifier of the file type.
 | 
				
			||||||
Each `[handler]` section describes this application's support to manage files of a given file type.
 | 
					- `textual` Set to 1 if the file type can be read by plain text editors.
 | 
				
			||||||
 | 
					- `actions` The actions the application supports with this file type, e.g. `open`.
 | 
				
			||||||
- `extension` The file name extension of the file type.
 | 
					 | 
				
			||||||
- `action` The action that this application support for the file type. Currently only "open" is supported.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Standard build configuration options
 | 
					### Standard build configuration options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,119 @@
 | 
				
			||||||
 | 
					These are the EsUniqueIdentifier values for different file content types.
 | 
				
			||||||
 | 
					TODO Write up how the content type system works.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ELF executable
 | 
				
			||||||
 | 
					{ 0xAB, 0xDE, 0x98, 0xB5, 0x56, 0x2C, 0x04, 0xDF, 0x1E, 0x43, 0xC8, 0x10, 0x24, 0x63, 0xDB, 0xB8 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					esx application
 | 
				
			||||||
 | 
					{ 0xBF, 0x88, 0xE4, 0xDD, 0x71, 0xE8, 0x5F, 0xDE, 0x16, 0xC5, 0xAF, 0x33, 0x41, 0xC7, 0xA2, 0x96 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					plain text files 
 | 
				
			||||||
 | 
					{ 0x25, 0x65, 0x4C, 0x89, 0xE7, 0x29, 0xEA, 0x9E, 0xB5, 0xBE, 0xB5, 0xCA, 0xA7, 0x60, 0xBD, 0x3D }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bochs config file
 | 
				
			||||||
 | 
					{ 0xFF, 0x92, 0xF0, 0x53, 0x28, 0x83, 0xDC, 0xF1, 0xBA, 0xDD, 0xDE, 0x79, 0x92, 0x33, 0x3F, 0x73 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					build core file
 | 
				
			||||||
 | 
					{ 0x6F, 0x2A, 0x23, 0x16, 0xAF, 0xA3, 0xD0, 0xFB, 0x95, 0x06, 0x52, 0xB3, 0x67, 0xFB, 0x37, 0xFA }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					theme designer source file
 | 
				
			||||||
 | 
					{ 0x26, 0x4A, 0xE0, 0x6C, 0x0F, 0xE8, 0x2C, 0xE7, 0xF4, 0x6E, 0x4E, 0x76, 0x5B, 0x4A, 0xCF, 0x4F }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uxn rom
 | 
				
			||||||
 | 
					{ 0x76, 0xC2, 0xC1, 0x73, 0xB1, 0x20, 0x3D, 0x42, 0x70, 0xFD, 0xD4, 0xBD, 0x66, 0xE9, 0x2A, 0x39 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static library .a
 | 
				
			||||||
 | 
					{ 0x7D, 0x39, 0xBF, 0x18, 0x9E, 0x07, 0xBC, 0xD3, 0x4F, 0x9F, 0x87, 0xEC, 0x6F, 0x70, 0x65, 0x5D }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libtool archive .la
 | 
				
			||||||
 | 
					{ 0x8F, 0x10, 0x4F, 0x33, 0x4A, 0xE5, 0x2D, 0xA7, 0x2A, 0x80, 0x2C, 0x4F, 0xEA, 0xE4, 0x99, 0xB1 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					compiled object .o
 | 
				
			||||||
 | 
					{ 0xB5, 0x19, 0xF2, 0x0D, 0xAD, 0x4F, 0xD4, 0xC9, 0xA4, 0xBF, 0x97, 0xE4, 0x5E, 0x4C, 0x55, 0x00 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					c source file
 | 
				
			||||||
 | 
					{ 0x36, 0x02, 0xD3, 0xAC, 0x6C, 0xDE, 0x8D, 0x31, 0xFA, 0x70, 0xB2, 0xDA, 0xFA, 0x81, 0x53, 0x69 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					c++ source file
 | 
				
			||||||
 | 
					{ 0x35, 0x84, 0xA6, 0x0E, 0x52, 0x28, 0xBA, 0xAB, 0xCA, 0x74, 0x14, 0xF4, 0xE1, 0x15, 0xCA, 0x5F }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					c header file
 | 
				
			||||||
 | 
					{ 0xD1, 0x16, 0xC4, 0xC1, 0x7B, 0xC0, 0xED, 0x4F, 0xCA, 0xAC, 0x18, 0x05, 0x32, 0x1D, 0x56, 0x32 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					html file
 | 
				
			||||||
 | 
					{ 0x4A, 0x5A, 0x1C, 0xE5, 0xEE, 0x08, 0x6E, 0x04, 0x13, 0x9C, 0x6D, 0x05, 0x48, 0x5C, 0xE5, 0xD2 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					source template
 | 
				
			||||||
 | 
					{ 0x76, 0x35, 0xF3, 0xF5, 0xC2, 0x69, 0x8A, 0xA4, 0xE4, 0x68, 0x5F, 0xE5, 0x2C, 0x73, 0x10, 0xF9 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					linker script
 | 
				
			||||||
 | 
					{ 0x23, 0x56, 0xBC, 0xD4, 0x5A, 0xD6, 0x56, 0x08, 0x06, 0x61, 0x7C, 0x33, 0x38, 0x66, 0xD5, 0x1F }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					markdown
 | 
				
			||||||
 | 
					{ 0xB1, 0xC3, 0x9E, 0x56, 0xC9, 0xB0, 0x85, 0xD6, 0xAF, 0x98, 0x12, 0x1C, 0x2E, 0x29, 0x8D, 0x24 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					UNIX package configuration
 | 
				
			||||||
 | 
					{ 0x74, 0x72, 0x01, 0x17, 0x97, 0x4B, 0x6B, 0x4B, 0x74, 0xCD, 0x18, 0x7C, 0x8F, 0x3C, 0x58, 0xBC }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					python script
 | 
				
			||||||
 | 
					{ 0x77, 0x19, 0xBA, 0x87, 0x2E, 0xDB, 0x51, 0xA4, 0x71, 0x5D, 0xFF, 0x8D, 0x39, 0x86, 0x96, 0xF0 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					UNIX manual file
 | 
				
			||||||
 | 
					{ 0xDB, 0x72, 0xDD, 0x5D, 0x92, 0x54, 0x09, 0x1A, 0xC4, 0x16, 0xDD, 0x89, 0x0B, 0x13, 0x12, 0x1F }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					application configuration file
 | 
				
			||||||
 | 
					{ 0x18, 0x8D, 0xD3, 0xAC, 0x35, 0xF5, 0xD3, 0x01, 0x8C, 0x66, 0xFD, 0x12, 0xE1, 0xED, 0xE2, 0x6F }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INI configuration file
 | 
				
			||||||
 | 
					{ 0x81, 0x72, 0x43, 0x29, 0xE5, 0x37, 0x89, 0x51, 0x8E, 0x22, 0xA0, 0x67, 0xA5, 0xB9, 0x42, 0x2C }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gzip archive
 | 
				
			||||||
 | 
					{ 0x7D, 0x93, 0x66, 0x74, 0x80, 0x0B, 0x64, 0x9F, 0x16, 0x5D, 0x44, 0xA5, 0x45, 0xFF, 0x80, 0xBF }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					virtual drive image
 | 
				
			||||||
 | 
					{ 0xE1, 0x61, 0x4D, 0x0D, 0x32, 0xEC, 0xE0, 0x0F, 0x36, 0x7F, 0xE9, 0x7B, 0x3F, 0x16, 0x43, 0x02 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jpeg image
 | 
				
			||||||
 | 
					{ 0xD8, 0xC2, 0x13, 0xB0, 0x53, 0x64, 0x82, 0x11, 0x48, 0x7B, 0x5B, 0x64, 0x0F, 0x92, 0xB9, 0x38 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					matroska video
 | 
				
			||||||
 | 
					{ 0x0D, 0xCA, 0x26, 0x92, 0x22, 0x44, 0xEB, 0x6B, 0xC6, 0xE9, 0x6C, 0x8E, 0xFC, 0x28, 0xD2, 0x8E }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3d object .obj
 | 
				
			||||||
 | 
					{ 0xF7, 0x81, 0xE9, 0x21, 0xDF, 0x89, 0x77, 0xD0, 0xC6, 0x97, 0xA8, 0x63, 0xF1, 0xF3, 0x4F, 0x63 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					png image
 | 
				
			||||||
 | 
					{ 0x59, 0x21, 0x05, 0x4D, 0x34, 0x40, 0xAB, 0x61, 0xEC, 0xF9, 0x7D, 0x5C, 0x6E, 0x04, 0x96, 0xAE }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tga image
 | 
				
			||||||
 | 
					{ 0x69, 0x62, 0x4E, 0x28, 0xA1, 0xE1, 0x7B, 0x35, 0x64, 0x2E, 0x36, 0x01, 0x65, 0x91, 0xBE, 0xA1 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bmp image
 | 
				
			||||||
 | 
					{ 0x40, 0x15, 0xB7, 0x82, 0x99, 0x6D, 0xD5, 0x41, 0x96, 0xD5, 0x3B, 0x6D, 0xA6, 0x5F, 0x07, 0x34 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					truetype font
 | 
				
			||||||
 | 
					{ 0xDA, 0xBF, 0xEC, 0x06, 0x31, 0x8A, 0x67, 0xB6, 0xE3, 0x95, 0xBC, 0xD1, 0x92, 0xD2, 0x9A, 0x56 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					opentype font
 | 
				
			||||||
 | 
					{ 0xE7, 0x2B, 0xBB, 0xE7, 0xFF, 0x75, 0x32, 0xE0, 0x4E, 0x75, 0x41, 0xC0, 0xD2, 0xED, 0x80, 0x56 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					application data
 | 
				
			||||||
 | 
					{ 0xB5, 0x32, 0x22, 0x14, 0x01, 0xCB, 0xD0, 0x1D, 0xA2, 0x55, 0x08, 0xC7, 0x0D, 0x46, 0x86, 0x53 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bash script
 | 
				
			||||||
 | 
					{ 0x66, 0x42, 0x88, 0xF9, 0xD1, 0x68, 0x47, 0xBF, 0x7F, 0x48, 0x82, 0x77, 0x2B, 0xE3, 0x39, 0xBA }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					makefile
 | 
				
			||||||
 | 
					{ 0x3A, 0x58, 0x08, 0x24, 0x00, 0x75, 0xB5, 0x8B, 0xA8, 0x39, 0xD8, 0x37, 0x03, 0x6B, 0x20, 0x85 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					kernel module
 | 
				
			||||||
 | 
					{ 0x80, 0x6D, 0x8E, 0xD6, 0xC7, 0x45, 0xAD, 0xA7, 0x03, 0x5B, 0xB3, 0x64, 0xC5, 0x0A, 0xEE, 0xC6 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ogg audio
 | 
				
			||||||
 | 
					{ 0xE5, 0x14, 0x14, 0xED, 0x4D, 0x67, 0xC0, 0xD0, 0x62, 0xA1, 0xA4, 0xD4, 0xF5, 0x82, 0xA7, 0x88 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mp4 video
 | 
				
			||||||
 | 
					{ 0x02, 0x76, 0xE1, 0x41, 0xB8, 0x54, 0x19, 0xA9, 0x05, 0x8D, 0xC1, 0x3F, 0x6E, 0x8F, 0xD7, 0x9E }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wav audio
 | 
				
			||||||
 | 
					{ 0x87, 0xBC, 0xA1, 0xA1, 0x25, 0x76, 0x26, 0x5C, 0x8F, 0x94, 0xD6, 0xD8, 0x35, 0xB6, 0x7A, 0x9F }
 | 
				
			||||||
| 
						 | 
					@ -65,7 +65,7 @@ EsError FSNodeOpenHandle(KNode *node, uint32_t flags, uint8_t mode);
 | 
				
			||||||
void FSNodeCloseHandle(KNode *node, uint32_t flags);
 | 
					void FSNodeCloseHandle(KNode *node, uint32_t flags);
 | 
				
			||||||
EsError FSNodeDelete(KNode *node);
 | 
					EsError FSNodeDelete(KNode *node);
 | 
				
			||||||
EsError FSNodeMove(KNode *node, KNode *destination, const char *newName, size_t nameNameBytes);
 | 
					EsError FSNodeMove(KNode *node, KNode *destination, const char *newName, size_t nameNameBytes);
 | 
				
			||||||
EsError FSFileResize(KNode *node, EsFileOffset newSizeBytes);
 | 
					EsError FSFileResize(KNode *node, EsFileOffset newSizeBytes, bool growOnly = false);
 | 
				
			||||||
ptrdiff_t FSDirectoryEnumerate(KNode *node, K_USER_BUFFER EsDirectoryChild *buffer, size_t bufferSize);
 | 
					ptrdiff_t FSDirectoryEnumerate(KNode *node, K_USER_BUFFER EsDirectoryChild *buffer, size_t bufferSize);
 | 
				
			||||||
EsError FSFileControlFlush(KNode *node);
 | 
					EsError FSFileControlFlush(KNode *node);
 | 
				
			||||||
EsError FSFileControlSetContentType(KNode *node, EsUniqueIdentifier identifier);
 | 
					EsError FSFileControlSetContentType(KNode *node, EsUniqueIdentifier identifier);
 | 
				
			||||||
| 
						 | 
					@ -136,6 +136,7 @@ bool FSCheckPathForIllegalCharacters(const char *path, size_t pathBytes) {
 | 
				
			||||||
EsError FSReadIntoCache(CCSpace *fileCache, void *buffer, EsFileOffset offset, EsFileOffset count) {
 | 
					EsError FSReadIntoCache(CCSpace *fileCache, void *buffer, EsFileOffset offset, EsFileOffset count) {
 | 
				
			||||||
	FSFile *node = EsContainerOf(FSFile, cache, fileCache);
 | 
						FSFile *node = EsContainerOf(FSFile, cache, fileCache);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// KWriterLockAssertLocked(&node->resizeLock);
 | 
				
			||||||
	KWriterLockTake(&node->writerLock, K_LOCK_SHARED);
 | 
						KWriterLockTake(&node->writerLock, K_LOCK_SHARED);
 | 
				
			||||||
	EsDefer(KWriterLockReturn(&node->writerLock, K_LOCK_SHARED));
 | 
						EsDefer(KWriterLockReturn(&node->writerLock, K_LOCK_SHARED));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -238,6 +239,7 @@ EsError FSFileCreateAndResizeOnFileSystem(FSFile *node, EsFileOffset fileSize) {
 | 
				
			||||||
EsError FSWriteFromCache(CCSpace *fileCache, const void *buffer, EsFileOffset offset, EsFileOffset count) {
 | 
					EsError FSWriteFromCache(CCSpace *fileCache, const void *buffer, EsFileOffset offset, EsFileOffset count) {
 | 
				
			||||||
	FSFile *node = EsContainerOf(FSFile, cache, fileCache);
 | 
						FSFile *node = EsContainerOf(FSFile, cache, fileCache);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// KWriterLockAssertLocked(&node->resizeLock);
 | 
				
			||||||
	KWriterLockTake(&node->writerLock, K_LOCK_EXCLUSIVE);
 | 
						KWriterLockTake(&node->writerLock, K_LOCK_EXCLUSIVE);
 | 
				
			||||||
	EsDefer(KWriterLockReturn(&node->writerLock, K_LOCK_EXCLUSIVE));
 | 
						EsDefer(KWriterLockReturn(&node->writerLock, K_LOCK_EXCLUSIVE));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -335,7 +337,7 @@ void _FSFileResize(FSFile *file, EsFileOffset newSize) {
 | 
				
			||||||
	KMutexRelease(&file->fileSystem->moveMutex);
 | 
						KMutexRelease(&file->fileSystem->moveMutex);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EsError FSFileResize(KNode *node, EsFileOffset newSize) {
 | 
					EsError FSFileResize(KNode *node, EsFileOffset newSize, bool growOnly) {
 | 
				
			||||||
	if (fs.shutdown) KernelPanic("FSFileResize - Attempting to resize a file after FSShutdown called.\n");
 | 
						if (fs.shutdown) KernelPanic("FSFileResize - Attempting to resize a file after FSShutdown called.\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (newSize > (EsFileOffset) 1 << 60) {
 | 
						if (newSize > (EsFileOffset) 1 << 60) {
 | 
				
			||||||
| 
						 | 
					@ -350,7 +352,9 @@ EsError FSFileResize(KNode *node, EsFileOffset newSize) {
 | 
				
			||||||
	EsError error = ES_SUCCESS;
 | 
						EsError error = ES_SUCCESS;
 | 
				
			||||||
	KWriterLockTake(&file->resizeLock, K_LOCK_EXCLUSIVE);
 | 
						KWriterLockTake(&file->resizeLock, K_LOCK_EXCLUSIVE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (file->blockResize) {
 | 
						if (growOnly && newSize <= file->directoryEntry->totalSize) {
 | 
				
			||||||
 | 
							// Nothing to do.
 | 
				
			||||||
 | 
						} else if (file->blockResize) {
 | 
				
			||||||
		error = ES_ERROR_OPERATION_BLOCKED;
 | 
							error = ES_ERROR_OPERATION_BLOCKED;
 | 
				
			||||||
	} else if (!file->fileSystem->resize) {
 | 
						} else if (!file->fileSystem->resize) {
 | 
				
			||||||
		error = ES_ERROR_FILE_ON_READ_ONLY_VOLUME;
 | 
							error = ES_ERROR_FILE_ON_READ_ONLY_VOLUME;
 | 
				
			||||||
| 
						 | 
					@ -365,10 +369,8 @@ EsError FSFileResize(KNode *node, EsFileOffset newSize) {
 | 
				
			||||||
ptrdiff_t FSFileWriteSync(KNode *node, K_USER_BUFFER const void *buffer, EsFileOffset offset, EsFileOffset bytes, uint32_t flags) {
 | 
					ptrdiff_t FSFileWriteSync(KNode *node, K_USER_BUFFER const void *buffer, EsFileOffset offset, EsFileOffset bytes, uint32_t flags) {
 | 
				
			||||||
	if (fs.shutdown) KernelPanic("FSFileWriteSync - Attempting to write to a file after FSShutdown called.\n");
 | 
						if (fs.shutdown) KernelPanic("FSFileWriteSync - Attempting to write to a file after FSShutdown called.\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (offset + bytes > node->directoryEntry->totalSize) {
 | 
						if (ES_SUCCESS != FSFileResize(node, offset + bytes, true /* growOnly */)) {
 | 
				
			||||||
		if (ES_SUCCESS != FSFileResize(node, offset + bytes)) {
 | 
							return ES_ERROR_COULD_NOT_RESIZE_FILE;
 | 
				
			||||||
			return ES_ERROR_COULD_NOT_RESIZE_FILE;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	FSFile *file = (FSFile *) node;
 | 
						FSFile *file = (FSFile *) node;
 | 
				
			||||||
| 
						 | 
					@ -426,8 +428,10 @@ EsError FSFileControlSetContentType(KNode *node, EsUniqueIdentifier identifier)
 | 
				
			||||||
	} else if (~node->fileSystem->flags & ES_VOLUME_STORES_CONTENT_TYPE) {
 | 
						} else if (~node->fileSystem->flags & ES_VOLUME_STORES_CONTENT_TYPE) {
 | 
				
			||||||
		return ES_ERROR_UNSUPPORTED;
 | 
							return ES_ERROR_UNSUPPORTED;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
 | 
							KWriterLockTake(&node->writerLock, K_LOCK_EXCLUSIVE);
 | 
				
			||||||
		node->directoryEntry->contentType = identifier;
 | 
							node->directoryEntry->contentType = identifier;
 | 
				
			||||||
		__sync_fetch_and_or(&node->flags, NODE_MODIFIED); // Set the modified flag *after* making the modification.
 | 
							__sync_fetch_and_or(&node->flags, NODE_MODIFIED); // Set the modified flag *after* making the modification.
 | 
				
			||||||
 | 
							KWriterLockReturn(&node->writerLock, K_LOCK_EXCLUSIVE);
 | 
				
			||||||
		return ES_SUCCESS;
 | 
							return ES_SUCCESS;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1015,9 +1019,11 @@ void FSNodeSynchronize(KNode *node) {
 | 
				
			||||||
	if (node->directoryEntry->type == ES_NODE_FILE) {
 | 
						if (node->directoryEntry->type == ES_NODE_FILE) {
 | 
				
			||||||
		FSFile *file = (FSFile *) node;
 | 
							FSFile *file = (FSFile *) node;
 | 
				
			||||||
		CCSpaceFlush(&file->cache);
 | 
							CCSpaceFlush(&file->cache);
 | 
				
			||||||
		KWriterLockTake(&node->writerLock, K_LOCK_EXCLUSIVE);
 | 
							KWriterLockTake(&file->resizeLock, K_LOCK_EXCLUSIVE);
 | 
				
			||||||
 | 
							KWriterLockTake(&file->writerLock, K_LOCK_EXCLUSIVE);
 | 
				
			||||||
		FSFileCreateAndResizeOnFileSystem(file, file->directoryEntry->totalSize);
 | 
							FSFileCreateAndResizeOnFileSystem(file, file->directoryEntry->totalSize);
 | 
				
			||||||
		KWriterLockReturn(&node->writerLock, K_LOCK_EXCLUSIVE);
 | 
							KWriterLockReturn(&file->writerLock, K_LOCK_EXCLUSIVE);
 | 
				
			||||||
 | 
							KWriterLockReturn(&file->resizeLock, K_LOCK_EXCLUSIVE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (node->flags & NODE_MODIFIED) {
 | 
						if (node->flags & NODE_MODIFIED) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -697,10 +697,15 @@ bool MMHandlePageFault(MMSpace *space, uintptr_t address, unsigned faultFlags) {
 | 
				
			||||||
			pagesToRead = region->pageCount - (offsetIntoRegion + region->data.file.zeroedBytes) / K_PAGE_SIZE;
 | 
								pagesToRead = region->pageCount - (offsetIntoRegion + region->data.file.zeroedBytes) / K_PAGE_SIZE;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// This shouldn't block, because mapped files cannot be resized.
 | 
				
			||||||
 | 
							KWriterLockTake(®ion->data.file.node->resizeLock, K_LOCK_SHARED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		EsError error = CCSpaceAccess(®ion->data.file.node->cache, (void *) address, 
 | 
							EsError error = CCSpaceAccess(®ion->data.file.node->cache, (void *) address, 
 | 
				
			||||||
				offsetIntoRegion + region->data.file.offset, pagesToRead * K_PAGE_SIZE, 
 | 
									offsetIntoRegion + region->data.file.offset, pagesToRead * K_PAGE_SIZE, 
 | 
				
			||||||
				CC_ACCESS_MAP, space, flags);
 | 
									CC_ACCESS_MAP, space, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							KWriterLockReturn(®ion->data.file.node->resizeLock, K_LOCK_SHARED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		KMutexAcquire(®ion->data.mapMutex);
 | 
							KMutexAcquire(®ion->data.mapMutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (error != ES_SUCCESS) {
 | 
							if (error != ES_SUCCESS) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1193,18 +1193,38 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_FILE_CONTROL) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (file->directoryEntry->type != ES_NODE_FILE) {
 | 
						if (file->directoryEntry->type != ES_NODE_FILE) {
 | 
				
			||||||
		SYSCALL_RETURN(ES_FATAL_ERROR_INCORRECT_NODE_TYPE, true);
 | 
							SYSCALL_RETURN(ES_FATAL_ERROR_INCORRECT_NODE_TYPE, true);
 | 
				
			||||||
	} else if ((handle.flags & (ES_FILE_WRITE_SHARED | ES_FILE_WRITE)) == 0) {
 | 
					 | 
				
			||||||
		SYSCALL_RETURN(ES_FATAL_ERROR_INCORRECT_FILE_ACCESS, true);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EsError error = ES_ERROR_UNSUPPORTED;
 | 
						EsError error = ES_ERROR_UNSUPPORTED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (argument1 == ES_FILE_CONTROL_FLUSH) {
 | 
						if (argument1 == ES_FILE_CONTROL_FLUSH) {
 | 
				
			||||||
 | 
							if ((handle.flags & (ES_FILE_WRITE_SHARED | ES_FILE_WRITE)) == 0) {
 | 
				
			||||||
 | 
								SYSCALL_RETURN(ES_FATAL_ERROR_INCORRECT_FILE_ACCESS, true);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		error = FSFileControlFlush(file);
 | 
							error = FSFileControlFlush(file);
 | 
				
			||||||
	} else if (argument1 == ES_FILE_CONTROL_SET_CONTENT_TYPE) {
 | 
						} else if (argument1 == ES_FILE_CONTROL_SET_CONTENT_TYPE) {
 | 
				
			||||||
 | 
							if ((handle.flags & (ES_FILE_WRITE_SHARED | ES_FILE_WRITE)) == 0) {
 | 
				
			||||||
 | 
								SYSCALL_RETURN(ES_FATAL_ERROR_INCORRECT_FILE_ACCESS, true);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		EsUniqueIdentifier identifier;
 | 
							EsUniqueIdentifier identifier;
 | 
				
			||||||
		SYSCALL_READ(&identifier, argument2, sizeof(identifier));
 | 
							SYSCALL_READ(&identifier, argument2, sizeof(identifier));
 | 
				
			||||||
		error = FSFileControlSetContentType(file, identifier);
 | 
							error = FSFileControlSetContentType(file, identifier);
 | 
				
			||||||
 | 
						} else if (argument1 == ES_FILE_CONTROL_GET_CONTENT_TYPE) {
 | 
				
			||||||
 | 
							EsUniqueIdentifier identifier;
 | 
				
			||||||
 | 
							KWriterLockTake(&file->writerLock, K_LOCK_SHARED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (file->fileSystem->flags & ES_VOLUME_STORES_CONTENT_TYPE) {
 | 
				
			||||||
 | 
								identifier = file->directoryEntry->contentType;
 | 
				
			||||||
 | 
								error = ES_SUCCESS;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							KWriterLockReturn(&file->writerLock, K_LOCK_SHARED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (error == ES_SUCCESS) {
 | 
				
			||||||
 | 
								SYSCALL_WRITE(argument2, &identifier, sizeof(EsUniqueIdentifier));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SYSCALL_RETURN(error, false);
 | 
						SYSCALL_RETURN(error, false);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,10 +9,9 @@ custom_compile_command=cp root/Applications/POSIX/bin/bochs bin/Bochs
 | 
				
			||||||
require=root/Applications/POSIX/bin/bochs
 | 
					require=root/Applications/POSIX/bin/bochs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=bochsrc
 | 
					match=ext:bochsrc
 | 
				
			||||||
name=Bochs configuration
 | 
					name=Bochs configuration
 | 
				
			||||||
icon=icon_applications_development
 | 
					icon=icon_applications_development
 | 
				
			||||||
 | 
					uuid=FF-92-F0-53-28-83-DC-F1-BA-DD-DE-79-92-33-3F-73
 | 
				
			||||||
[handler]
 | 
					actions=open
 | 
				
			||||||
extension=bochsrc
 | 
					textual=1
 | 
				
			||||||
action=open
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,11 +9,10 @@ link_flags=-lOSMesa -lstdc++ -lz
 | 
				
			||||||
with_cstdlib=1
 | 
					with_cstdlib=1
 | 
				
			||||||
source=ports/mesa/obj_viewer.c
 | 
					source=ports/mesa/obj_viewer.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[handler]
 | 
					 | 
				
			||||||
extension=obj
 | 
					 | 
				
			||||||
action=open
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=obj
 | 
					match=ext:obj
 | 
				
			||||||
name=3D model
 | 
					name=3D model
 | 
				
			||||||
icon=icon_model
 | 
					icon=icon_model
 | 
				
			||||||
 | 
					uuid=F7-81-E9-21-DF-89-77-D0-C6-97-A8-63-F1-F3-4F-63
 | 
				
			||||||
 | 
					actions=open
 | 
				
			||||||
 | 
					textual=1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,11 +6,9 @@ hidden=1
 | 
				
			||||||
source=ports/uxn/emulator.c
 | 
					source=ports/uxn/emulator.c
 | 
				
			||||||
compile_flags=-Wno-unknown-pragmas -Wno-unused-parameter
 | 
					compile_flags=-Wno-unknown-pragmas -Wno-unused-parameter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[handler]
 | 
					 | 
				
			||||||
extension=uxn
 | 
					 | 
				
			||||||
action=open
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=uxn
 | 
					match=ext:uxn
 | 
				
			||||||
name=Uxn ROM
 | 
					name=Uxn ROM
 | 
				
			||||||
icon=icon_unknown
 | 
					icon=icon_unknown
 | 
				
			||||||
 | 
					uuid=76-C2-C1-73-B1-20-3D-42-70-FD-D4-BD-66-E9-2A-39
 | 
				
			||||||
 | 
					actions=open
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -589,6 +589,16 @@ void _StringFormat(FormatCallback callback, void *callbackData, const char *form
 | 
				
			||||||
					callback('}', callbackData);
 | 
										callback('}', callbackData);
 | 
				
			||||||
				} break;
 | 
									} break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									case 'I': {
 | 
				
			||||||
 | 
										EsUniqueIdentifier value = va_arg(arguments, EsUniqueIdentifier);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										for (uintptr_t i = 0; i < 16; i++) {
 | 
				
			||||||
 | 
											if (i) callback('-', callbackData);
 | 
				
			||||||
 | 
											callback(hexChars[(value.d[i] & 0xF0) >> 4], callbackData);
 | 
				
			||||||
 | 
											callback(hexChars[(value.d[i] & 0xF)], callbackData);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				case 'X': {
 | 
									case 'X': {
 | 
				
			||||||
					uintptr_t value = va_arg(arguments, uintptr_t);
 | 
										uintptr_t value = va_arg(arguments, uintptr_t);
 | 
				
			||||||
					callback(hexChars[(value & 0xF0) >> 4], callbackData);
 | 
										callback(hexChars[(value & 0xF0) >> 4], callbackData);
 | 
				
			||||||
| 
						 | 
					@ -920,6 +930,7 @@ int64_t EsStringParseInteger(const char **string, size_t *length, int base) {
 | 
				
			||||||
int EsStringCompareRaw(const char *s1, ptrdiff_t length1, const char *s2, ptrdiff_t length2) {
 | 
					int EsStringCompareRaw(const char *s1, ptrdiff_t length1, const char *s2, ptrdiff_t length2) {
 | 
				
			||||||
	if (length1 == -1) length1 = EsCStringLength(s1);
 | 
						if (length1 == -1) length1 = EsCStringLength(s1);
 | 
				
			||||||
	if (length2 == -1) length2 = EsCStringLength(s2);
 | 
						if (length2 == -1) length2 = EsCStringLength(s2);
 | 
				
			||||||
 | 
						if (s1 == s2 && length1 == length2) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (length1 || length2) {
 | 
						while (length1 || length2) {
 | 
				
			||||||
		if (!length1) return -1;
 | 
							if (!length1) return -1;
 | 
				
			||||||
| 
						 | 
					@ -945,6 +956,7 @@ int EsStringCompare(const char *s1, ptrdiff_t _length1, const char *s2, ptrdiff_
 | 
				
			||||||
	if (_length1 == -1) _length1 = EsCStringLength(s1);
 | 
						if (_length1 == -1) _length1 = EsCStringLength(s1);
 | 
				
			||||||
	if (_length2 == -1) _length2 = EsCStringLength(s2);
 | 
						if (_length2 == -1) _length2 = EsCStringLength(s2);
 | 
				
			||||||
	size_t length1 = _length1, length2 = _length2;
 | 
						size_t length1 = _length1, length2 = _length2;
 | 
				
			||||||
 | 
						if (s1 == s2 && length1 == length2) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (length1 || length2) {
 | 
						while (length1 || length2) {
 | 
				
			||||||
		if (!length1) return -1;
 | 
							if (!length1) return -1;
 | 
				
			||||||
| 
						 | 
					@ -1072,6 +1084,28 @@ static int64_t ConvertCharacterToDigit(int character, int base) {
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EsUniqueIdentifier EsUniqueIdentifierParse(const char *text, ptrdiff_t bytes) {
 | 
				
			||||||
 | 
						if (bytes == -1) bytes = EsCStringLength(text);
 | 
				
			||||||
 | 
						if (bytes != 3 * 16 - 1) return {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (uintptr_t i = 0; i < 15; i++) {
 | 
				
			||||||
 | 
							if (text[i * 3 + 2] != '-') {
 | 
				
			||||||
 | 
								return {};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EsUniqueIdentifier identifier;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (uintptr_t i = 0; i < 16; i++) {
 | 
				
			||||||
 | 
							int64_t a = ConvertCharacterToDigit(text[i * 3 + 0], 16);
 | 
				
			||||||
 | 
							int64_t b = ConvertCharacterToDigit(text[i * 3 + 1], 16);
 | 
				
			||||||
 | 
							if (a == -1 || b == -1) return {};
 | 
				
			||||||
 | 
							identifier.d[i] = a * 16 + b;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return identifier;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int64_t EsIntegerParse(const char *text, ptrdiff_t bytes) {
 | 
					int64_t EsIntegerParse(const char *text, ptrdiff_t bytes) {
 | 
				
			||||||
	if (bytes == -1) bytes = EsCStringLength(text);
 | 
						if (bytes == -1) bytes = EsCStringLength(text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -694,12 +694,13 @@ void PrintTree(uint64_t block, int indent = 2, uint64_t lowerThan = -1) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void NewDirectoryEntry(DirectoryEntry *entry, uint8_t nodeType, EsUniqueIdentifier parentUID, const char *name) {
 | 
					void NewDirectoryEntry(DirectoryEntry *entry, uint8_t nodeType, EsUniqueIdentifier parentUID, const char *name, EsUniqueIdentifier contentType) {
 | 
				
			||||||
	memcpy(entry->signature, ESFS_DIRECTORY_ENTRY_SIGNATURE, 8);
 | 
						memcpy(entry->signature, ESFS_DIRECTORY_ENTRY_SIGNATURE, 8);
 | 
				
			||||||
	GenerateUniqueIdentifier(&entry->identifier, false);
 | 
						GenerateUniqueIdentifier(&entry->identifier, false);
 | 
				
			||||||
	entry->attributeOffset = ESFS_ATTRIBUTE_OFFSET;
 | 
						entry->attributeOffset = ESFS_ATTRIBUTE_OFFSET;
 | 
				
			||||||
	entry->nodeType = nodeType; 
 | 
						entry->nodeType = nodeType; 
 | 
				
			||||||
	entry->parent = parentUID;
 | 
						entry->parent = parentUID;
 | 
				
			||||||
 | 
						entry->contentType = contentType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uint8_t *position = (uint8_t *) entry + entry->attributeOffset;
 | 
						uint8_t *position = (uint8_t *) entry + entry->attributeOffset;
 | 
				
			||||||
	size_t newFilenameSize = ((strlen(name) + ESFS_FILENAME_HEADER_SIZE - 1) & ~7) + 8; // Size of name + size of header, rounded up to the nearest 8 bytes.
 | 
						size_t newFilenameSize = ((strlen(name) + ESFS_FILENAME_HEADER_SIZE - 1) & ~7) + 8; // Size of name + size of header, rounded up to the nearest 8 bytes.
 | 
				
			||||||
| 
						 | 
					@ -736,7 +737,7 @@ void NewDirectoryEntry(DirectoryEntry *entry, uint8_t nodeType, EsUniqueIdentifi
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, DirectoryEntryReference *outputReference, 
 | 
					bool AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, DirectoryEntryReference *outputReference, 
 | 
				
			||||||
		DirectoryEntryReference directoryReference) {
 | 
							DirectoryEntryReference directoryReference, EsUniqueIdentifier contentType) {
 | 
				
			||||||
	// Log("add %s to %s\n", name, path);
 | 
						// Log("add %s to %s\n", name, path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Step 1: Resize the directory so that it can fit another directory entry.
 | 
						// Step 1: Resize the directory so that it can fit another directory entry.
 | 
				
			||||||
| 
						 | 
					@ -770,7 +771,7 @@ bool AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di
 | 
				
			||||||
	DirectoryEntry entry = {};
 | 
						DirectoryEntry entry = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		NewDirectoryEntry(&entry, nodeType, directory.identifier, name);
 | 
							NewDirectoryEntry(&entry, nodeType, directory.identifier, name, contentType);
 | 
				
			||||||
		// Log("\tchild nodes: %ld\n", directoryAttribute->childNodes);
 | 
							// Log("\tchild nodes: %ld\n", directoryAttribute->childNodes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!AccessNode(&directory, &entry, (directoryAttribute->childNodes - 1) * sizeof(DirectoryEntry), sizeof(DirectoryEntry), &reference, false)) {
 | 
							if (!AccessNode(&directory, &entry, (directoryAttribute->childNodes - 1) * sizeof(DirectoryEntry), sizeof(DirectoryEntry), &reference, false)) {
 | 
				
			||||||
| 
						 | 
					@ -1041,6 +1042,7 @@ typedef struct ImportNode {
 | 
				
			||||||
	const char *name, *path;
 | 
						const char *name, *path;
 | 
				
			||||||
	struct ImportNode *children;
 | 
						struct ImportNode *children;
 | 
				
			||||||
	bool isFile;
 | 
						bool isFile;
 | 
				
			||||||
 | 
						EsUniqueIdentifier contentType;
 | 
				
			||||||
} ImportNode;
 | 
					} ImportNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int64_t Import(ImportNode node, DirectoryEntryReference parentDirectory) {
 | 
					int64_t Import(ImportNode node, DirectoryEntryReference parentDirectory) {
 | 
				
			||||||
| 
						 | 
					@ -1059,7 +1061,7 @@ int64_t Import(ImportNode node, DirectoryEntryReference parentDirectory) {
 | 
				
			||||||
				DirectoryEntryReference reference;
 | 
									DirectoryEntryReference reference;
 | 
				
			||||||
				DirectoryEntry entry;
 | 
									DirectoryEntry entry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (!AddNode(node.children[i].name, ESFS_NODE_TYPE_FILE, &entry, &reference, parentDirectory)) {
 | 
									if (!AddNode(node.children[i].name, ESFS_NODE_TYPE_FILE, &entry, &reference, parentDirectory, node.children[i].contentType)) {
 | 
				
			||||||
					return -1;
 | 
										return -1;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1081,7 +1083,7 @@ int64_t Import(ImportNode node, DirectoryEntryReference parentDirectory) {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			DirectoryEntryReference reference;
 | 
								DirectoryEntryReference reference;
 | 
				
			||||||
			if (!AddNode(node.children[i].name, ESFS_NODE_TYPE_DIRECTORY, NULL, &reference, parentDirectory)) return -1;
 | 
								if (!AddNode(node.children[i].name, ESFS_NODE_TYPE_DIRECTORY, NULL, &reference, parentDirectory, node.children[i].contentType)) return -1;
 | 
				
			||||||
			int64_t size = Import(node.children[i], reference);
 | 
								int64_t size = Import(node.children[i], reference);
 | 
				
			||||||
			if (size == -1) return -1;
 | 
								if (size == -1) return -1;
 | 
				
			||||||
			DirectoryEntry directory; 
 | 
								DirectoryEntry directory; 
 | 
				
			||||||
| 
						 | 
					@ -1241,7 +1243,8 @@ bool Format(uint64_t driveSize, const char *volumeName, EsUniqueIdentifier osIns
 | 
				
			||||||
		DirectoryEntry entry;
 | 
							DirectoryEntry entry;
 | 
				
			||||||
		EsUniqueIdentifier unused = {};
 | 
							EsUniqueIdentifier unused = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		NewDirectoryEntry(&entry, ESFS_NODE_TYPE_FILE, unused, "Kernel");
 | 
							EsUniqueIdentifier elf = (EsUniqueIdentifier) {{ 0xAB, 0xDE, 0x98, 0xB5, 0x56, 0x2C, 0x04, 0xDF, 0x1E, 0x43, 0xC8, 0x10, 0x24, 0x63, 0xDB, 0xB8 }};
 | 
				
			||||||
 | 
							NewDirectoryEntry(&entry, ESFS_NODE_TYPE_FILE, unused, "Kernel", elf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (WriteDirectoryEntryReference(reference, &entry)) {
 | 
							if (WriteDirectoryEntryReference(reference, &entry)) {
 | 
				
			||||||
			if (ResizeNode(&entry, kernelBytes)) {
 | 
								if (ResizeNode(&entry, kernelBytes)) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								start.sh
								
								
								
								
							
							
						
						
									
										2
									
								
								start.sh
								
								
								
								
							| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
cd "$(dirname "$0")" && mkdir -p bin && gcc -o bin/script util/script.c -g -Wall -Wextra -O2 -pthread -ldl && bin/script util/start.script options="`echo $@`"
 | 
					cd "$(dirname "$0")" && mkdir -p bin && gcc -o bin/script util/script.c -g -Wall -Wextra -O2 -pthread && bin/script util/start.script options="`echo $@`"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,7 @@ EsCommandSetCheck=35
 | 
				
			||||||
EsTextboxGetContentsAsDouble=36
 | 
					EsTextboxGetContentsAsDouble=36
 | 
				
			||||||
EsFileStoreAppend=37
 | 
					EsFileStoreAppend=37
 | 
				
			||||||
EsBufferFlushToFileStore=38
 | 
					EsBufferFlushToFileStore=38
 | 
				
			||||||
 | 
					EsFileStoreSetContentType=39
 | 
				
			||||||
EsPathExists=40
 | 
					EsPathExists=40
 | 
				
			||||||
EsInstanceSetClassViewer=41
 | 
					EsInstanceSetClassViewer=41
 | 
				
			||||||
EsPathDelete=42
 | 
					EsPathDelete=42
 | 
				
			||||||
| 
						 | 
					@ -494,3 +495,4 @@ EsRectangleContainsAll=493
 | 
				
			||||||
EsListViewFixedItemSetEnumStringsForColumn=494
 | 
					EsListViewFixedItemSetEnumStringsForColumn=494
 | 
				
			||||||
EsImageDisplayGetImageHeight=495
 | 
					EsImageDisplayGetImageHeight=495
 | 
				
			||||||
EsDirectoryEnumerate=496
 | 
					EsDirectoryEnumerate=496
 | 
				
			||||||
 | 
					EsUniqueIdentifierParse=497
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -406,6 +406,91 @@ bool FileExists(const char *path) {
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef OS_ESSENCE
 | 
				
			||||||
 | 
					EsUniqueIdentifier MatchFileContentType(const char *pathBuffer) {
 | 
				
			||||||
 | 
						FILE *f = fopen(pathBuffer, "rb");
 | 
				
			||||||
 | 
						assert(f);
 | 
				
			||||||
 | 
						uint32_t elfSignature;
 | 
				
			||||||
 | 
						fread(&elfSignature, 1, sizeof(elfSignature), f);
 | 
				
			||||||
 | 
						bool isELFExecutable = elfSignature == 0x464C457F;
 | 
				
			||||||
 | 
						fclose(f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EsUniqueIdentifier t = { 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ENDS_WITH(s) (strlen(pathBuffer) >= strlen(s) && 0 == strcmp(pathBuffer + strlen(pathBuffer) - strlen(s), s))
 | 
				
			||||||
 | 
					#define STARTS_WITH(s) (strlen(pathBuffer) >= strlen(s) && 0 == memcmp(pathBuffer, s, strlen(s)))
 | 
				
			||||||
 | 
						if (ENDS_WITH(".bochsrc"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xFF, 0x92, 0xF0, 0x53, 0x28, 0x83, 0xDC, 0xF1, 0xBA, 0xDD, 0xDE, 0x79, 0x92, 0x33, 0x3F, 0x73 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".build_core"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x6F, 0x2A, 0x23, 0x16, 0xAF, 0xA3, 0xD0, 0xFB, 0x95, 0x06, 0x52, 0xB3, 0x67, 0xFB, 0x37, 0xFA }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".designer"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x26, 0x4A, 0xE0, 0x6C, 0x0F, 0xE8, 0x2C, 0xE7, 0xF4, 0x6E, 0x4E, 0x76, 0x5B, 0x4A, 0xCF, 0x4F }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".uxn"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x76, 0xC2, 0xC1, 0x73, 0xB1, 0x20, 0x3D, 0x42, 0x70, 0xFD, 0xD4, 0xBD, 0x66, 0xE9, 0x2A, 0x39 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".a"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x7D, 0x39, 0xBF, 0x18, 0x9E, 0x07, 0xBC, 0xD3, 0x4F, 0x9F, 0x87, 0xEC, 0x6F, 0x70, 0x65, 0x5D }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".la"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x8F, 0x10, 0x4F, 0x33, 0x4A, 0xE5, 0x2D, 0xA7, 0x2A, 0x80, 0x2C, 0x4F, 0xEA, 0xE4, 0x99, 0xB1 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".esx"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xBF, 0x88, 0xE4, 0xDD, 0x71, 0xE8, 0x5F, 0xDE, 0x16, 0xC5, 0xAF, 0x33, 0x41, 0xC7, 0xA2, 0x96 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".o"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xB5, 0x19, 0xF2, 0x0D, 0xAD, 0x4F, 0xD4, 0xC9, 0xA4, 0xBF, 0x97, 0xE4, 0x5E, 0x4C, 0x55, 0x00 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".c"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x36, 0x02, 0xD3, 0xAC, 0x6C, 0xDE, 0x8D, 0x31, 0xFA, 0x70, 0xB2, 0xDA, 0xFA, 0x81, 0x53, 0x69 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".cpp"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x35, 0x84, 0xA6, 0x0E, 0x52, 0x28, 0xBA, 0xAB, 0xCA, 0x74, 0x14, 0xF4, 0xE1, 0x15, 0xCA, 0x5F }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".h"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xD1, 0x16, 0xC4, 0xC1, 0x7B, 0xC0, 0xED, 0x4F, 0xCA, 0xAC, 0x18, 0x05, 0x32, 0x1D, 0x56, 0x32 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".html"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x4A, 0x5A, 0x1C, 0xE5, 0xEE, 0x08, 0x6E, 0x04, 0x13, 0x9C, 0x6D, 0x05, 0x48, 0x5C, 0xE5, 0xD2 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".in"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x76, 0x35, 0xF3, 0xF5, 0xC2, 0x69, 0x8A, 0xA4, 0xE4, 0x68, 0x5F, 0xE5, 0x2C, 0x73, 0x10, 0xF9 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH("/Makefile"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x3A, 0x58, 0x08, 0x24, 0x00, 0x75, 0xB5, 0x8B, 0xA8, 0x39, 0xD8, 0x37, 0x03, 0x6B, 0x20, 0x85 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".ld") || STARTS_WITH("root/Applications/POSIX/x86_64-essence/lib/ldscripts/"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x23, 0x56, 0xBC, 0xD4, 0x5A, 0xD6, 0x56, 0x08, 0x06, 0x61, 0x7C, 0x33, 0x38, 0x66, 0xD5, 0x1F }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".md"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xB1, 0xC3, 0x9E, 0x56, 0xC9, 0xB0, 0x85, 0xD6, 0xAF, 0x98, 0x12, 0x1C, 0x2E, 0x29, 0x8D, 0x24 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".pc"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x74, 0x72, 0x01, 0x17, 0x97, 0x4B, 0x6B, 0x4B, 0x74, 0xCD, 0x18, 0x7C, 0x8F, 0x3C, 0x58, 0xBC }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".py"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x77, 0x19, 0xBA, 0x87, 0x2E, 0xDB, 0x51, 0xA4, 0x71, 0x5D, 0xFF, 0x8D, 0x39, 0x86, 0x96, 0xF0 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".sh"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x66, 0x42, 0x88, 0xF9, 0xD1, 0x68, 0x47, 0xBF, 0x7F, 0x48, 0x82, 0x77, 0x2B, 0xE3, 0x39, 0xBA }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".txt") || ENDS_WITH("/README") || ENDS_WITH("/LICENSE") || ENDS_WITH("/COPYING") || ENDS_WITH("/AUTHORS") || ENDS_WITH("/TODO")
 | 
				
			||||||
 | 
							|| ENDS_WITH("/BUGS") || ENDS_WITH("/NEWS") || ENDS_WITH("/CHANGES") || ENDS_WITH("/ReadMe") || ENDS_WITH(".LESSER"))
 | 
				
			||||||
 | 
								t = (EsUniqueIdentifier) {{ 0x25, 0x65, 0x4C, 0x89, 0xE7, 0x29, 0xEA, 0x9E, 0xB5, 0xBE, 0xB5, 0xCA, 0xA7, 0x60, 0xBD, 0x3D }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".1") || ENDS_WITH(".7") || ENDS_WITH(".info") || ENDS_WITH(".info-1") || ENDS_WITH(".info-2"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xDB, 0x72, 0xDD, 0x5D, 0x92, 0x54, 0x09, 0x1A, 0xC4, 0x16, 0xDD, 0x89, 0x0B, 0x13, 0x12, 0x1F }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".conf") || ENDS_WITH(".bfd"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x18, 0x8D, 0xD3, 0xAC, 0x35, 0xF5, 0xD3, 0x01, 0x8C, 0x66, 0xFD, 0x12, 0xE1, 0xED, 0xE2, 0x6F }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".ini"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x81, 0x72, 0x43, 0x29, 0xE5, 0x37, 0x89, 0x51, 0x8E, 0x22, 0xA0, 0x67, 0xA5, 0xB9, 0x42, 0x2C }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".gz"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x7D, 0x93, 0x66, 0x74, 0x80, 0x0B, 0x64, 0x9F, 0x16, 0x5D, 0x44, 0xA5, 0x45, 0xFF, 0x80, 0xBF }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".img"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xE1, 0x61, 0x4D, 0x0D, 0x32, 0xEC, 0xE0, 0x0F, 0x36, 0x7F, 0xE9, 0x7B, 0x3F, 0x16, 0x43, 0x02 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".jpg"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xD8, 0xC2, 0x13, 0xB0, 0x53, 0x64, 0x82, 0x11, 0x48, 0x7B, 0x5B, 0x64, 0x0F, 0x92, 0xB9, 0x38 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".mkv"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x0D, 0xCA, 0x26, 0x92, 0x22, 0x44, 0xEB, 0x6B, 0xC6, 0xE9, 0x6C, 0x8E, 0xFC, 0x28, 0xD2, 0x8E }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".obj"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xF7, 0x81, 0xE9, 0x21, 0xDF, 0x89, 0x77, 0xD0, 0xC6, 0x97, 0xA8, 0x63, 0xF1, 0xF3, 0x4F, 0x63 }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".png"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0x59, 0x21, 0x05, 0x4D, 0x34, 0x40, 0xAB, 0x61, 0xEC, 0xF9, 0x7D, 0x5C, 0x6E, 0x04, 0x96, 0xAE }};
 | 
				
			||||||
 | 
						else if (ENDS_WITH(".ttf"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xDA, 0xBF, 0xEC, 0x06, 0x31, 0x8A, 0x67, 0xB6, 0xE3, 0x95, 0xBC, 0xD1, 0x92, 0xD2, 0x9A, 0x56 }};
 | 
				
			||||||
 | 
						else if (STARTS_WITH("root/Applications/POSIX/share/bochs/"))
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xB5, 0x32, 0x22, 0x14, 0x01, 0xCB, 0xD0, 0x1D, 0xA2, 0x55, 0x08, 0xC7, 0x0D, 0x46, 0x86, 0x53 }};
 | 
				
			||||||
 | 
						else if (isELFExecutable)
 | 
				
			||||||
 | 
							t = (EsUniqueIdentifier) {{ 0xAB, 0xDE, 0x98, 0xB5, 0x56, 0x2C, 0x04, 0xDF, 0x1E, 0x43, 0xC8, 0x10, 0x24, 0x63, 0xDB, 0xB8 }};
 | 
				
			||||||
 | 
					#undef ENDS_WITH
 | 
				
			||||||
 | 
					#undef STARTS_WITH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return t;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void CreateImportNode(const char *path, ImportNode *node) {
 | 
					void CreateImportNode(const char *path, ImportNode *node) {
 | 
				
			||||||
	char pathBuffer[256];
 | 
						char pathBuffer[256];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -429,6 +514,7 @@ void CreateImportNode(const char *path, ImportNode *node) {
 | 
				
			||||||
			CreateImportNode(pathBuffer, &child);
 | 
								CreateImportNode(pathBuffer, &child);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			child.isFile = true;
 | 
								child.isFile = true;
 | 
				
			||||||
 | 
								child.contentType = children[i].contentType;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		child.name = EsStringAllocateAndFormat(NULL, "%s", children[i].nameBytes, children[i].name);
 | 
							child.name = EsStringAllocateAndFormat(NULL, "%s", children[i].nameBytes, children[i].name);
 | 
				
			||||||
| 
						 | 
					@ -464,6 +550,7 @@ void CreateImportNode(const char *path, ImportNode *node) {
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			child.isFile = true;
 | 
								child.isFile = true;
 | 
				
			||||||
 | 
								child.contentType = MatchFileContentType(pathBuffer);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		child.name = strdup(dir->d_name);
 | 
							child.name = strdup(dir->d_name);
 | 
				
			||||||
| 
						 | 
					@ -549,20 +636,6 @@ bool MakeBundle(const char *outputFile, BundleInput *inputFiles, size_t inputFil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//////////////////////////////////
 | 
					//////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct FileType {
 | 
					 | 
				
			||||||
	const char *extension;
 | 
					 | 
				
			||||||
	const char *name;
 | 
					 | 
				
			||||||
	const char *icon;
 | 
					 | 
				
			||||||
	int id, openID;
 | 
					 | 
				
			||||||
	bool hasThumbnailGenerator;
 | 
					 | 
				
			||||||
} FileType;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct Handler {
 | 
					 | 
				
			||||||
	const char *extension;
 | 
					 | 
				
			||||||
	const char *action;
 | 
					 | 
				
			||||||
	int fileTypeID;
 | 
					 | 
				
			||||||
} Handler;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct DependencyFile {
 | 
					typedef struct DependencyFile {
 | 
				
			||||||
	char path[256];
 | 
						char path[256];
 | 
				
			||||||
	const char *name;
 | 
						const char *name;
 | 
				
			||||||
| 
						 | 
					@ -573,11 +646,9 @@ typedef struct Application {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const char *name; 
 | 
						const char *name; 
 | 
				
			||||||
	EsINIState *properties;
 | 
						EsINIState *properties;
 | 
				
			||||||
 | 
						EsINIState *fileTypeLines;
 | 
				
			||||||
	int id;
 | 
						int id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	FileType *fileTypes;
 | 
					 | 
				
			||||||
	Handler *handlers;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool install, builtin, withCStdLib;
 | 
						bool install, builtin, withCStdLib;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const char **sources;
 | 
						const char **sources;
 | 
				
			||||||
| 
						 | 
					@ -794,8 +865,6 @@ void ParseApplicationManifest(const char *manifestPath) {
 | 
				
			||||||
	application.manifestPath = manifestPath;
 | 
						application.manifestPath = manifestPath;
 | 
				
			||||||
	application.compileFlags = "";
 | 
						application.compileFlags = "";
 | 
				
			||||||
	application.linkFlags = "";
 | 
						application.linkFlags = "";
 | 
				
			||||||
	Handler *handler = NULL;
 | 
					 | 
				
			||||||
	FileType *fileType = NULL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (EsINIParse(&s)) {
 | 
						while (EsINIParse(&s)) {
 | 
				
			||||||
		EsINIZeroTerminate(&s);
 | 
							EsINIZeroTerminate(&s);
 | 
				
			||||||
| 
						 | 
					@ -815,27 +884,8 @@ void ParseApplicationManifest(const char *manifestPath) {
 | 
				
			||||||
			else INI_READ_BOOL(disabled, disabled);
 | 
								else INI_READ_BOOL(disabled, disabled);
 | 
				
			||||||
			else INI_READ_BOOL(needs_native_toolchain, needsNativeToolchain);
 | 
								else INI_READ_BOOL(needs_native_toolchain, needsNativeToolchain);
 | 
				
			||||||
			else if (s.keyBytes && s.valueBytes) arrput(application.properties, s);
 | 
								else if (s.keyBytes && s.valueBytes) arrput(application.properties, s);
 | 
				
			||||||
		} else if (0 == strcmp(s.section, "handler")) {
 | 
					 | 
				
			||||||
			if (!s.keyBytes) {
 | 
					 | 
				
			||||||
				Handler _handler = {};
 | 
					 | 
				
			||||||
				arrput(application.handlers, _handler);
 | 
					 | 
				
			||||||
				handler = &arrlast(application.handlers);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			INI_READ_STRING_PTR(extension, handler->extension);
 | 
					 | 
				
			||||||
			INI_READ_STRING_PTR(action, handler->action);
 | 
					 | 
				
			||||||
		} else if (0 == strcmp(s.section, "file_type")) {
 | 
							} else if (0 == strcmp(s.section, "file_type")) {
 | 
				
			||||||
			if (!s.keyBytes) {
 | 
								arrput(application.fileTypeLines, s);
 | 
				
			||||||
				FileType _fileType = {};
 | 
					 | 
				
			||||||
				_fileType.id = nextID++;
 | 
					 | 
				
			||||||
				arrput(application.fileTypes, _fileType);
 | 
					 | 
				
			||||||
				fileType = &arrlast(application.fileTypes);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			INI_READ_STRING_PTR(extension, fileType->extension);
 | 
					 | 
				
			||||||
			INI_READ_STRING_PTR(name, fileType->name);
 | 
					 | 
				
			||||||
			INI_READ_STRING_PTR(icon, fileType->icon);
 | 
					 | 
				
			||||||
			INI_READ_BOOL(has_thumbnail_generator, fileType->hasThumbnailGenerator);
 | 
					 | 
				
			||||||
		} else if (0 == strcmp(s.section, "embed") && s.key[0] != ';' && s.value[0]) {
 | 
							} else if (0 == strcmp(s.section, "embed") && s.key[0] != ';' && s.value[0]) {
 | 
				
			||||||
			BundleInput input = { 0 };
 | 
								BundleInput input = { 0 };
 | 
				
			||||||
			input.path = s.value;
 | 
								input.path = s.value;
 | 
				
			||||||
| 
						 | 
					@ -884,38 +934,6 @@ void OutputSystemConfiguration() {
 | 
				
			||||||
		FileWrite(file, EsINIFormat(generalOptions + i, buffer, sizeof(buffer)), buffer);
 | 
							FileWrite(file, EsINIFormat(generalOptions + i, buffer, sizeof(buffer)), buffer);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (uintptr_t i = 0; i < arrlenu(applications); i++) {
 | 
					 | 
				
			||||||
		if (!applications[i].install) {
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (uintptr_t j = 0; j < arrlenu(applications[i].handlers); j++) {
 | 
					 | 
				
			||||||
			Handler *handler = applications[i].handlers + j;
 | 
					 | 
				
			||||||
			int handlerID = applications[i].id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			for (uintptr_t i = 0; i < arrlenu(applications); i++) {
 | 
					 | 
				
			||||||
				for (uintptr_t j = 0; j < arrlenu(applications[i].fileTypes); j++) {
 | 
					 | 
				
			||||||
					FileType *fileType = applications[i].fileTypes + j;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					if (0 == strcmp(handler->extension, fileType->extension)) {
 | 
					 | 
				
			||||||
						handler->fileTypeID = fileType->id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						if (0 == strcmp(handler->action, "open")) {
 | 
					 | 
				
			||||||
							fileType->openID = handlerID;
 | 
					 | 
				
			||||||
						} else {
 | 
					 | 
				
			||||||
							Log("Warning: unrecognised handler action '%s'.\n", handler->action);
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!handler->fileTypeID) {
 | 
					 | 
				
			||||||
				Log("Warning: could not find a file_type entry for handler with extension '%s' in application '%s'.\n",
 | 
					 | 
				
			||||||
						handler->extension, applications[i].name);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (uintptr_t i = 0; i < arrlenu(applications); i++) {
 | 
						for (uintptr_t i = 0; i < arrlenu(applications); i++) {
 | 
				
			||||||
		if (!applications[i].install) {
 | 
							if (!applications[i].install) {
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
| 
						 | 
					@ -931,21 +949,13 @@ void OutputSystemConfiguration() {
 | 
				
			||||||
			FilePrintFormat(file, "%s=%s\n", applications[i].properties[j].key, applications[i].properties[j].value);
 | 
								FilePrintFormat(file, "%s=%s\n", applications[i].properties[j].key, applications[i].properties[j].value);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (uintptr_t j = 0; j < arrlenu(applications[i].fileTypes); j++) {
 | 
							for (uintptr_t j = 0; j < arrlenu(applications[i].fileTypeLines); j++) {
 | 
				
			||||||
			FilePrintFormat(file, "\n[file_type]\n");
 | 
								char buffer[4096];
 | 
				
			||||||
			FilePrintFormat(file, "id=%d\n", applications[i].fileTypes[j].id);
 | 
								FileWrite(file, EsINIFormat(applications[i].fileTypeLines + j, buffer, sizeof(buffer)), buffer);
 | 
				
			||||||
			FilePrintFormat(file, "extension=%s\n", applications[i].fileTypes[j].extension);
 | 
					 | 
				
			||||||
			FilePrintFormat(file, "name=%s\n", applications[i].fileTypes[j].name);
 | 
					 | 
				
			||||||
			FilePrintFormat(file, "icon=%s\n", applications[i].fileTypes[j].icon);
 | 
					 | 
				
			||||||
			FilePrintFormat(file, "open=%d\n", applications[i].fileTypes[j].openID);
 | 
					 | 
				
			||||||
			FilePrintFormat(file, "has_thumbnail_generator=%d\n", applications[i].fileTypes[j].hasThumbnailGenerator);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (uintptr_t j = 0; j < arrlenu(applications[i].handlers); j++) {
 | 
								if (!applications[i].fileTypeLines[j].keyBytes) {
 | 
				
			||||||
			FilePrintFormat(file, "\n[handler]\n");
 | 
									FilePrintFormat(file, "application=%d\n", applications[i].id);
 | 
				
			||||||
			FilePrintFormat(file, "action=%s\n", applications[i].handlers[j].action);
 | 
								}
 | 
				
			||||||
			FilePrintFormat(file, "application=%d\n", applications[i].id);
 | 
					 | 
				
			||||||
			FilePrintFormat(file, "file_type=%d\n", applications[i].handlers[j].fileTypeID);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1752,8 +1762,7 @@ int main(int argc, char **argv) {
 | 
				
			||||||
		arrfree(applications[i].sources);
 | 
							arrfree(applications[i].sources);
 | 
				
			||||||
		arrfree(applications[i].dependencyFiles);
 | 
							arrfree(applications[i].dependencyFiles);
 | 
				
			||||||
		arrfree(applications[i].bundleInputFiles);
 | 
							arrfree(applications[i].bundleInputFiles);
 | 
				
			||||||
		arrfree(applications[i].fileTypes);
 | 
							arrfree(applications[i].fileTypeLines);
 | 
				
			||||||
		arrfree(applications[i].handlers);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	arrfree(applicationManifests);
 | 
						arrfree(applicationManifests);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,10 +9,9 @@ permission_run_temporary_application=1
 | 
				
			||||||
source=util/build_core.c
 | 
					source=util/build_core.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=build_core
 | 
					match=ext:build_core
 | 
				
			||||||
name=Build config
 | 
					name=Build config
 | 
				
			||||||
icon=icon_text_x_makefile
 | 
					icon=icon_text_x_makefile
 | 
				
			||||||
 | 
					uuid=6F-2A-23-16-AF-A3-D0-FB-95-06-52-B3-67-FB-37-FA
 | 
				
			||||||
[handler]
 | 
					actions=open
 | 
				
			||||||
extension=build_core
 | 
					textual=1
 | 
				
			||||||
action=open
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3758,6 +3758,9 @@ int _UIInstanceCallback(EsInstance *instance, EsMessage *message) {
 | 
				
			||||||
	if (message->type == ES_MSG_INSTANCE_SAVE) {
 | 
						if (message->type == ES_MSG_INSTANCE_SAVE) {
 | 
				
			||||||
		fileStore = message->instanceSave.file;
 | 
							fileStore = message->instanceSave.file;
 | 
				
			||||||
		DocumentSave(nullptr);
 | 
							DocumentSave(nullptr);
 | 
				
			||||||
 | 
							EsUniqueIdentifier type = (EsUniqueIdentifier) 
 | 
				
			||||||
 | 
								{{ 0x26, 0x4A, 0xE0, 0x6C, 0x0F, 0xE8, 0x2C, 0xE7, 0xF4, 0x6E, 0x4E, 0x76, 0x5B, 0x4A, 0xCF, 0x4F }};
 | 
				
			||||||
 | 
							EsFileStoreSetContentType(fileStore, type);
 | 
				
			||||||
		EsInstanceSaveComplete(instance, fileStore, true);
 | 
							EsInstanceSaveComplete(instance, fileStore, true);
 | 
				
			||||||
		fileStore = nullptr;
 | 
							fileStore = nullptr;
 | 
				
			||||||
	} else if (message->type == ES_MSG_INSTANCE_OPEN) {
 | 
						} else if (message->type == ES_MSG_INSTANCE_OPEN) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,10 +7,8 @@ source=util/designer2.cpp
 | 
				
			||||||
compile_flags=-D UI_ESSENCE -Wno-unused-parameter
 | 
					compile_flags=-D UI_ESSENCE -Wno-unused-parameter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[file_type]
 | 
					[file_type]
 | 
				
			||||||
extension=designer
 | 
					match=ext:designer
 | 
				
			||||||
name=Designer file
 | 
					name=Designer file
 | 
				
			||||||
icon=icon_text_css
 | 
					icon=icon_text_css
 | 
				
			||||||
 | 
					uuid=26-4A-E0-6C-0F-E8-2C-E7-F4-6E-4E-76-5B-4A-CF-4F
 | 
				
			||||||
[handler]
 | 
					actions=open
 | 
				
			||||||
extension=designer
 | 
					 | 
				
			||||||
action=open
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue