Now that we understand the outer structure of the file, it's time to look closer to what's inside. The DllDirectory stream looks like a good starting point. After cleaning up a whole bunch of code to make it easier to plug in specialized handling code, a nice and readable output of this structure shows up as follows:

  1. DllDirectory
  2. (StorageContainer) [20] {
  3. 0 0x21c0: (StorageRaw) {
  4. Size: 4
  5. String: ....
  6. Hex: d8 00 e0 2e }
  7. 1 0x2038: (StorageContainer) [2] {
  8. 0 0x2039: (StorageRaw) {
  9. Size: 78
  10. String: V.i.e.w.p.o.r.t. .M.a.n.a.g.e.r. .f.o.r. .D.i.r.e.c.t.X. .(.A.u.t.o.d.e.s.k.). }
  11. 1 0x2037: (StorageRaw) {
  12. Size: 38
  13. String: V.i.e.w.p.o.r.t.M.a.n.a.g.e.r...g.u.p. } }
  14. 2 0x2038: (StorageContainer) [2] {
  15. 0 0x2039: (StorageRaw) {
  16. Size: 98
  17. String: m.e.n.t.a.l. .r.a.y.:. .M.a.t.e.r.i.a.l. .C.u.s.t.o.m. .A.t.t.r.i.b.u.t.e.s. .(.A.u.t.o.d.e.s.k.). }
  18. 1 0x2037: (StorageRaw) {
  19. Size: 42
  20. String: m.r.M.a.t.e.r.i.a.l.A.t.t.r.i.b.s...g.u.p. } }
  21. ...
  22. 19 0x2038: (StorageContainer) [2] {
  23. 0 0x2039: (StorageRaw) {
  24. Size: 54
  25. String: B.i.p.e.d. .C.o.n.t.r.o.l.l.e.r. .(.A.u.t.o.d.e.s.k.). }
  26. 1 0x2037: (StorageRaw) {
  27. Size: 18
  28. String: b.i.p.e.d...d.l.c. } } }

Thanks to the chunks, most of this is self-explanatory, and fairly easy to handle. The 0x21c0 chunk seems to be a header chunk for the DllDirectory, so we can call it DllHeader or something similar, and contains 4 bytes. This chunk is found in a version 2010 file, but doesn't seem to exist in files from version 3, so it's probably not crucial to handle and we can ignore it's contents until it seems we need it for something. The rest of the chunks in this container are all of id 0x2038, and are the entries in this list, so they are called DllEntry. Inside each of these, there is a 0x2039 chunk containing a description, and a 0x2037 chunk containing a name, both in UTF-16.

The meaning of chunk identifiers depends on the parent container chunk, so we have to code it like that as well. Each container type is set up with a handler to create the class that handles a chunk of a specified identifier. Parsing this block with some smarter code results in a data reading that looks as follows:

  1. (DllDirectory) [20] {
  2. 0 0x21c0: (CStorageValue) { 786432216 }
  3. 1 0x2038: (DllEntry) [2] {
  4. 0 0x2039: (CStorageValue) { Viewport Manager for DirectX (Autodesk) }
  5. 1 0x2037: (CStorageValue) { ViewportManager.gup } }
  6. 2 0x2038: (DllEntry) [2] {
  7. 0 0x2039: (CStorageValue) { mental ray: Material Custom Attributes (Autodesk) }
  8. 1 0x2037: (CStorageValue) { mrMaterialAttribs.gup } }
  9. ...
  10. 19 0x2038: (DllEntry) [2] {
  11. 0 0x2039: (CStorageValue) { Biped Controller (Autodesk) }
  12. 1 0x2037: (CStorageValue) { biped.dlc } } }

Next, we do a similar thing for the ClassDirectory3 stream.

  1. ClassDirectory3
  2. (StorageContainer) [57] {
  3. 0 0x2040: (StorageContainer) [2] {
  4. 0 0x2060: (StorageRaw) {
  5. Size: 16
  6. String: ................
  7. Hex: ff ff ff ff 82 00 00 00 00 00 00 00 82 00 00 00 }
  8. 1 0x2042: (StorageRaw) {
  9. Size: 22
  10. String: P.a.r.a.m.B.l.o.c.k.2. } }
  11. 1 0x2040: (StorageContainer) [2] {
  12. 0 0x2060: (StorageRaw) {
  13. Size: 16
  14. String: ....<).Z..B0`...
  15. Hex: 00 00 00 00 3c 29 06 5a 1e 0c 42 30 60 11 00 00 }
  16. 1 0x2042: (StorageRaw) {
  17. Size: 30
  18. String: V.i.e.w.p.o.r.t.M.a.n.a.g.e.r. } }
  19. ...
  20. 8 0x2040: (StorageContainer) [2] {
  21. 0 0x2060: (StorageRaw) {
  22. Size: 16
  23. String: ................
  24. Hex: 03 00 00 00 02 00 00 00 00 00 00 00 00 0c 00 00 }
  25. 1 0x2042: (StorageRaw) {
  26. Size: 16
  27. String: S.t.a.n.d.a.r.d. } }
  28. ...
  29. 13 0x2040: (StorageContainer) [2] {
  30. 0 0x2060: (StorageRaw) {
  31. Size: 16
  32. String: ....._.d..+"....
  33. Hex: fe ff ff ff ec 5f c7 64 b9 9e 2b 22 00 0c 00 00 }
  34. 1 0x2042: (StorageRaw) {
  35. Size: 24
  36. String: N.e.L. .M.a.t.e.r.i.a.l. } }
  37. ...
  38. 56 0x2040: (StorageContainer) [2] {
  39. 0 0x2060: (StorageRaw) {
  40. Size: 16
  41. String: ....""..........
  42. Hex: ff ff ff ff 22 22 00 00 00 00 00 00 00 01 00 00 }
  43. 1 0x2042: (StorageRaw) {
  44. Size: 10
  45. String: S.c.e.n.e. } } }

This container does not seem to have a header chunk, but again simply contains a whole bunch of entries of id 0x2040, containing a binary blob with id 0x2060 and a UTF-16 string with id 0x2042 that has a description. There's a block in here with some data that I can recognize and reference from our own code. The NeL Material, which is a MAXScript, has a class id of (0x64c75fec, 0x222b9eb9) which matches the middle 8 bytes of the 16 byte blob (read them backwards). The last four bytes in the blob match with the last four bytes in the Standard (material) class entry, and appear to be the SuperClassID. When we look closer at the first four bytes, this appears to be a signed integer, given that there's both ff ff ff as 00 00 00 numbers without too much inbetween. For the NeL Material, which is a script, this value is -2, cross-referencing with other max files with scripted classes reveals the same. Builtin types, such as Scene, have this number as -1. Classes that come from plugins, such as ViewPortManager, have a positive value. Even closer inspection reveals that this value matches with the index of the associated dll in the DllDirectory, ViewPortManager being part of ViewPortManager.gup, and Standard being part of mtl.dlt. It can be expected that the indices of the classes in this list will be needed later on as well. A smarter parsing output looks as follows:

  1. (ClassDirectory3) [57] {
  2. 0 0x2040: (ClassEntry) [2] {
  3. 0 0x2060: (ClassDirectoryHeader) {
  4. DllIndex: -1
  5. ClassID: (0x00000000, 0x00000082)
  6. SuperClassID: 130 }
  7. 1 0x2042: (CStorageValue) { ParamBlock2 } }
  8. 1 0x2040: (ClassEntry) [2] {
  9. 0 0x2060: (ClassDirectoryHeader) {
  10. DllIndex: 0
  11. ClassID: (0x30420c1e, 0x5a06293c)
  12. SuperClassID: 4448 }
  13. 1 0x2042: (CStorageValue) { ViewportManager } }
  14. ...
  15. 56 0x2040: (ClassEntry) [2] {
  16. 0 0x2060: (ClassDirectoryHeader) {
  17. DllIndex: -1
  18. ClassID: (0x00000000, 0x00002222)
  19. SuperClassID: 256 }
  20. 1 0x2042: (CStorageValue) { Scene } } }

The ClassData stream is very similar, and seems to contain a global data storage for classes, or something in that style. It doesn't seem to have anything in it that interests me or seems crucial at this point, so I won't bother with it too much for now. It's fairly self-explanatory.

  1. (ClassData) [7] {
  2. 0 0x2100: (ClassDataEntry) [2] {
  3. 0 0x2110: (ClassDataHeader) {
  4. ClassID: (0xbe7c7e52, 0x87d987f4)
  5. SuperClassID: 16 }
  6. 1 0x2120: (StorageRaw) {
  7. Size: 0
  8. String:
  9. Hex: } }
  10. ...
  11. 4 0x2100: (ClassDataEntry) [2] {
  12. 0 0x2110: (ClassDataHeader) {
  13. ClassID: (0x33b673a4, 0x44b50d1e)
  14. SuperClassID: 4128 }
  15. 1 0x2120: (StorageContainer) [14] {
  16. 0 0x0190: (StorageRaw) {
  17. Size: 48
  18. String: ...................=...=.......@.z.B.......@...=
  19. Hex: 00 00 00 00 00 00 00 00 1f 1c c1 c3 01 00 00 00 cd cc cc 3d cd cc cc 3d 00 00 00 00 cf f7 7b 40 e1 7a 1d 42 01 00 00 00 00 00 a0 40 cd cc cc 3d }
  20. 1 0x019c: (StorageRaw) {
  21. Size: 72
  22. String: ...................?...@.......@.z.B.......@...=...=.@.E...=..........HC
  23. Hex: 00 00 00 00 00 00 00 00 1f 1c c1 c3 01 00 00 00 00 00 80 3f 00 00 a0 40 00 00 00 00 cf f7 7b 40 e1 7a 1d 42 01 00 00 00 00 00 a0 40 cd cc cc 3d cd cc cc 3d 00 40 9c 45 cd cc cc 3d 01 00 00 00 01 00 00 00 00 00 48 43 }
  24. ...

So far, this was easy. After this comes the real stuff.

【转】http://blog.kaetemi.be/post/2012/08/19/3ds-Max-File-Format-%28Part-2%29

3ds Max File Format (Part 2: The first inner structures; DllDirectory, ClassDirectory3)的更多相关文章

  1. 3ds Max File Format (Part 1: The outer file format; OLE2)

    The 3ds Max file format, not too much documentation to be found about it. There are some hints here ...

  2. 3ds Max File Format (Part 5: How it all links together; ReferenceMaker, INode)

    At this point, you should start to familiarize yourself a bit with the publicly available 3ds Max AP ...

  3. 3ds Max File Format (Part 3: The department of redundancy department; Config)

    Now we'll have a look at the Config stream. It begins like follows, and goes on forever with various ...

  4. 3ds Max File Format (Part 4: The first useful data; Scene, AppData, Animatable)

    The most interesting part of this file is, evidently, the Scene. Opening it up in the chunk parser, ...

  5. 3ds Max File Format (Part 6: We get signal)

    Let's see what we can do now. INode *node = scene.container()->scene()->rootNode()->find(uc ...

  6. AVEVA PDMS to 3ds Max - RvmTranslator6.0beta

    AVEVA PDMS to 3ds Max - RvmTranslator6.0beta eryar@163.com RvmTranslato6.0 translate PDMS RVM to 3ds ...

  7. VRay 2.0 SP1 2.10.01 for 3ds max 9/2008/2009/2010/2011/2012 32/64位 顶渲简体中文版+英文版[中国室内设计论坛-室内人]

    VRay 2.0 SP1 2.10.01 for 3ds max 9/2008/2009/2010/2011/2012 32/64位 顶渲简体中文版+英文版[中国室内设计论坛-室内人] 对最新版本的V ...

  8. 万圣节福利:红孩儿3D引擎开发课程《3ds max导出插件初步》

    ds max文件夹,插件文件夹以及3ds max的可执行程序文件夹: 位的,这里要改成x64,否则启动程序后3ds max会提示"不是有效的win32程序"之类的对话框. 然后要将 ...

  9. 【Unity】3.3 用3ds Max 2015制作模型并将其导入到Unity

    分类:Unity.C#.VS2015 创建日期:2016-04-05 一.常用三维软件简介 由于游戏引擎本身的建模功能相对较弱,无论是专业性还是自由度都无法同专业的三维软件相比,所以大多数游戏中的模型 ...

随机推荐

  1. #《Essential C++》读书笔记# 第三章 泛型编程风格

    基础知识 array与vector是连续存储空间,可以用指针的算术运算实现对容器的访问.list也是一个容器,不同的是,list的元素以一组指针相互链接(linked):前向(forward)指针指向 ...

  2. Android开发中按钮的语法

    按钮的主要作用就是触发一个动作,所以会用到监听器. 如何为按钮添加单机事件监听器: 1.匿名内部类作为单机事件监听器 案例: 首先在.xml文件中添加一个按钮一,然后设置其id属性,然后在main里获 ...

  3. C#设计模式学习笔记:(3)抽象工厂模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...

  4. C# Excel导出超出65536行报错 Invalid row number (65536) outside allowable range (0..65535)

    C# Excel导出超出65536行报错 Invalid row number (65536) outside allowable range (0..65535) 一:报错 Invalid row ...

  5. 二维数组 A[m][n] 按行优先和按列优先的 下标地址转换公式

    设二维数组 A[m][n] 按行优先存储, 每个元素占 p 个字节, 则 Loc(i, j) 的地址为 (i * n + m) * p, 第 i 行前面有 i 行, 每行有 n 个元素, 加上 第 i ...

  6. 快捷使用 Iterm2 连接SSH ( HTTP代理 )

    1,配置iterm2 > Preferences.. > Profiles > 填写:name : 别名 : Command : expect /Users/jerryxu/wwwr ...

  7. web攻击与防御

    攻击方式 利用输出值转义漏洞 跨站脚本攻击(XSS) SQL注入攻击 OS命令注入攻击 HTTP首部注入攻击 邮件首部注入攻击 文件目录遍历攻击 利用设置或设计缺陷 强制游览 开放重定向 不正确的错误 ...

  8. python 字典 day6

    字典 :键与值用:分开   项与项用,分开 特点:key-value结构‘ key为不可变数据类型,key必须唯一 可以任意存放多个value值 ,可以不唯一,可修改 无序 查询速度极快  且不受di ...

  9. Educational Codeforces Round 80 (Rated for Div. 2)(A-E)

    C D E 这三道题感觉挺好       决定程序是否能通过优化在要求的时间内完成,程序运行时间为t,你可以选择花X天来优化,优化后程序的运行时间为t/(x+1)取上整,花费的时间为程序运行时间加上优 ...

  10. vue项目中使用element ui上传图片到七牛

    1.获取token值 后台有接口调用直接返回token值 //请求后台拿七牛云token async getQiniuToken() { //token let uploadtoken = await ...