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 :)
|
||||
|
||||
## Source
|
||||
## Building
|
||||
|
||||
See `help/Building.md` for a description of how to build and test the system from source.
|
||||
|
||||
For an overview of the files in the source tree, see `help/Source Map.md`.
|
||||
See `help/Building.md` for a description of how to build and test the system.
|
||||
|
||||
## Features
|
||||
|
||||
|
|
|
@ -13,108 +13,131 @@ background_service=1
|
|||
source=apps/file_manager/main.cpp
|
||||
|
||||
[file_type]
|
||||
extension=esx
|
||||
match=ext:esx
|
||||
name=Executable
|
||||
icon=icon_application_default_icon
|
||||
uuid=BF-88-E4-DD-71-E8-5F-DE-16-C5-AF-33-41-C7-A2-96
|
||||
|
||||
[file_type]
|
||||
extension=ini
|
||||
match=ext:ini
|
||||
name=Configuration file
|
||||
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]
|
||||
extension=esx_symbols
|
||||
name=Debugging data
|
||||
icon=icon_extension
|
||||
|
||||
[file_type]
|
||||
extension=exe
|
||||
name=PC executable
|
||||
icon=icon_application_x_ms_dos_executable
|
||||
|
||||
[file_type]
|
||||
extension=png
|
||||
match=ext:png
|
||||
name=PNG image
|
||||
icon=icon_image_x_generic
|
||||
has_thumbnail_generator=1
|
||||
uuid=59-21-05-4D-34-40-AB-61-EC-F9-7D-5C-6E-04-96-AE
|
||||
|
||||
[file_type]
|
||||
extension=jpg
|
||||
match=ext:jpg,ext:jpeg
|
||||
name=JPG image
|
||||
icon=icon_image_x_generic
|
||||
has_thumbnail_generator=1
|
||||
uuid=D8-C2-13-B0-53-64-82-11-48-7B-5B-64-0F-92-B9-38
|
||||
|
||||
[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
|
||||
icon=icon_font_x_generic
|
||||
uuid=DA-BF-EC-06-31-8A-67-B6-E3-95-BC-D1-92-D2-9A-56
|
||||
|
||||
[file_type]
|
||||
extension=otf
|
||||
match=ext:otf
|
||||
name=OpenType font
|
||||
icon=icon_font_x_generic
|
||||
uuid=E7-2B-BB-E7-FF-75-32-E0-4E-75-41-C0-D2-ED-80-56
|
||||
|
||||
[file_type]
|
||||
extension=a
|
||||
match=ext:a
|
||||
name=Static library
|
||||
icon=icon_extension
|
||||
uuid=7D-39-BF-18-9E-07-BC-D3-4F-9F-87-EC-6F-70-65-5D
|
||||
|
||||
[file_type]
|
||||
extension=dat
|
||||
name=Database
|
||||
match=ext:dat
|
||||
name=Application data
|
||||
icon=icon_office_database
|
||||
uuid=B5-32-22-14-01-CB-D0-1D-A2-55-08-C7-0D-46-86-53
|
||||
|
||||
[file_type]
|
||||
extension=icon_pack
|
||||
name=Icon pack
|
||||
icon=icon_package_x_generic
|
||||
|
||||
[file_type]
|
||||
extension=ekm
|
||||
match=ext:ekm
|
||||
name=Kernel module
|
||||
icon=icon_extension
|
||||
uuid=80-6D-8E-D6-C7-45-AD-A7-03-5B-B3-64-C5-0A-EE-C6
|
||||
|
||||
[file_type]
|
||||
extension=o
|
||||
match=ext:o
|
||||
name=Binary object
|
||||
icon=icon_extension
|
||||
uuid=B5-19-F2-0D-AD-4F-D4-C9-A4-BF-97-E4-5E-4C-55-00
|
||||
|
||||
[file_type]
|
||||
extension=c
|
||||
match=ext:c
|
||||
name=C source
|
||||
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]
|
||||
extension=cpp
|
||||
match=ext:cpp
|
||||
name=C++ source
|
||||
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]
|
||||
extension=h
|
||||
match=ext:h
|
||||
name=C header
|
||||
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]
|
||||
extension=txt
|
||||
match=ext:txt
|
||||
name=Text file
|
||||
icon=icon_text
|
||||
uuid=25-65-4C-89-E7-29-EA-9E-B5-BE-B5-CA-A7-60-BD-3D
|
||||
textual=1
|
||||
|
||||
[file_type]
|
||||
extension=md
|
||||
match=ext:md
|
||||
name=Markdown file
|
||||
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]
|
||||
extension=ogg
|
||||
match=ext:ogg
|
||||
name=OGG audio
|
||||
icon=icon_audio_x_generic
|
||||
uuid=E5-14-14-ED-4D-67-C0-D0-62-A1-A4-D4-F5-82-A7-88
|
||||
|
||||
[file_type]
|
||||
extension=mp4
|
||||
match=ext:mp4
|
||||
name=MP4 video
|
||||
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]
|
||||
extension=wav
|
||||
match=ext:wav
|
||||
name=Wave audio
|
||||
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;
|
||||
|
||||
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.
|
||||
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) {
|
||||
|
@ -499,23 +500,32 @@ void InstanceRegisterCommands(Instance *instance) {
|
|||
EsCommandRegister(&instance->commandRename, instance, INTERFACE_STRING(FileManagerRenameAction), CommandRename, stableCommandID++, "F2");
|
||||
|
||||
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;
|
||||
InstanceRefreshViewType(instance, old != instance->viewSettings.viewType);
|
||||
InstanceRefreshViewType(instance);
|
||||
InstanceViewSettingsUpdated(instance);
|
||||
}, stableCommandID++);
|
||||
|
||||
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;
|
||||
InstanceRefreshViewType(instance, old != instance->viewSettings.viewType);
|
||||
InstanceRefreshViewType(instance);
|
||||
InstanceViewSettingsUpdated(instance);
|
||||
}, stableCommandID++);
|
||||
|
||||
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;
|
||||
InstanceRefreshViewType(instance, old != instance->viewSettings.viewType);
|
||||
InstanceRefreshViewType(instance);
|
||||
InstanceViewSettingsUpdated(instance);
|
||||
}, stableCommandID++);
|
||||
|
||||
|
|
|
@ -394,7 +394,6 @@ FolderEntry *FolderAddEntry(Folder *folder, const char *_name, size_t nameBytes,
|
|||
entry->handles = 1;
|
||||
entry->name = name;
|
||||
entry->nameBytes = nameBytes;
|
||||
entry->extensionOffset = PathGetExtension(entry->GetName()).text - name;
|
||||
entry->internalName = name;
|
||||
entry->internalNameBytes = nameBytes;
|
||||
|
||||
|
@ -417,6 +416,7 @@ FolderEntry *FolderAddEntry(Folder *folder, const char *_name, size_t nameBytes,
|
|||
entry->size = information->fileSize;
|
||||
entry->isFolder = information->type == ES_NODE_DIRECTORY;
|
||||
entry->sizeUnknown = entry->isFolder && information->directoryChildren == ES_DIRECTORY_CHILDREN_UNKNOWN;
|
||||
entry->contentType = information->contentType;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
@ -459,12 +459,26 @@ uint64_t FolderRemoveEntryAndUpdateInstances(Folder *folder, const char *name, s
|
|||
return id;
|
||||
}
|
||||
|
||||
void FolderFileUpdatedAtPath(String path, Instance *instance) {
|
||||
void FolderFileUpdatedAtPath(String path, Instance *instance, bool setGuessedContentType = false) {
|
||||
path = PathRemoveTrailingSlash(path);
|
||||
String file = PathGetName(path);
|
||||
String folder = PathGetParent(path);
|
||||
EsDirectoryChild 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++) {
|
||||
if (loadedFolders[i]->itemHandler->type != NAMESPACE_HANDLER_FILE_SYSTEM) continue;
|
||||
|
|
|
@ -53,12 +53,14 @@ const char *errorTypeStrings[] = {
|
|||
#define LOAD_FOLDER_NO_FOCUS (1 << 8)
|
||||
|
||||
struct FolderEntry {
|
||||
// TODO Can this structure be made smaller?
|
||||
uint16_t handles;
|
||||
bool isFolder, sizeUnknown;
|
||||
uint8_t nameBytes, extensionOffset, internalNameBytes; // 0 -> 256.
|
||||
bool isFolder, sizeUnknown, guessedContentType;
|
||||
uint8_t nameBytes, internalNameBytes; // 0 -> 256.
|
||||
char *name, *internalName;
|
||||
EsFileOffset size, previousSize;
|
||||
uint64_t id;
|
||||
EsUniqueIdentifier contentType;
|
||||
|
||||
inline String GetName() {
|
||||
return { .text = name, .bytes = nameBytes ?: 256u, .allocated = nameBytes ?: 256u };
|
||||
|
@ -67,11 +69,6 @@ struct FolderEntry {
|
|||
inline String GetInternalName() {
|
||||
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 {
|
||||
|
@ -227,7 +224,7 @@ void InstanceReportError(struct Instance *instance, int error, EsError code);
|
|||
bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, int historyMode = 0);
|
||||
void InstanceUpdateStatusString(Instance *instance);
|
||||
void InstanceViewSettingsUpdated(Instance *instance);
|
||||
void InstanceRefreshViewType(Instance *instance, bool startTransition);
|
||||
void InstanceRefreshViewType(Instance *instance);
|
||||
void InstanceFolderPathChanged(Instance *instance, bool fromLoadFolder);
|
||||
void InstanceAddContents(struct Instance *instance, HashTable *newEntries);
|
||||
void InstanceAddSingle(struct Instance *instance, ListEntry newEntry);
|
||||
|
@ -583,6 +580,10 @@ void _start() {
|
|||
StringDestroy(&openDocuments[i]);
|
||||
}
|
||||
|
||||
for (uintptr_t i = 0; i < knownFileTypes.Length(); i++) {
|
||||
knownFileTypes[i].applicationEntries.Free();
|
||||
}
|
||||
|
||||
EsAssert(!instances.Length());
|
||||
EsHeapFree(fileTypesBuffer.out);
|
||||
|
||||
|
@ -608,7 +609,7 @@ void _start() {
|
|||
size_t pathSectionCount = PathCountSections(fullPath);
|
||||
|
||||
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);
|
||||
|
|
|
@ -2,101 +2,242 @@
|
|||
// It is released under the terms of the MIT license -- see LICENSE.md.
|
||||
// Written by: nakst.
|
||||
|
||||
struct FileTypeApplicationEntry {
|
||||
int64_t application;
|
||||
bool open;
|
||||
};
|
||||
|
||||
struct FileType {
|
||||
Array<FileTypeApplicationEntry> applicationEntries; // One per application that is involved with this type.
|
||||
EsUniqueIdentifier identifier;
|
||||
char *name;
|
||||
size_t nameBytes;
|
||||
char *extension;
|
||||
size_t extensionBytes;
|
||||
uint32_t iconID;
|
||||
int64_t openHandler;
|
||||
|
||||
// TODO Allow applications to register their own thumbnail generators.
|
||||
bool hasThumbnailGenerator;
|
||||
bool textual;
|
||||
bool hasThumbnailGenerator; // TODO Allow applications to register their own thumbnail generators.
|
||||
};
|
||||
|
||||
Array<FileType> knownFileTypes;
|
||||
HashStore<char, uintptr_t /* index into knownFileTypes */> knownFileTypesByExtension;
|
||||
HashStore<char, EsUniqueIdentifier> knownFileTypesByExtension;
|
||||
EsBuffer fileTypesBuffer;
|
||||
|
||||
void AddKnownFileTypes() {
|
||||
#define ADD_FILE_TYPE(_extension, _name, _iconID) \
|
||||
#define ADD_FILE_TYPE(_name, _iconID) \
|
||||
{ \
|
||||
FileType type = {}; \
|
||||
type.name = (char *) _name; \
|
||||
type.nameBytes = EsCStringLength(_name); \
|
||||
type.extension = (char *) _extension; \
|
||||
type.extensionBytes = EsCStringLength(_extension); \
|
||||
type.iconID = _iconID; \
|
||||
knownFileTypes.Add(type); \
|
||||
}
|
||||
|
||||
#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)
|
||||
ADD_FILE_TYPE("", interfaceString_CommonItemFile, ES_ICON_UNKNOWN);
|
||||
ADD_FILE_TYPE(interfaceString_CommonItemFile, ES_ICON_UNKNOWN);
|
||||
#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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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 };
|
||||
EsSystemConfigurationReadFileTypes(&fileTypesBuffer);
|
||||
|
||||
EsINIState s = { .buffer = (char *) fileTypesBuffer.out, .bytes = fileTypesBuffer.bytes };
|
||||
FileType type = {};
|
||||
FileTypeApplicationEntry applicationEntry = {};
|
||||
bool specifiedTextual = false, specifiedHasThumbnailGenerator = false, hasUUID = false;
|
||||
const char *matchValue;
|
||||
size_t matchValueBytes = 0;
|
||||
|
||||
while (EsINIParse(&s)) {
|
||||
if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("name"))) {
|
||||
type.name = s.value, type.nameBytes = s.valueBytes;
|
||||
} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("extension"))) {
|
||||
type.extension = s.value, type.extensionBytes = s.valueBytes;
|
||||
} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("open"))) {
|
||||
type.openHandler = EsIntegerParse(s.value, s.valueBytes);
|
||||
} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("uuid"))) {
|
||||
type.identifier = EsUniqueIdentifierParse(s.value, s.valueBytes);
|
||||
hasUUID = true;
|
||||
} else if (0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("icon"))) {
|
||||
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.
|
||||
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) {
|
||||
uintptr_t index = knownFileTypes.Length();
|
||||
knownFileTypes.Add(type);
|
||||
*knownFileTypesByExtension.Put(type.extension, type.extensionBytes) = index;
|
||||
EsUniqueIdentifier zeroIdentifier = {};
|
||||
|
||||
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(&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) {
|
||||
EsUniqueIdentifier zeroIdentifier = {};
|
||||
|
||||
if (entry->isFolder) {
|
||||
if (folder->itemHandler->getFileType != NamespaceDefaultGetFileType) {
|
||||
String path = StringAllocateAndFormat("%s%s", STRFMT(folder->path), STRFMT(entry->GetInternalName()));
|
||||
FileType *type = &knownFileTypes[folder->itemHandler->getFileType(path)];
|
||||
StringDestroy(&path);
|
||||
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[index ? index : KNOWN_FILE_TYPE_UNKNOWN];
|
||||
return &knownFileTypes[KNOWN_FILE_TYPE_DIRECTORY];
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
|
@ -168,11 +168,7 @@ bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, i
|
|||
return true;
|
||||
}
|
||||
|
||||
void InstanceRefreshViewType(Instance *instance, bool startTransition) {
|
||||
if (startTransition) {
|
||||
EsElementStartTransition(instance->list, ES_TRANSITION_FADE, ES_ELEMENT_TRANSITION_CONTENT_ONLY, 1.0f);
|
||||
}
|
||||
|
||||
void InstanceRefreshViewType(Instance *instance) {
|
||||
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->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) {
|
||||
result = EsStringCompare(STRING(left->GetName()), STRING(right->GetName()));
|
||||
} 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
if (instance->listContents.Length()) {
|
||||
|
@ -835,6 +837,8 @@ int ListCallback(EsElement *element, EsMessage *message) {
|
|||
|
||||
if (listEntry) {
|
||||
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) {
|
||||
String path = instance->folder->itemHandler->getPathForChild(instance->folder, entry);
|
||||
|
@ -850,7 +854,7 @@ int ListCallback(EsElement *element, EsMessage *message) {
|
|||
} else {
|
||||
InstanceLoadFolder(instance, path);
|
||||
}
|
||||
} else if (StringEquals(entry->GetExtension(), StringFromLiteral("esx"))) {
|
||||
} else if (0 == EsMemoryCompare(&entry->contentType, &applicationContentType, sizeof(EsUniqueIdentifier))) {
|
||||
// TODO Temporary.
|
||||
String path = StringAllocateAndFormat("%s%s", STRFMT(instance->folder->path), STRFMT(entry->GetInternalName()));
|
||||
|
||||
|
@ -866,11 +870,18 @@ int ListCallback(EsElement *element, EsMessage *message) {
|
|||
StringDestroy(&path);
|
||||
} else {
|
||||
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()));
|
||||
EsApplicationStartupRequest request = {};
|
||||
request.id = fileType->openHandler;
|
||||
request.id = typeEntry->application;
|
||||
request.filePath = path.text;
|
||||
request.filePathBytes = path.bytes;
|
||||
request.flags = EsKeyboardIsCtrlHeld() || message->chooseItem.source == ES_LIST_VIEW_CHOOSE_ITEM_MIDDLE_CLICK
|
||||
|
|
|
@ -5,10 +5,10 @@ icon=icon_applications_fonts
|
|||
[build]
|
||||
source=apps/font_book.cpp
|
||||
|
||||
[handler]
|
||||
extension=ttf
|
||||
action=open
|
||||
[file_type]
|
||||
actions=open
|
||||
uuid=DA-BF-EC-06-31-8A-67-B6-E3-95-BC-D1-92-D2-9A-56
|
||||
|
||||
[handler]
|
||||
extension=otf
|
||||
action=open
|
||||
[file_type]
|
||||
actions=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 };
|
||||
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"))
|
||||
|| 0 == EsStringCompare(extension, extensionBytes, EsLiteral("jpeg"))) {
|
||||
EsFileStoreSetContentType(message->instanceSave.file, typeJPG);
|
||||
stbi_write_jpg_to_func(WriteCallback, &buffer, width, height, 4, bits, 90);
|
||||
} else if (0 == EsStringCompare(extension, extensionBytes, EsLiteral("bmp"))) {
|
||||
EsFileStoreSetContentType(message->instanceSave.file, typeBMP);
|
||||
stbi_write_bmp_to_func(WriteCallback, &buffer, width, height, 4, bits);
|
||||
} else if (0 == EsStringCompare(extension, extensionBytes, EsLiteral("tga"))) {
|
||||
EsFileStoreSetContentType(message->instanceSave.file, typeTGA);
|
||||
stbi_write_tga_to_func(WriteCallback, &buffer, width, height, 4, bits);
|
||||
} else {
|
||||
EsFileStoreSetContentType(message->instanceSave.file, typePNG);
|
||||
stbi_write_png_to_func(WriteCallback, &buffer, width, height, 4, bits, stride);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,18 @@ use_single_process=1
|
|||
[build]
|
||||
source=apps/image_editor.cpp
|
||||
|
||||
[handler]
|
||||
extension=jpg
|
||||
action=open
|
||||
[file_type]
|
||||
actions=open
|
||||
uuid=D8-C2-13-B0-53-64-82-11-48-7B-5B-64-0F-92-B9-38
|
||||
|
||||
[handler]
|
||||
extension=png
|
||||
action=open
|
||||
[file_type]
|
||||
actions=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]
|
||||
source=apps/markdown_viewer.cpp
|
||||
|
||||
[handler]
|
||||
extension=md
|
||||
action=open
|
||||
[file_type]
|
||||
actions=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;
|
||||
char *contents = EsTextboxGetContents(instance->textboxDocument, &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);
|
||||
EsInstanceSaveComplete(instance, message->instanceSave.file, true);
|
||||
} else if (message->type == ES_MSG_INSTANCE_OPEN) {
|
||||
|
|
|
@ -3,25 +3,8 @@ name=Text Editor
|
|||
icon=icon_accessories_text_editor
|
||||
use_single_process=1
|
||||
|
||||
[file_type]
|
||||
opens_any_textual_file=1
|
||||
|
||||
[build]
|
||||
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) {
|
||||
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) {
|
||||
EsInstance *instance = InstanceFromWindowID(message.message.tabOperation.id);
|
||||
if (instance) UIRefreshPrimaryClipboard(instance->window);
|
||||
|
@ -1237,9 +1241,9 @@ void EsInstanceSaveComplete(EsInstance *instance, EsFileStore *file, bool succes
|
|||
APIInstance *apiInstance = (APIInstance *) instance->_private;
|
||||
|
||||
if (instance) {
|
||||
char buffer[1];
|
||||
buffer[0] = DESKTOP_MSG_COMPLETE_SAVE;
|
||||
MessageDesktop(buffer, 1, instance->window->handle);
|
||||
// HACK Post this message so that our handle to the file is (hopefully) closed first.
|
||||
EsMessage m = { .type = ES_MSG_INSTANCE_SAVE_COMPLETE_DELAYED, ._argument = instance };
|
||||
EsMessagePost(nullptr, &m);
|
||||
|
||||
if (success) {
|
||||
const char *name;
|
||||
|
|
|
@ -98,6 +98,7 @@ struct OpenDocument {
|
|||
size_t pathBytes;
|
||||
char *temporarySavePath;
|
||||
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;
|
||||
EsObjectID id;
|
||||
EsObjectID currentWriter;
|
||||
|
@ -2312,6 +2313,11 @@ void ApplicationInstanceRequestSave(ApplicationInstance *instance, const char *n
|
|||
EsHeapFree(document->temporarySavePath);
|
||||
document->temporarySavePath = nullptr;
|
||||
|
||||
if (ES_SUCCESS != EsFileControl(document->readHandle, ES_FILE_CONTROL_GET_CONTENT_TYPE,
|
||||
&document->temporarySavePreviousContentType, sizeof(EsUniqueIdentifier))) {
|
||||
document->temporarySavePreviousContentType = {};
|
||||
}
|
||||
|
||||
EsHandle fileHandle;
|
||||
m.tabOperation.error = TemporaryFileCreate(&fileHandle, &document->temporarySavePath, &document->temporarySavePathBytes, ES_FILE_WRITE);
|
||||
|
||||
|
@ -2410,6 +2416,22 @@ void ApplicationInstanceCompleteSave(ApplicationInstance *fromInstance) {
|
|||
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;
|
||||
|
||||
if (desktop.fileManager && desktop.fileManager->singleProcess) {
|
||||
|
|
|
@ -95,7 +95,7 @@ EsError EsPathCreate(const char *path, ptrdiff_t pathBytes, EsNodeType type, boo
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
EsMutexAcquire(&api.mountPointsMutex);
|
||||
bool duplicate = NodeFindMountPoint(prefix, prefixBytes, nullptr, true);
|
||||
|
@ -545,7 +560,7 @@ EsError EsFileWriteAllGather(const char *filePath, ptrdiff_t filePathLength, con
|
|||
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) {
|
||||
return information.error;
|
||||
|
|
|
@ -763,6 +763,7 @@ inttype EsConnectionOpenFlags uint32_t none {
|
|||
inttype EsFileControlOperation uint32_t none {
|
||||
ES_FILE_CONTROL_FLUSH = 0 // data/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 {
|
||||
|
@ -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 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 EsFileOffset EsFileGetSize(EsHandle handle);
|
||||
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 EsFileOffsetDifference EsFileStoreGetSize(EsFileStore *file); // Returns -1 on error.
|
||||
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.
|
||||
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 int64_t EsIntegerParse(STRING text); // Parses in hexadecimal if the first two characters are '0x'.
|
||||
function EsUniqueIdentifier EsUniqueIdentifierParse(STRING text);
|
||||
|
||||
// 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_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_SAVE_COMPLETE_DELAYED ((EsMessageType) (ES_MSG_SYSTEM_START + 0x206))
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ struct TextPiece {
|
|||
uintptr_t glyphOffset;
|
||||
size_t glyphCount;
|
||||
uintptr_t start, end;
|
||||
bool isTabPiece;
|
||||
bool isTabPiece, isEllipsisPiece;
|
||||
};
|
||||
|
||||
struct TextLine {
|
||||
|
@ -1829,6 +1829,7 @@ void TextAddEllipsis(EsTextPlan *plan, int32_t maximumLineWidth, bool needFinalE
|
|||
piece.glyphOffset = plan->glyphInfos.Length();
|
||||
piece.ascent = FontGetAscent (&plan->font) + plan->currentTextStyle->baselineOffset,
|
||||
piece.descent = -FontGetDescent(&plan->font) - plan->currentTextStyle->baselineOffset;
|
||||
piece.isEllipsisPiece = true;
|
||||
|
||||
for (uintptr_t i = 0; i < glyphCount; i++) {
|
||||
if (!plan->glyphInfos.Add(glyphInfos[i])) break;
|
||||
|
@ -2295,7 +2296,7 @@ void DrawTextPiece(EsPainter *painter, EsTextPlan *plan, TextPiece *piece, TextL
|
|||
|
||||
// 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;
|
||||
|
||||
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.
|
||||
|
||||
- `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.
|
||||
- `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.
|
||||
|
||||
Each `[handler]` section describes this application's support to manage files of a given file type.
|
||||
|
||||
- `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.
|
||||
- `uuid` The EsUniqueIdentifier of the 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`.
|
||||
|
||||
### 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);
|
||||
EsError FSNodeDelete(KNode *node);
|
||||
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);
|
||||
EsError FSFileControlFlush(KNode *node);
|
||||
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) {
|
||||
FSFile *node = EsContainerOf(FSFile, cache, fileCache);
|
||||
|
||||
// KWriterLockAssertLocked(&node->resizeLock);
|
||||
KWriterLockTake(&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) {
|
||||
FSFile *node = EsContainerOf(FSFile, cache, fileCache);
|
||||
|
||||
// KWriterLockAssertLocked(&node->resizeLock);
|
||||
KWriterLockTake(&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);
|
||||
}
|
||||
|
||||
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 (newSize > (EsFileOffset) 1 << 60) {
|
||||
|
@ -350,7 +352,9 @@ EsError FSFileResize(KNode *node, EsFileOffset newSize) {
|
|||
EsError error = ES_SUCCESS;
|
||||
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;
|
||||
} else if (!file->fileSystem->resize) {
|
||||
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) {
|
||||
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)) {
|
||||
return ES_ERROR_COULD_NOT_RESIZE_FILE;
|
||||
}
|
||||
if (ES_SUCCESS != FSFileResize(node, offset + bytes, true /* growOnly */)) {
|
||||
return ES_ERROR_COULD_NOT_RESIZE_FILE;
|
||||
}
|
||||
|
||||
FSFile *file = (FSFile *) node;
|
||||
|
@ -426,8 +428,10 @@ EsError FSFileControlSetContentType(KNode *node, EsUniqueIdentifier identifier)
|
|||
} else if (~node->fileSystem->flags & ES_VOLUME_STORES_CONTENT_TYPE) {
|
||||
return ES_ERROR_UNSUPPORTED;
|
||||
} else {
|
||||
KWriterLockTake(&node->writerLock, K_LOCK_EXCLUSIVE);
|
||||
node->directoryEntry->contentType = identifier;
|
||||
__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;
|
||||
}
|
||||
}
|
||||
|
@ -1015,9 +1019,11 @@ void FSNodeSynchronize(KNode *node) {
|
|||
if (node->directoryEntry->type == ES_NODE_FILE) {
|
||||
FSFile *file = (FSFile *) node;
|
||||
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);
|
||||
KWriterLockReturn(&node->writerLock, K_LOCK_EXCLUSIVE);
|
||||
KWriterLockReturn(&file->writerLock, K_LOCK_EXCLUSIVE);
|
||||
KWriterLockReturn(&file->resizeLock, K_LOCK_EXCLUSIVE);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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,
|
||||
offsetIntoRegion + region->data.file.offset, pagesToRead * K_PAGE_SIZE,
|
||||
CC_ACCESS_MAP, space, flags);
|
||||
|
||||
KWriterLockReturn(®ion->data.file.node->resizeLock, K_LOCK_SHARED);
|
||||
|
||||
KMutexAcquire(®ion->data.mapMutex);
|
||||
|
||||
if (error != ES_SUCCESS) {
|
||||
|
|
|
@ -1193,18 +1193,38 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_FILE_CONTROL) {
|
|||
|
||||
if (file->directoryEntry->type != ES_NODE_FILE) {
|
||||
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;
|
||||
|
||||
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);
|
||||
} 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;
|
||||
SYSCALL_READ(&identifier, argument2, sizeof(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);
|
||||
|
|
|
@ -9,10 +9,9 @@ custom_compile_command=cp root/Applications/POSIX/bin/bochs bin/Bochs
|
|||
require=root/Applications/POSIX/bin/bochs
|
||||
|
||||
[file_type]
|
||||
extension=bochsrc
|
||||
match=ext:bochsrc
|
||||
name=Bochs configuration
|
||||
icon=icon_applications_development
|
||||
|
||||
[handler]
|
||||
extension=bochsrc
|
||||
action=open
|
||||
uuid=FF-92-F0-53-28-83-DC-F1-BA-DD-DE-79-92-33-3F-73
|
||||
actions=open
|
||||
textual=1
|
||||
|
|
|
@ -9,11 +9,10 @@ link_flags=-lOSMesa -lstdc++ -lz
|
|||
with_cstdlib=1
|
||||
source=ports/mesa/obj_viewer.c
|
||||
|
||||
[handler]
|
||||
extension=obj
|
||||
action=open
|
||||
|
||||
[file_type]
|
||||
extension=obj
|
||||
match=ext:obj
|
||||
name=3D 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
|
||||
compile_flags=-Wno-unknown-pragmas -Wno-unused-parameter
|
||||
|
||||
[handler]
|
||||
extension=uxn
|
||||
action=open
|
||||
|
||||
[file_type]
|
||||
extension=uxn
|
||||
match=ext:uxn
|
||||
name=Uxn ROM
|
||||
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);
|
||||
} 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': {
|
||||
uintptr_t value = va_arg(arguments, uintptr_t);
|
||||
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) {
|
||||
if (length1 == -1) length1 = EsCStringLength(s1);
|
||||
if (length2 == -1) length2 = EsCStringLength(s2);
|
||||
if (s1 == s2 && length1 == length2) return 0;
|
||||
|
||||
while (length1 || length2) {
|
||||
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 (_length2 == -1) _length2 = EsCStringLength(s2);
|
||||
size_t length1 = _length1, length2 = _length2;
|
||||
if (s1 == s2 && length1 == length2) return 0;
|
||||
|
||||
while (length1 || length2) {
|
||||
if (!length1) return -1;
|
||||
|
@ -1072,6 +1084,28 @@ static int64_t ConvertCharacterToDigit(int character, int base) {
|
|||
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) {
|
||||
if (bytes == -1) bytes = EsCStringLength(text);
|
||||
|
||||
|
|
|
@ -694,12 +694,13 @@ void PrintTree(uint64_t block, int indent = 2, uint64_t lowerThan = -1) {
|
|||
}
|
||||
#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);
|
||||
GenerateUniqueIdentifier(&entry->identifier, false);
|
||||
entry->attributeOffset = ESFS_ATTRIBUTE_OFFSET;
|
||||
entry->nodeType = nodeType;
|
||||
entry->parent = parentUID;
|
||||
entry->contentType = contentType;
|
||||
|
||||
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.
|
||||
|
@ -736,7 +737,7 @@ void NewDirectoryEntry(DirectoryEntry *entry, uint8_t nodeType, EsUniqueIdentifi
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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 = {};
|
||||
|
||||
{
|
||||
NewDirectoryEntry(&entry, nodeType, directory.identifier, name);
|
||||
NewDirectoryEntry(&entry, nodeType, directory.identifier, name, contentType);
|
||||
// Log("\tchild nodes: %ld\n", directoryAttribute->childNodes);
|
||||
|
||||
if (!AccessNode(&directory, &entry, (directoryAttribute->childNodes - 1) * sizeof(DirectoryEntry), sizeof(DirectoryEntry), &reference, false)) {
|
||||
|
@ -1041,6 +1042,7 @@ typedef struct ImportNode {
|
|||
const char *name, *path;
|
||||
struct ImportNode *children;
|
||||
bool isFile;
|
||||
EsUniqueIdentifier contentType;
|
||||
} ImportNode;
|
||||
|
||||
int64_t Import(ImportNode node, DirectoryEntryReference parentDirectory) {
|
||||
|
@ -1059,7 +1061,7 @@ int64_t Import(ImportNode node, DirectoryEntryReference parentDirectory) {
|
|||
DirectoryEntryReference reference;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1081,7 +1083,7 @@ int64_t Import(ImportNode node, DirectoryEntryReference parentDirectory) {
|
|||
}
|
||||
} else {
|
||||
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);
|
||||
if (size == -1) return -1;
|
||||
DirectoryEntry directory;
|
||||
|
@ -1241,7 +1243,8 @@ bool Format(uint64_t driveSize, const char *volumeName, EsUniqueIdentifier osIns
|
|||
DirectoryEntry entry;
|
||||
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 (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
|
||||
EsFileStoreAppend=37
|
||||
EsBufferFlushToFileStore=38
|
||||
EsFileStoreSetContentType=39
|
||||
EsPathExists=40
|
||||
EsInstanceSetClassViewer=41
|
||||
EsPathDelete=42
|
||||
|
@ -494,3 +495,4 @@ EsRectangleContainsAll=493
|
|||
EsListViewFixedItemSetEnumStringsForColumn=494
|
||||
EsImageDisplayGetImageHeight=495
|
||||
EsDirectoryEnumerate=496
|
||||
EsUniqueIdentifierParse=497
|
||||
|
|
|
@ -406,6 +406,91 @@ bool FileExists(const char *path) {
|
|||
#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) {
|
||||
char pathBuffer[256];
|
||||
|
||||
|
@ -429,6 +514,7 @@ void CreateImportNode(const char *path, ImportNode *node) {
|
|||
CreateImportNode(pathBuffer, &child);
|
||||
} else {
|
||||
child.isFile = true;
|
||||
child.contentType = children[i].contentType;
|
||||
}
|
||||
|
||||
child.name = EsStringAllocateAndFormat(NULL, "%s", children[i].nameBytes, children[i].name);
|
||||
|
@ -464,6 +550,7 @@ void CreateImportNode(const char *path, ImportNode *node) {
|
|||
continue;
|
||||
} else {
|
||||
child.isFile = true;
|
||||
child.contentType = MatchFileContentType(pathBuffer);
|
||||
}
|
||||
|
||||
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 {
|
||||
char path[256];
|
||||
const char *name;
|
||||
|
@ -573,11 +646,9 @@ typedef struct Application {
|
|||
|
||||
const char *name;
|
||||
EsINIState *properties;
|
||||
EsINIState *fileTypeLines;
|
||||
int id;
|
||||
|
||||
FileType *fileTypes;
|
||||
Handler *handlers;
|
||||
|
||||
bool install, builtin, withCStdLib;
|
||||
|
||||
const char **sources;
|
||||
|
@ -794,8 +865,6 @@ void ParseApplicationManifest(const char *manifestPath) {
|
|||
application.manifestPath = manifestPath;
|
||||
application.compileFlags = "";
|
||||
application.linkFlags = "";
|
||||
Handler *handler = NULL;
|
||||
FileType *fileType = NULL;
|
||||
|
||||
while (EsINIParse(&s)) {
|
||||
EsINIZeroTerminate(&s);
|
||||
|
@ -815,27 +884,8 @@ void ParseApplicationManifest(const char *manifestPath) {
|
|||
else INI_READ_BOOL(disabled, disabled);
|
||||
else INI_READ_BOOL(needs_native_toolchain, needsNativeToolchain);
|
||||
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")) {
|
||||
if (!s.keyBytes) {
|
||||
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);
|
||||
arrput(application.fileTypeLines, s);
|
||||
} else if (0 == strcmp(s.section, "embed") && s.key[0] != ';' && s.value[0]) {
|
||||
BundleInput input = { 0 };
|
||||
input.path = s.value;
|
||||
|
@ -884,38 +934,6 @@ void OutputSystemConfiguration() {
|
|||
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++) {
|
||||
if (!applications[i].install) {
|
||||
continue;
|
||||
|
@ -931,21 +949,13 @@ void OutputSystemConfiguration() {
|
|||
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++) {
|
||||
FilePrintFormat(file, "\n[file_type]\n");
|
||||
FilePrintFormat(file, "id=%d\n", applications[i].fileTypes[j].id);
|
||||
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].fileTypeLines); j++) {
|
||||
char buffer[4096];
|
||||
FileWrite(file, EsINIFormat(applications[i].fileTypeLines + j, buffer, sizeof(buffer)), buffer);
|
||||
|
||||
for (uintptr_t j = 0; j < arrlenu(applications[i].handlers); j++) {
|
||||
FilePrintFormat(file, "\n[handler]\n");
|
||||
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);
|
||||
if (!applications[i].fileTypeLines[j].keyBytes) {
|
||||
FilePrintFormat(file, "application=%d\n", applications[i].id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1752,8 +1762,7 @@ int main(int argc, char **argv) {
|
|||
arrfree(applications[i].sources);
|
||||
arrfree(applications[i].dependencyFiles);
|
||||
arrfree(applications[i].bundleInputFiles);
|
||||
arrfree(applications[i].fileTypes);
|
||||
arrfree(applications[i].handlers);
|
||||
arrfree(applications[i].fileTypeLines);
|
||||
}
|
||||
|
||||
arrfree(applicationManifests);
|
||||
|
|
|
@ -9,10 +9,9 @@ permission_run_temporary_application=1
|
|||
source=util/build_core.c
|
||||
|
||||
[file_type]
|
||||
extension=build_core
|
||||
match=ext:build_core
|
||||
name=Build config
|
||||
icon=icon_text_x_makefile
|
||||
|
||||
[handler]
|
||||
extension=build_core
|
||||
action=open
|
||||
uuid=6F-2A-23-16-AF-A3-D0-FB-95-06-52-B3-67-FB-37-FA
|
||||
actions=open
|
||||
textual=1
|
||||
|
|
|
@ -3758,6 +3758,9 @@ int _UIInstanceCallback(EsInstance *instance, EsMessage *message) {
|
|||
if (message->type == ES_MSG_INSTANCE_SAVE) {
|
||||
fileStore = message->instanceSave.file;
|
||||
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);
|
||||
fileStore = nullptr;
|
||||
} else if (message->type == ES_MSG_INSTANCE_OPEN) {
|
||||
|
|
|
@ -7,10 +7,8 @@ source=util/designer2.cpp
|
|||
compile_flags=-D UI_ESSENCE -Wno-unused-parameter
|
||||
|
||||
[file_type]
|
||||
extension=designer
|
||||
match=ext:designer
|
||||
name=Designer file
|
||||
icon=icon_text_css
|
||||
|
||||
[handler]
|
||||
extension=designer
|
||||
action=open
|
||||
uuid=26-4A-E0-6C-0F-E8-2C-E7-F4-6E-4E-76-5B-4A-CF-4F
|
||||
actions=open
|
||||
|
|
Loading…
Reference in New Issue