Path delimiter behaves differntly on macOS and Windows for HyperFile ReadFilename

Hello,

We use HyperFile to store some absolute paths related to our plugin. We, therefore, write the Filename using WriteFilename and upon loading read it back using ReadFilename.

When exchanging the saved c4d files between windows and macOS I noticed that the path delimiters behave differently.

If the Filename that is written to the HyperFile under Windows is C:\Program Files\MyAssets\somefile.dat and the c4d file is then loaded on macOS the Filename read is C:\Program Files\MyAssets\somefile.dat (path delimiter did not change).

If the Filename that is written to the HyperFile under macOS is /Library/Application Support/MyAssets/somefile.dat and the c4d file is then loaded on windows the Filename read is \Library\Application Support\MyAssets\somefile.dat (path delimiter changed from /, \)

Why is the delimiter only changing when loading a file saved on macOS on windows and not the other way round? Is that an expected behavior? And can this be made consistent (that the delimiter is changed on both or on none)?

Kind regards,
Till

Hello @till-niese,

welcome to the Plugin Café community and thank you for reaching out to us.

Your question is a bit hard to answer in this form. I assume you are in some kind of NodeData plugin and must serialize and deserialize some data manually with NodeData::Read and NodeData::Write. In this context you are encountering the described behavior of Windows 'changing' the path delimiters written on MacOS. You have also tagged your posting as maxon API although the types you are mentioning, HyperFile and FileName, are both classic API. What are the maxon API types you are using?

This might be intentional behavior of Filename, a bug in our API, or a problem on your side, it is hard to tell without the code you are using. You can however use maxon::Url, a maxon API type, and its file scheme to normalize file paths. There is also the function MaxonConvert which can convert between Filename and maxon::Url. Find a small code snippet at the end of the posting.

I hope this helps, if not, I would ask you to provide a code example, as I otherwise would have to guess what you are doing when testing it.

Cheers,
Ferdinand

The example code:

// For https://plugincafe.maxon.net/topic/13782

#include "c4d_basedocument.h"
#include "c4d_file.h"

static maxon::Result<void> pc13782(BaseDocument* doc)
{
    iferr_scope;

    // Iterate over some path strings.
    for (const maxon::String& path : {R"(E:\temp\databases\cache)"_s,
                                                R"(E:/temp/databases/cache)"_s})
    {
        // Convert the string to an url, see the Url manual for details.
        const maxon::Url url(path);
        ApplicationOutput("path: @, url: @", path, url);
    }

    // The output will be (the file scheme of UrlInterface will normalize the inputs):
    //
    //   path: E:\temp\databases\cache, url: file:///E:/temp/databases/cache
    //   path: E:/temp/databases/cache, url: file:///E:/temp/databases/cache

    // There is also MaxonConvert, which can be used to convert between some classic API and related
    // maxon API types. LoadFile expects a FileName as its only argument.
    const maxon::Url someUrl("C:\\somefile.c4d"_s);
    LoadFile(MaxonConvert(someUrl));

    // Which also works the other way around, but here are different modes to choose from. See 
    // documentation of MaxonConvert for details.
    const Filename file("C:\\somefile.c4d"_s);
    const maxon::Url anotherUrl = MaxonConvert(file, MAXONCONVERTMODE::READ);

    return maxon::OK;
}

Hello @ferdinand thank you for your response.

You have also tagged your posting as maxon API although the types you are mentioning, HyperFile and FileName, are both classic API. What are the maxon API types you are using?

Yes, it should have been classic api and not maxon api.

Your question is a bit hard to answer in this form. I assume you are in some kind of NodeData plugin and must serialize and deserialize some data manually with NodeData::Read and NodeData::Write. In this context you are encountering the described behavior of Windows 'changing' the path delimiters written on MacOS.

I guess that answers my question.

But just in case, here is a simplified version of the code that hopefully illustrates better what is done:

class MyData: public iCustomDataType<MyData>
{
	Filename _assetPath;
public:
    Bool Read(HyperFile* hf, Int32 level) {
      hf->ReadFilename(&_assetPath);
     // inpsecting _assetPath and _assetPath.GetFile()

      return TRUE; 
    }

    Bool Write(HyperFile* hf, Int32 level) {
      // inpsecting _assetPath
      hf->WriteFilename(_assetPath);
      return TRUE; 
    }
};

class MyDataDataType : public CustomDataTypeClass
{
  INSTANCEOF(MyDataDataType , CustomDataTypeClass)

public:
  virtual Bool WriteData(const CustomDataType* d, HyperFile* hf) {
    return static_cast<const MyData*>(d)->Write(hf);
  }
	
  virtual Bool ReadData(CustomDataType* d, HyperFile* hf, Int32 level) {
    return static_cast<MyData*>(d)->Read(hf, level);
  }  
};

So yes WriteFilename and ReadFilename are called for the serialization/deserialize process.

For completeness a slightly rephrased description of the observation from the initial question:

Project created on windows:

  • WriteData is called and _assetPath - that contains C:\Program Files\MyAssets\somefile.dat (when inspecting it in Write) - is written.
  • opening that same project on windows then _assetPath is again C:\Program Files\MyAssets\somefile.dat when inspected in Read and _assetPath.GetFile() returns somefile.dat.
  • copying that project to macOS and opening it there _assetPath is also C:\Program Files\MyAssets\somefile.dat and _assetPath.GetFile() returns C:\Program Files\MyAssets\somefile.dat.

Project created on macOS:

  • WriteData is called and _assetPath - that contains /Library/Application Support/MyAssets/somefile.dat when inspecting it in Write- is written.
  • opening that same project on macOS then _assetPath is again /Library/Application Support/MyAssets/somefile.dat when inspected in Read and _assetPath.GetFile() returns somefile.dat.
  • copying that project to windows and opening it there_assetPath it now \Library\Application Support\MyAssets\somefile.dat and _assetPath.GetFile() returns somefile.dat.

But based on your answer I think this behavior is expected.

Kind regards,
Till

Hello @till-niese,

I did not say or want to imply that this is expected, I will have to test it myself. In the meantime, you can use maxon::Url as shown above to normalize paths if that is of importance for you. I will report back here when I have tried replicating your findings. When there is a bug, we will have to decide if we will fix it. The classic API is a dormant API and the bug, if it is there, does not seem to critical. I cannot tell you right away "that is a bug, and we will fix it (or not)", as this involves multiple operating systems and serialization of paths, which both can be tricky subjects.

Cheers,
Ferdinand