general: use contentType field for file types

This commit is contained in:
nakst 2022-03-24 16:58:35 +00:00
parent 4f9b5194cd
commit 910bc1bbb0
35 changed files with 745 additions and 288 deletions

View File

@ -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

View File

@ -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

View File

@ -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++);

View File

@ -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;

View File

@ -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);

View File

@ -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];
}

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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++) {

View File

@ -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

119
help/Content Types.txt Normal file
View File

@ -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 }

View File

@ -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) {

View File

@ -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(&region->data.file.node->resizeLock, K_LOCK_SHARED);
EsError error = CCSpaceAccess(&region->data.file.node->cache, (void *) address,
offsetIntoRegion + region->data.file.offset, pagesToRead * K_PAGE_SIZE,
CC_ACCESS_MAP, space, flags);
KWriterLockReturn(&region->data.file.node->resizeLock, K_LOCK_SHARED);
KMutexAcquire(&region->data.mapMutex);
if (error != ES_SUCCESS) {

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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)) {

View File

@ -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 $@`"

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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) {

View File

@ -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