UNSOLVED Find C4D's directory without C4D

Hello PluginCafe :)

I'm writing a standalone application which will be used for installing my C4D plugins.
I need to find an appropriate way of finding plugins directory.

Currently, I'm using a simple expression to define the preferences folder.
Then I'm iterating through each file in the preferences folder and then creating plugins directory with os module.

here is the code.

import os
from getpass import getuser as user

def plugins_path(release_number):
    if os.name == 'nt': MAXON = r"C:\Users\{}\AppData\Roaming\MAXON".format(user()) #Windows
    if os.name != 'nt': MAXON = r"/Users/{}/Library/Preferences/MAXON".format(user()) #OSX

    plugins_path = ""
    for i in os.listdir(MAXON):
        if release_number.upper() in i.upper(): plugins_path = os.path.join(MAXON, i, "plugins")

    if os.path.exists(plugins_path): return plugins_path

user_input = "R20"

The problem is that some users are installing C4D in a custom directory and my code is very limited for this task.

Is it possible to find an exact directory of C4D's plugins folder?

Hi @merkvilson thanks for reaching us,

Here is a script to retrieves the Cinema 4D preference folder of a specific installation stored in temp folder working from R17 to R20.

Copyright: MAXON Computer GmbH
Author: Maxime Adam

    - Retrieves the path of the preference folder according to an installation path without the need to launch Cinema 4D.

    - From Cinema 4D c4d.storage.GeGetC4DPath(c4d.C4D_PATH_STARTUPWRITE) can be called to retrieves the current installation path.

    - Win / Mac
    - R17, R18, R19, R20
import os
import platform

def CRC(rawString):
    # Performs CRC 32 BIT on the input byte
    crc = 0xffffffff

    crc32tab_o32 = [
        0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
        0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
        0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
        0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
        0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
        0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
        0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
        0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
        0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
        0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
        0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
        0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
        0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
        0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
        0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
        0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
        0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
        0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
        0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
        0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
        0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
        0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
        0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
        0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
        0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
        0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
        0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
        0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
        0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
        0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
        0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
        0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351]

    for s in rawString:
        k = ord(s)
        crc = crc32tab_o32[(crc & 0xff) ^ (k & 0xff)] ^ (crc >> 8)
        k >>= 8
        crc = crc32tab_o32[(crc & 0xff) ^ (k & 0xff)] ^ (crc >> 8)
        k >>= 8
        crc = crc32tab_o32[(crc & 0xff) ^ (k & 0xff)] ^ (crc >> 8)
        k >>= 8
        crc = crc32tab_o32[(crc & 0xff) ^ (k & 0xff)] ^ (crc >> 8)

    return format(crc ^ 0xffffffff, 'x').upper()

def GetTempFolder(instalDir):
    # Retrieves the temp folder
    tempFolder = None
    if platform.system() == "Windows":
        tempFolder = os.getenv('APPDATA')
    elif platform.system() == "Darwin":
        libPath = os.path.expanduser('~/Library')
        tempFolder = os.path.join(libPath, "Preferences")

    if not os.path.exists(tempFolder) or tempFolder is None:
        raise RuntimeError("Failed to retrieves appdata folder.")

    # Cleanups the input path
    instalDir = os.path.normpath(instalDir)
    instalDir = instalDir.replace('\\\\', '\\')
    parentDirLower = instalDir.lower()

    # Retrieves only the name of the last folder
    parentName = os.path.split(instalDir)[1]
    if not parentName:
        raise RuntimeError("Failed to retrieves parent Name folder")

    parentName = parentName.replace(':', '_')

    # Computes the crc of the dir in lowercase.
    crc = CRC(parentDirLower)

    # Builds the final path
    finalPath = os.path.join(tempFolder, "MAXON", parentName + "_" + crc)

    return finalPath

def main():
    # Path must be in unicode, it can be retrieved from c4d with c4d.storage.GeGetStartupPath()
    winInstallationPath = unicode("C:\Program Files\MAXON\Cinema 4D R20")
    macInstallationPath = unicode("/Users/m_adam/Application/MAXON/Cinema 4D R20")
    print "Path:", GetTempFolder(winInstallationPath)

if __name__ == '__main__':

As a side note in R20 plugins can be installed everywhere as long as the path is set in the preferences.
And finally, there is no reliable way to know where Cinema 4D is installed from outside of Cinema 4D.

If you have any question, please let me know.

Thanks, Maxime!

Can you tell me about Copyright and EULA? How can I use this code in my software?

@merkvilson, sorry this code is planned to be part of our github, so like any of our examples, they are under the Apache License.

For more information see Apache License from our Github repository.

Thanks, Maxime! :)

I tested the code, and it works.
But this is not what I exactly wanted.

As I mentioned, the problem is that some users are installing C4D in a custom directory and I can't find preferences path nor an actual installation path.

I also tried os.walk() Method to find MAXON directory, but it has two significant problems.

  1. It's too slow
  2. there are several folders called MAXON

The only thing that users can define is the release number, and I have to find it according to the string, for example, "R20".

Here is how the installer works.

alt text

It works on most of the computers but some users already reported that it can't find their directory so they are forced to manually define it but most of them are defining global directory. If you remember, I had to remove symbolcache file and this is one of the reasons why I made this installer.

Unfortunately, Cinema 4D does not track this, and there is no way to know it currently since Cinema 4D is almost a portable application (you can copy/paste Cinema 4D folder to anywhere as long as dependencies have been installed once it will work).

In any case, it's a request we already made to our development team, I'll push the topic again and will see 😉.

Thanks, Maxime! This script is still quite useful.
Is it possible to use it in order to find C4D executable on Windows as well as on mac?

Let's assume the directory is C:\Program Files\MAXON\Cinema 4D R20\ which contains several exe. How to define which one is the Cinema 4D itself?

I tried this way but I guess there should be a better option

def MAXON_P():
    if os.name == 'nt': MAXON = r"C:\Program Files\MAXON"
    if os.name != 'nt': MAXON = r"/Applications/MAXON"
    if os.path.exists(MAXON): return MAXON
    else: return ""

def c4d_executable_path(ver):

    user_ver = ""
    versions = os.listdir(MAXON_P())
    for i in versions:
        if ver.upper() in i.upper() and "demo".upper() not in i.upper():
            user_ver = i

    c4d_folder = os.path.join(MAXON_P(), user_ver,)
    files = os.listdir(c4d_folder)

    for file in files:
        if os.name == 'nt' and "CINEMA 4D" in file.upper() and "exe" in file and len(file) < 18: ex = file
        if os.name != 'nt' and "CINEMA 4D" in file.upper() in file and len(file) < 18: ex = file

    return os.path.join(c4d_folder, ex)

print c4d_executable_path("R20")

On Windows, you can use a little trick: To retrieve the currently used Cinema 4D installation, you can query the registry for the program associated with *.c4d files.
This gives you only a single installation - probably the most current one.

Is it possible to use it in order to find C4D executable on Windows as well as on mac?

On Win+Mac, filenames must be unique. So, Cinema 4D.exe/app should be sufficient.

Thanks, buddy! :)
But the problem is that the most current one is not a way to go.

If the user installed the plugin for R16, then the installer should open R16 itself.

I'm trying to find and open it by the name, but the name is always different. It can be Cinema 4D / Cinema 4D R16 / Cinema 4D R16 Demo / Cinema 4D Demo / etc.
Well. This is considerably easier on windows since I can check if it exists by using os.path.exists() function, but I have no Idea how to do this on Mac.