mirror of
https://github.com/cocos/cocos-engine.git
synced 2026-09-09 00:58:50 +08:00
1224 lines
41 KiB
TypeScript
1224 lines
41 KiB
TypeScript
/*
|
|
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
|
|
|
https://www.cocos.com
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated engine source code (the "Software"), a limited,
|
|
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
|
to use Cocos Creator solely to develop games on your target platforms. You shall
|
|
not use Cocos Creator software for developing other software or tools that's
|
|
used for developing games. You are not granted to publish, distribute,
|
|
sublicense, and/or sell copies of Cocos Creator.
|
|
|
|
The software or tools in this License Agreement are licensed, not sold.
|
|
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
|
|
import { EDITOR, TEST, PREVIEW, BUILD, DEBUG, JSB, DEV } from 'internal:constants';
|
|
import { legacyCC } from '../global-exports';
|
|
import { ValueType } from '../value-types';
|
|
import { Vec2, Vec3, Vec4, Color, Size, Rect, Quat, Mat4 } from '../math';
|
|
import { warnID, errorID, getError } from '../platform/debug';
|
|
import * as js from '../utils/js';
|
|
|
|
import { deserializeDynamic, parseUuidDependenciesDynamic } from './deserialize-dynamic';
|
|
import { Asset } from '../assets/asset';
|
|
|
|
import { deserializeTag } from './custom-serializable';
|
|
import type { CCON } from './ccon';
|
|
import { reportMissingClass as defaultReportMissingClass } from './report-missing-class';
|
|
import type { CompiledDeserializeFn } from './deserialize-dynamic';
|
|
|
|
const FORCE_COMPILED = false; // TODO: BUILD;
|
|
|
|
/** **************************************************************************
|
|
* BUILT-IN TYPES / CONSTAINTS
|
|
*************************************************************************** */
|
|
|
|
const SUPPORT_MIN_FORMAT_VERSION = 1;
|
|
const EMPTY_PLACEHOLDER = 0;
|
|
|
|
// Used for Data.ValueType.
|
|
// If a value type is not registered in this list, it will be serialized to Data.Class.
|
|
const BuiltinValueTypes: (typeof ValueType)[] = [
|
|
Vec2, // 0
|
|
Vec3, // 1
|
|
Vec4, // 2
|
|
Quat, // 3
|
|
Color, // 4
|
|
Size, // 5
|
|
Rect, // 6
|
|
Mat4, // 7
|
|
];
|
|
|
|
// Used for Data.ValueTypeCreated.
|
|
function BuiltinValueTypeParsers_xyzw (obj: Vec4 | Quat, data: number[]) {
|
|
obj.x = data[1];
|
|
obj.y = data[2];
|
|
obj.z = data[3];
|
|
obj.w = data[4];
|
|
}
|
|
const BuiltinValueTypeSetters: ((obj: any, data: number[]) => void)[] = [
|
|
(obj: Vec2, data: number[]) => {
|
|
obj.x = data[1];
|
|
obj.y = data[2];
|
|
},
|
|
(obj: Vec3, data: number[]) => {
|
|
obj.x = data[1];
|
|
obj.y = data[2];
|
|
obj.z = data[3];
|
|
},
|
|
BuiltinValueTypeParsers_xyzw, // Vec4
|
|
BuiltinValueTypeParsers_xyzw, // Quat
|
|
(obj: Color, data: number[]) => {
|
|
obj._val = data[1];
|
|
},
|
|
(obj: Size, data: number[]) => {
|
|
obj.width = data[1];
|
|
obj.height = data[2];
|
|
},
|
|
(obj: Rect, data: number[]) => {
|
|
obj.x = data[1];
|
|
obj.y = data[2];
|
|
obj.width = data[3];
|
|
obj.height = data[4];
|
|
},
|
|
(obj: Mat4, data: number[]) => {
|
|
Mat4.fromArray(obj, data, 1);
|
|
},
|
|
];
|
|
|
|
function serializeBuiltinValueTypes (obj: ValueType): IValueTypeData | null {
|
|
const ctor = obj.constructor as typeof ValueType;
|
|
const typeId = BuiltinValueTypes.indexOf(ctor);
|
|
switch (ctor) {
|
|
case Vec2:
|
|
// @ts-expect-error Complex typing
|
|
return [typeId, obj.x, obj.y];
|
|
case Vec3:
|
|
// @ts-expect-error Complex typing
|
|
return [typeId, obj.x, obj.y, obj.z];
|
|
case Vec4:
|
|
case Quat:
|
|
// @ts-expect-error Complex typing
|
|
return [typeId, obj.x, obj.y, obj.z, obj.w];
|
|
case Color:
|
|
// @ts-expect-error Complex typing
|
|
return [typeId, obj._val];
|
|
case Size:
|
|
// @ts-expect-error Complex typing
|
|
return [typeId, obj.width, obj.height];
|
|
case Rect:
|
|
// @ts-expect-error Complex typing
|
|
return [typeId, obj.x, obj.y, obj.width, obj.height];
|
|
case Mat4: {
|
|
// @ts-expect-error Complex typing
|
|
const res: IValueTypeData = new Array<number>(1 + 16);
|
|
res[VALUETYPE_SETTER] = typeId;
|
|
Mat4.toArray(res, obj as Mat4, 1);
|
|
return res;
|
|
}
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// // TODO: Used for Data.TypedArray.
|
|
// const TypedArrays = [
|
|
// Float32Array,
|
|
// Float64Array,
|
|
//
|
|
// Int8Array,
|
|
// Int16Array,
|
|
// Int32Array,
|
|
//
|
|
// Uint8Array,
|
|
// Uint16Array,
|
|
// Uint32Array,
|
|
//
|
|
// Uint8ClampedArray,
|
|
// // BigInt64Array,
|
|
// // BigUint64Array,
|
|
// ];
|
|
|
|
/** **************************************************************************
|
|
* TYPE DECLARATIONS
|
|
*************************************************************************** */
|
|
|
|
// Includes Bitwise NOT value.
|
|
// Both T and U have non-negative integer ranges.
|
|
// When the value >= 0 represents T
|
|
// When the value is < 0, it represents ~U. Use ~x to extract the value of U.
|
|
type Bnot<T extends number, U extends number> = T|U;
|
|
|
|
// Combines a boolean and a number into one value.
|
|
// The number must >= 0.
|
|
// When the value >= 0, the boolean is true, the number is value.
|
|
// When the value < 0, the boolean is false, the number is ~value.
|
|
type BoolAndNum<B extends boolean, N extends number> = Bnot<N, N>;
|
|
|
|
type SharedString = string;
|
|
type Empty = typeof EMPTY_PLACEHOLDER;
|
|
type StringIndex = number;
|
|
type InstanceIndex = number;
|
|
type RootInstanceIndex = InstanceIndex;
|
|
type NoNativeDep = boolean; // Indicates whether the asset depends on a native asset
|
|
type RootInfo = BoolAndNum<NoNativeDep, RootInstanceIndex>;
|
|
|
|
// When the value >= 0 represents the string index
|
|
// When the value is < 0, it just represents non-negative integer. Use ~x to extract the value.
|
|
type StringIndexBnotNumber = Bnot<StringIndex, number>;
|
|
|
|
// A reverse index used to assign current parsing object to target command buffer so it could be assembled later.
|
|
// Should >= REF.OBJ_OFFSET
|
|
type ReverseIndex = number;
|
|
|
|
// Used to index the current object
|
|
type InstanceBnotReverseIndex = Bnot<InstanceIndex, ReverseIndex>;
|
|
|
|
// shared with the editor
|
|
export declare namespace deserialize.Internal {
|
|
export type SharedString_ = SharedString;
|
|
export type Empty_ = Empty;
|
|
export type StringIndex_ = StringIndex;
|
|
export type InstanceIndex_ = InstanceIndex;
|
|
export type StringIndexBnotNumber_ = StringIndexBnotNumber;
|
|
}
|
|
|
|
const enum DataTypeID {
|
|
|
|
// Fields that can be assigned directly, can be values in any JSON, or even a complex JSON array, object (no type).
|
|
// Contains null, no undefined, JSON does not support serialization of undefined.
|
|
// This is the only type that supports null, and all other advanced fields are forbidden with null values.
|
|
// If the value of an object is likely to be null, it needs to exist as a new class,
|
|
// but the probability of this is very low and will be analyzed below.
|
|
SimpleType = 0,
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Except Simple, the rest belong to Advanced Type.
|
|
|
|
// Rarely will it be NULL, as NULL will be dropped as the default value.
|
|
InstanceRef,
|
|
|
|
// Arrays of exactly equal types.
|
|
// Arrays will have default values that developers will rarely assign to null manually.
|
|
Array_InstanceRef,
|
|
Array_AssetRefByInnerObj,
|
|
|
|
// Embedded object
|
|
// Rarely will it be NULL, as NULL will be dropped as the default value.
|
|
Class,
|
|
|
|
// Existing ValueType (created by the Class constructor).
|
|
// Developers will rarely manually assign a null.
|
|
ValueTypeCreated,
|
|
|
|
// Resource reference for embedded objects (such as arrays), the value is the index of DEPEND_OBJS.
|
|
// (The objects in INSTANCES do not need to dynamically resolve resource reference relationships, so there is no need to have the AssetRef type.)
|
|
AssetRefByInnerObj,
|
|
|
|
// Common TypedArray for legacyCC.Node only. Never be null.
|
|
TRS,
|
|
|
|
// // From the point of view of simplified implementation,
|
|
// // it is not supported to deserialize TypedArray that is initialized to null in the constructor.
|
|
// // Also, the length of TypedArray cannot be changed.
|
|
// // Developers will rarely manually assign a null.
|
|
// TypedArray,
|
|
|
|
// ValueType without default value (in arrays, dictionaries).
|
|
// Developers will rarely manually assign a null.
|
|
ValueType,
|
|
|
|
Array_Class,
|
|
|
|
// CustomizedClass embedded in Class
|
|
CustomizedClass,
|
|
|
|
// Universal dictionary with unlimited types of values (except TypedArray)
|
|
Dict,
|
|
|
|
// Universal arrays, of any type (except TypedArray) and can be unequal.
|
|
// (The editor doesn't seem to have a good way of stopping arrays of unequal types either)
|
|
Array,
|
|
|
|
ARRAY_LENGTH,
|
|
}
|
|
|
|
export declare namespace deserialize.Internal {
|
|
export import DataTypeID_ = DataTypeID;
|
|
export type DataTypes_ = DataTypes;
|
|
}
|
|
|
|
interface DataTypes {
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
[DataTypeID.SimpleType]: number | string | boolean | null | object;
|
|
[DataTypeID.InstanceRef]: InstanceBnotReverseIndex;
|
|
[DataTypeID.Array_InstanceRef]: DataTypes[DataTypeID.InstanceRef][];
|
|
[DataTypeID.Array_AssetRefByInnerObj]: DataTypes[DataTypeID.AssetRefByInnerObj][];
|
|
[DataTypeID.Class]: IClassObjectData;
|
|
[DataTypeID.ValueTypeCreated]: IValueTypeData;
|
|
[DataTypeID.AssetRefByInnerObj]: number;
|
|
[DataTypeID.TRS]: ITRSData;
|
|
// [DataTypeID.TypedArray]: Array<InstanceOrReverseIndex>;
|
|
[DataTypeID.ValueType]: IValueTypeData;
|
|
[DataTypeID.Array_Class]: DataTypes[DataTypeID.Class][];
|
|
[DataTypeID.CustomizedClass]: ICustomObjectData;
|
|
[DataTypeID.Dict]: IDictData;
|
|
[DataTypeID.Array]: IArrayData;
|
|
}
|
|
|
|
type PrimitiveObjectTypeID = (
|
|
DataTypeID.SimpleType | // SimpleType also includes any pure JSON object
|
|
DataTypeID.Array |
|
|
DataTypeID.Array_Class |
|
|
DataTypeID.Array_AssetRefByInnerObj |
|
|
DataTypeID.Array_InstanceRef |
|
|
DataTypeID.Dict
|
|
);
|
|
|
|
type AdvancedTypeID = Exclude<DataTypeID, DataTypeID.SimpleType>;
|
|
|
|
// Collection of all data types
|
|
type AnyData = DataTypes[keyof DataTypes];
|
|
|
|
type AdvancedData = DataTypes[Exclude<keyof DataTypes, DataTypeID.SimpleType>];
|
|
|
|
type OtherObjectData = ICustomObjectDataContent | Exclude<DataTypes[PrimitiveObjectTypeID], (number|string|boolean|null)>;
|
|
|
|
// class Index of DataTypeID.CustomizedClass or PrimitiveObjectTypeID
|
|
type OtherObjectTypeID = Bnot<number, PrimitiveObjectTypeID>;
|
|
|
|
type Ctor<T> = new() => T;
|
|
// Includes normal CCClass and fast defined class
|
|
export interface CCClassConstructor<T> extends Ctor<T> {
|
|
__values__: string[]
|
|
__deserialize__?: CompiledDeserializeFn;
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
type AnyCtor = Ctor<Object>;
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
type AnyCCClass = CCClassConstructor<Object>;
|
|
|
|
export declare namespace deserialize.Internal {
|
|
export type AnyData_ = AnyData;
|
|
export type OtherObjectData_ = OtherObjectData;
|
|
export type OtherObjectTypeID_ = OtherObjectTypeID;
|
|
export type AnyCCClass_ = AnyCCClass;
|
|
}
|
|
|
|
/**
|
|
* If the value type is different, different Classes will be generated
|
|
*/
|
|
const CLASS_TYPE = 0;
|
|
const CLASS_KEYS = 1;
|
|
const CLASS_PROP_TYPE_OFFSET = 2;
|
|
type IClass = [
|
|
string|AnyCtor,
|
|
string[],
|
|
// offset - It is used to specify the correspondence between the elements in CLASS_KEYS and their AdvancedType,
|
|
// which is only valid for AdvancedType.
|
|
// When parsing, the type of IClass[CLASS_KEYS][x] is IClass[x + IClass[CLASS_PROP_TYPE_OFFSET]]
|
|
// When serializing, IClass[CLASS_PROP_TYPE_OFFSET] = CLASS_PROP_TYPE_OFFSET + 1 - (The number of SimpleType)
|
|
number,
|
|
// The AdvancedType type corresponding to the property.
|
|
...DataTypeID[]
|
|
];
|
|
|
|
/**
|
|
* Mask is used to define the properties and types that need to be deserialized.
|
|
* Instances of the same class may have different Masks due to different default properties removed.
|
|
*/
|
|
const MASK_CLASS = 0;
|
|
type IMask = [
|
|
// The index of its Class
|
|
number,
|
|
// The indices of the property that needs to be deserialized in IClass, except that the last number represents OFFSET.
|
|
// All properties before OFFSET are SimpleType, and those starting at OFFSET are AdvancedType.
|
|
// default is 1
|
|
...number[]
|
|
];
|
|
|
|
const OBJ_DATA_MASK = 0;
|
|
type IClassObjectData = [
|
|
// The index of its Mask
|
|
number,
|
|
// Starting from 1, the values corresponding to the properties in the Mask
|
|
...AnyData[]
|
|
];
|
|
|
|
type ICustomObjectDataContent = any;
|
|
|
|
const CUSTOM_OBJ_DATA_CLASS = 0;
|
|
const CUSTOM_OBJ_DATA_CONTENT = 1;
|
|
interface ICustomObjectData extends Array<any> {
|
|
// The index of its Class
|
|
[CUSTOM_OBJ_DATA_CLASS]: number;
|
|
// Content
|
|
[CUSTOM_OBJ_DATA_CONTENT]: ICustomObjectDataContent;
|
|
}
|
|
|
|
const VALUETYPE_SETTER = 0;
|
|
type IValueTypeData = [
|
|
// Predefined parsing function index
|
|
number,
|
|
// Starting with 1, the corresponding value in the attributes are followed in order
|
|
...number[]
|
|
];
|
|
|
|
type ITRSData = [number, number, number, number, number,
|
|
number, number, number, number, number];
|
|
|
|
const DICT_JSON_LAYOUT = 0;
|
|
interface IDictData extends Array<any> {
|
|
// The raw json object
|
|
[DICT_JSON_LAYOUT]: any,
|
|
// key
|
|
// Shared strings are not considered here, can be defined as CCClass if it is required.
|
|
[1]: string;
|
|
// value type
|
|
// Should not be SimpleType, SimpleType is built directly into DICT_JSON_LAYOUT.
|
|
[2]: AdvancedTypeID;
|
|
// value
|
|
[3]: AdvancedData;
|
|
// More repeated key values
|
|
[index: number]: any,
|
|
}
|
|
|
|
const ARRAY_ITEM_VALUES = 0;
|
|
type IArrayData = [
|
|
AnyData[],
|
|
// types
|
|
...DataTypeID[]
|
|
];
|
|
|
|
export declare namespace deserialize.Internal {
|
|
export type IClass_ = IClass;
|
|
export type IMask_ = IMask;
|
|
export type IClassObjectData_ = IClassObjectData;
|
|
export type ICustomObjectDataContent_ = ICustomObjectDataContent;
|
|
export type ICustomObjectData_ = ICustomObjectData;
|
|
export type ITRSData_ = ITRSData;
|
|
export type IDictData_ = IDictData;
|
|
export type IArrayData_ = IArrayData;
|
|
}
|
|
|
|
// const TYPEDARRAY_TYPE = 0;
|
|
// const TYPEDARRAY_ELEMENTS = 1;
|
|
// interface ITypedArrayData extends Array<number|number[]> {
|
|
// [TYPEDARRAY_TYPE]: number,
|
|
// [TYPEDARRAY_ELEMENTS]: number[],
|
|
// }
|
|
|
|
const enum Refs {
|
|
EACH_RECORD_LENGTH = 3,
|
|
OWNER_OFFSET = 0,
|
|
KEY_OFFSET = 1,
|
|
TARGET_OFFSET = 2,
|
|
}
|
|
|
|
interface IRefs extends Array<number> {
|
|
// owner
|
|
// The owner of all the objects in the front is of type object, starting from OFFSET * 3 are of type InstanceIndex
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
[0]: (object | InstanceIndex),
|
|
// property name
|
|
[1]?: StringIndexBnotNumber;
|
|
// target object
|
|
[2]?: InstanceIndex;
|
|
// All the following objects are arranged in the order of the first three values,
|
|
// except that the last number represents OFFSET.
|
|
[index: number]: any;
|
|
}
|
|
|
|
const enum File {
|
|
Version = 0,
|
|
Context = 0,
|
|
|
|
SharedUuids,
|
|
SharedStrings,
|
|
SharedClasses,
|
|
SharedMasks,
|
|
|
|
Instances,
|
|
InstanceTypes,
|
|
|
|
Refs,
|
|
|
|
DependObjs,
|
|
DependKeys,
|
|
DependUuidIndices,
|
|
|
|
ARRAY_LENGTH,
|
|
}
|
|
|
|
// Main file structure
|
|
interface IFileData extends Array<any> {
|
|
// version
|
|
[File.Version]: number | FileInfo | any;
|
|
|
|
// Shared data area, the higher the number of references, the higher the position
|
|
|
|
[File.SharedUuids]: SharedString[] | Empty; // Shared uuid strings for dependent assets
|
|
[File.SharedStrings]: SharedString[] | Empty;
|
|
[File.SharedClasses]: (IClass|string|AnyCCClass)[];
|
|
[File.SharedMasks]: IMask[] | Empty; // Shared Object layouts for IClassObjectData
|
|
|
|
// Data area
|
|
|
|
// A one-dimensional array to represent object datas, layout is [...IClassObjectData[], ...OtherObjectData[], RootInfo]
|
|
// If the last element is not RootInfo(number), the first element will be the root object to return and it doesn't have native asset
|
|
[File.Instances]: (IClassObjectData|OtherObjectData|RootInfo)[];
|
|
[File.InstanceTypes]: OtherObjectTypeID[] | Empty;
|
|
// Object references infomation
|
|
[File.Refs]: IRefs | Empty;
|
|
|
|
// Result area
|
|
|
|
// Asset-dependent objects that are deserialized and parsed into object arrays
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
[File.DependObjs]: (object|InstanceIndex)[];
|
|
// Asset-dependent key name or array index
|
|
[File.DependKeys]: (StringIndexBnotNumber|string)[];
|
|
// UUID of dependent assets
|
|
[File.DependUuidIndices]: (StringIndex|string)[];
|
|
}
|
|
|
|
// type Body = Pick<IFileData, File.Instances | File.InstanceTypes | File.Refs | File.DependObjs | File.DependKeys | File.DependUuidIndices>
|
|
type Shared = Pick<IFileData, File.Version | File.SharedUuids | File.SharedStrings | File.SharedClasses | File.SharedMasks>;
|
|
const PACKED_SECTIONS = File.Instances;
|
|
interface IPackedFileData extends Shared {
|
|
[PACKED_SECTIONS]: IFileData[];
|
|
}
|
|
|
|
export declare namespace deserialize.Internal {
|
|
export import Refs_ = Refs;
|
|
export type IRefs_ = IRefs;
|
|
export import File_ = File;
|
|
export type IFileData_ = IFileData;
|
|
export type IPackedFileData_ = IPackedFileData;
|
|
}
|
|
|
|
interface ICustomHandler {
|
|
result: Details,
|
|
customEnv: any,
|
|
}
|
|
type ClassFinder = (type: string) => AnyCtor;
|
|
|
|
interface IOptions extends Partial<ICustomHandler> {
|
|
classFinder?: ClassFinder;
|
|
reportMissingClass: deserialize.ReportMissingClass;
|
|
_version?: number;
|
|
}
|
|
interface ICustomClass {
|
|
_deserialize?: (content: any, context: ICustomHandler) => void;
|
|
}
|
|
|
|
/** **************************************************************************
|
|
* IMPLEMENTS
|
|
*************************************************************************** */
|
|
|
|
/**
|
|
* @en Contains information collected during deserialization
|
|
* @zh 包含反序列化时的一些信息。
|
|
* @class Details
|
|
*/
|
|
export class Details {
|
|
/**
|
|
* @en
|
|
* the object list whose field needs to load asset by uuid
|
|
* @zh
|
|
* 对象列表,其中每个对象有属性需要通过 uuid 进行资源加载
|
|
*/
|
|
uuidObjList: IFileData[File.DependObjs] | null = null;
|
|
/**
|
|
* @en
|
|
* the corresponding field name which referenced to the asset
|
|
* @zh
|
|
* 引用着资源的字段名称
|
|
*/
|
|
uuidPropList: IFileData[File.DependKeys] | null = null;
|
|
/**
|
|
* @en
|
|
* list of the depends assets' uuid
|
|
* @zh
|
|
* 依赖资源的 uuid 列表
|
|
*/
|
|
uuidList: IFileData[File.DependUuidIndices] | null = null;
|
|
|
|
/**
|
|
* @en
|
|
* list of the depends assets' type
|
|
* @zh
|
|
* 依赖的资源类型列表
|
|
*/
|
|
uuidTypeList: string[] = [];
|
|
|
|
static pool = new js.Pool((obj: Details) => {
|
|
obj.reset();
|
|
}, 5);
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
public declare assignAssetsBy: Function;
|
|
|
|
/**
|
|
* @method init
|
|
* @param {Object} data
|
|
*/
|
|
init (data?: IFileData) {
|
|
if (FORCE_COMPILED || data) {
|
|
this.uuidObjList = data![File.DependObjs];
|
|
this.uuidPropList = data![File.DependKeys];
|
|
this.uuidList = data![File.DependUuidIndices];
|
|
} else {
|
|
// could be used by deserialize-dynamic
|
|
const used = this.uuidList;
|
|
if (!used) {
|
|
this.uuidList = [];
|
|
this.uuidObjList = [];
|
|
this.uuidPropList = [];
|
|
this.uuidTypeList = [];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @method reset
|
|
*/
|
|
reset () {
|
|
if (FORCE_COMPILED) {
|
|
this.uuidList = null;
|
|
this.uuidObjList = null;
|
|
this.uuidPropList = null;
|
|
} else {
|
|
// could be reused by deserialize-dynamic
|
|
const used = this.uuidList;
|
|
if (used) {
|
|
this.uuidList!.length = 0;
|
|
this.uuidObjList!.length = 0;
|
|
this.uuidPropList!.length = 0;
|
|
this.uuidTypeList.length = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @method push
|
|
* @param {Object} obj
|
|
* @param {String} propName
|
|
* @param {String} uuid
|
|
*/
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
push (obj: object, propName: string, uuid: string, type?: string) {
|
|
this.uuidObjList!.push(obj);
|
|
this.uuidPropList!.push(propName);
|
|
this.uuidList!.push(uuid);
|
|
this.uuidTypeList.push(type || '');
|
|
}
|
|
}
|
|
Details.pool.get = function () {
|
|
return this._get() || new Details();
|
|
};
|
|
if (EDITOR || TEST) {
|
|
Details.prototype.assignAssetsBy = function (getter: (uuid: string, type: Constructor<Asset>) => any) {
|
|
for (let i = 0, len = this.uuidList!.length; i < len; i++) {
|
|
const obj = this.uuidObjList![i];
|
|
const prop = this.uuidPropList![i];
|
|
const uuid = this.uuidList![i];
|
|
const type = this.uuidTypeList[i];
|
|
obj[prop] = getter(uuid as string, js._getClassById(type) as Constructor<Asset> || Asset);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function dereference (refs: IRefs, instances: IFileData[File.Instances], strings: IFileData[File.SharedStrings]): void {
|
|
const dataLength = refs.length - 1;
|
|
let i = 0;
|
|
// owner is object
|
|
const instanceOffset: number = refs[dataLength] * Refs.EACH_RECORD_LENGTH;
|
|
for (; i < instanceOffset; i += Refs.EACH_RECORD_LENGTH) {
|
|
const owner = refs[i];
|
|
|
|
const target = instances[refs[i + Refs.TARGET_OFFSET]];
|
|
const keyIndex = refs[i + Refs.KEY_OFFSET] as StringIndexBnotNumber;
|
|
if (keyIndex >= 0) {
|
|
owner[strings[keyIndex]] = target;
|
|
} else {
|
|
owner[~keyIndex] = target;
|
|
}
|
|
}
|
|
// owner is instance index
|
|
for (; i < dataLength; i += Refs.EACH_RECORD_LENGTH) {
|
|
const owner = instances[refs[i]];
|
|
|
|
const target = instances[refs[i + Refs.TARGET_OFFSET]];
|
|
const keyIndex = refs[i + Refs.KEY_OFFSET] as StringIndexBnotNumber;
|
|
if (keyIndex >= 0) {
|
|
owner[strings[keyIndex]] = target;
|
|
} else {
|
|
owner[~keyIndex] = target;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
|
|
function deserializeCCObject (data: IFileData, objectData: IClassObjectData) {
|
|
const mask = data[File.SharedMasks][objectData[OBJ_DATA_MASK]];
|
|
const clazz = mask[MASK_CLASS];
|
|
const ctor = clazz[CLASS_TYPE] as Exclude<AnyCtor, ICustomClass>;
|
|
// if (!ctor) {
|
|
// return null;
|
|
// }
|
|
|
|
// eslint-disable-next-line new-cap
|
|
const obj = new ctor();
|
|
|
|
const keys = clazz[CLASS_KEYS];
|
|
const classTypeOffset = clazz[CLASS_PROP_TYPE_OFFSET];
|
|
const maskTypeOffset = mask[mask.length - 1];
|
|
|
|
// parse simple type
|
|
let i = MASK_CLASS + 1;
|
|
for (; i < maskTypeOffset; ++i) {
|
|
const key = keys[mask[i]];
|
|
obj[key] = objectData[i];
|
|
}
|
|
|
|
// parse advanced type
|
|
for (; i < objectData.length; ++i) {
|
|
const key = keys[mask[i]];
|
|
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
|
const type = clazz[mask[i] + classTypeOffset];
|
|
const op = ASSIGNMENTS[type];
|
|
op(data, obj, key, objectData[i]);
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
function deserializeCustomCCObject (data: IFileData, ctor: Ctor<ICustomClass>, value: ICustomObjectDataContent) {
|
|
// eslint-disable-next-line new-cap
|
|
const obj = new ctor();
|
|
if (obj._deserialize) {
|
|
obj._deserialize(value, data[File.Context]);
|
|
} else {
|
|
errorID(5303, js.getClassName(ctor));
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
// Parse Functions
|
|
|
|
type ParseFunction<T> = (data: IFileData, owner: any, key: string, value: T) => void;
|
|
|
|
function assignSimple (data: IFileData, owner: any, key: string, value: DataTypes[DataTypeID.SimpleType]) {
|
|
owner[key] = value;
|
|
}
|
|
|
|
function assignInstanceRef (data: IFileData, owner: any, key: string, value: InstanceBnotReverseIndex) {
|
|
if (value >= 0) {
|
|
owner[key] = data[File.Instances][value];
|
|
} else {
|
|
(data[File.Refs] as IRefs)[(~value) * Refs.EACH_RECORD_LENGTH] = owner;
|
|
}
|
|
}
|
|
|
|
function genArrayParser<T> (parser: ParseFunction<T>): ParseFunction<T[]> {
|
|
return (data: IFileData, owner: any, key: string, value: T[]) => {
|
|
owner[key] = value;
|
|
for (let i = 0; i < value.length; ++i) {
|
|
parser(data, value, i as unknown as string, value[i]);
|
|
}
|
|
};
|
|
}
|
|
|
|
function parseAssetRefByInnerObj (data: IFileData, owner: any, key: string, value: number) {
|
|
owner[key] = null;
|
|
data[File.DependObjs][value] = owner;
|
|
}
|
|
|
|
function parseClass (data: IFileData, owner: any, key: string, value: IClassObjectData) {
|
|
owner[key] = deserializeCCObject(data, value);
|
|
}
|
|
|
|
function parseCustomClass (data: IFileData, owner: any, key: string, value: ICustomObjectData) {
|
|
const ctor = data[File.SharedClasses][value[CUSTOM_OBJ_DATA_CLASS]] as CCClassConstructor<ICustomClass>;
|
|
owner[key] = deserializeCustomCCObject(data, ctor, value[CUSTOM_OBJ_DATA_CONTENT]);
|
|
}
|
|
|
|
function parseValueTypeCreated (data: IFileData, owner: any, key: string, value: IValueTypeData) {
|
|
BuiltinValueTypeSetters[value[VALUETYPE_SETTER]](owner[key], value);
|
|
}
|
|
|
|
function parseValueType (data: IFileData, owner: any, key: string, value: IValueTypeData) {
|
|
const val: ValueType = new BuiltinValueTypes[value[VALUETYPE_SETTER]]();
|
|
BuiltinValueTypeSetters[value[VALUETYPE_SETTER]](val, value);
|
|
owner[key] = val;
|
|
}
|
|
|
|
function parseTRS (data: IFileData, owner: any, key: string, value: ITRSData) {
|
|
const typedArray = owner[key] as (Float32Array | Float64Array);
|
|
typedArray.set(value);
|
|
}
|
|
|
|
function parseDict (data: IFileData, owner: any, key: string, value: IDictData) {
|
|
const dict = value[DICT_JSON_LAYOUT];
|
|
owner[key] = dict;
|
|
for (let i = DICT_JSON_LAYOUT + 1; i < value.length; i += 3) {
|
|
const subKey = value[i] as string;
|
|
const subType = value[i + 1] as DataTypeID;
|
|
const subValue = value[i + 2] as AnyData;
|
|
const op = ASSIGNMENTS[subType];
|
|
op(data, dict, subKey, subValue);
|
|
}
|
|
}
|
|
|
|
function parseArray (data: IFileData, owner: any, key: string, value: IArrayData) {
|
|
const array = value[ARRAY_ITEM_VALUES];
|
|
owner[key] = array;
|
|
for (let i = 0; i < array.length; ++i) {
|
|
const subValue = array[i];
|
|
const type = value[i + 1] as DataTypeID;
|
|
if (type !== DataTypeID.SimpleType) {
|
|
const op = ASSIGNMENTS[type];
|
|
op(data, array, i, subValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
// function parseTypedArray (data: IFileData, owner: any, key: string, value: ITypedArrayData) {
|
|
// let val: ValueType = new TypedArrays[value[TYPEDARRAY_TYPE]]();
|
|
// BuiltinValueTypeSetters[value[VALUETYPE_SETTER]](val, value);
|
|
// // obj = new window[serialized.ctor](array.length);
|
|
// // for (let i = 0; i < array.length; ++i) {
|
|
// // obj[i] = array[i];
|
|
// // }
|
|
// // return obj;
|
|
// owner[key] = val;
|
|
// }
|
|
|
|
const ASSIGNMENTS: {
|
|
[K in keyof DataTypes]?: ParseFunction<DataTypes[K]>;
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
} = new Array(DataTypeID.ARRAY_LENGTH) as {};
|
|
ASSIGNMENTS[DataTypeID.SimpleType] = assignSimple; // Only be used in the instances array
|
|
ASSIGNMENTS[DataTypeID.InstanceRef] = assignInstanceRef;
|
|
ASSIGNMENTS[DataTypeID.Array_InstanceRef] = genArrayParser(assignInstanceRef);
|
|
ASSIGNMENTS[DataTypeID.Array_AssetRefByInnerObj] = genArrayParser(parseAssetRefByInnerObj);
|
|
ASSIGNMENTS[DataTypeID.Class] = parseClass;
|
|
ASSIGNMENTS[DataTypeID.ValueTypeCreated] = parseValueTypeCreated;
|
|
ASSIGNMENTS[DataTypeID.AssetRefByInnerObj] = parseAssetRefByInnerObj;
|
|
ASSIGNMENTS[DataTypeID.TRS] = parseTRS;
|
|
ASSIGNMENTS[DataTypeID.ValueType] = parseValueType;
|
|
ASSIGNMENTS[DataTypeID.Array_Class] = genArrayParser(parseClass);
|
|
ASSIGNMENTS[DataTypeID.CustomizedClass] = parseCustomClass;
|
|
ASSIGNMENTS[DataTypeID.Dict] = parseDict;
|
|
ASSIGNMENTS[DataTypeID.Array] = parseArray;
|
|
// ASSIGNMENTS[DataTypeID.TypedArray] = parseTypedArray;
|
|
|
|
function parseInstances (data: IFileData): RootInstanceIndex {
|
|
const instances = data[File.Instances];
|
|
const instanceTypes = data[File.InstanceTypes];
|
|
const instanceTypesLen = instanceTypes === EMPTY_PLACEHOLDER ? 0 : (instanceTypes).length;
|
|
let rootIndex = instances[instances.length - 1];
|
|
let normalObjectCount = instances.length - instanceTypesLen;
|
|
if (typeof rootIndex !== 'number') {
|
|
rootIndex = 0;
|
|
} else {
|
|
if (rootIndex < 0) {
|
|
rootIndex = ~rootIndex;
|
|
}
|
|
--normalObjectCount;
|
|
}
|
|
|
|
// DataTypeID.Class
|
|
|
|
let insIndex = 0;
|
|
for (; insIndex < normalObjectCount; ++insIndex) {
|
|
instances[insIndex] = deserializeCCObject(data, instances[insIndex] as IClassObjectData);
|
|
}
|
|
|
|
const classes = data[File.SharedClasses];
|
|
for (let typeIndex = 0; typeIndex < instanceTypesLen; ++typeIndex, ++insIndex) {
|
|
let type = instanceTypes[typeIndex] as OtherObjectTypeID;
|
|
const eachData = instances[insIndex];
|
|
if (type >= 0) {
|
|
// class index for DataTypeID.CustomizedClass
|
|
|
|
const ctor = classes[type] as CCClassConstructor<ICustomClass>; // class
|
|
instances[insIndex] = deserializeCustomCCObject(data, ctor, eachData);
|
|
} else {
|
|
// Other
|
|
|
|
type = (~type) as PrimitiveObjectTypeID;
|
|
const op = ASSIGNMENTS[type];
|
|
op(data, instances, insIndex, eachData);
|
|
}
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
return rootIndex;
|
|
}
|
|
|
|
// const DESERIALIZE_AS = Attr.DELIMETER + 'deserializeAs';
|
|
// function deserializeAs(klass: AnyCCClass, klassLayout: IClass) {
|
|
// var attrs = Attr.getClassAttrs(klass);
|
|
// let keys = klassLayout[CLASS_KEYS];
|
|
// for (let i = 0; i < keys.length; ++i) {
|
|
// let newKey = attrs[keys[i] + DESERIALIZE_AS];
|
|
// if (newKey) {
|
|
// // @ts-expect-error
|
|
// if (keys.includes(newKey)) {
|
|
// // %s cannot be deserialized by property %s because %s was also present in the serialized data.
|
|
// warnID(, newKey, keys[i], newKey);
|
|
// }
|
|
// else {
|
|
// keys[i] = newKey;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
function getMissingClass (hasCustomFinder, type, reportMissingClass: deserialize.ReportMissingClass) {
|
|
if (!hasCustomFinder) {
|
|
reportMissingClass(type);
|
|
}
|
|
return Object;
|
|
}
|
|
function doLookupClass (classFinder, type: string, container: any[], index: number, silent: boolean, hasCustomFinder, reportMissingClass: deserialize.ReportMissingClass) {
|
|
let klass = classFinder(type);
|
|
if (!klass) {
|
|
// if (klass.__FSA__) {
|
|
// deserializeAs(klass, klassLayout as IClass);
|
|
// }
|
|
if (silent) {
|
|
// generate a lazy proxy for ctor
|
|
container[index] = ((c, i, t) => function proxy () {
|
|
const actualClass = classFinder(t) || getMissingClass(hasCustomFinder, t, reportMissingClass);
|
|
c[i] = actualClass;
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, new-cap
|
|
return new actualClass();
|
|
})(container, index, type);
|
|
return;
|
|
} else {
|
|
klass = getMissingClass(hasCustomFinder, type, reportMissingClass);
|
|
}
|
|
}
|
|
container[index] = klass;
|
|
}
|
|
|
|
function lookupClasses (data: IPackedFileData, silent: boolean, customFinder: ClassFinder | undefined, reportMissingClass: deserialize.ReportMissingClass) {
|
|
const classFinder = customFinder || js._getClassById;
|
|
const classes = data[File.SharedClasses];
|
|
for (let i = 0; i < classes.length; ++i) {
|
|
const klassLayout = classes[i];
|
|
if (typeof klassLayout !== 'string') {
|
|
if (DEBUG) {
|
|
if (typeof klassLayout[CLASS_TYPE] === 'function') {
|
|
throw new Error('Can not deserialize the same JSON data again.');
|
|
}
|
|
}
|
|
const type: string = klassLayout[CLASS_TYPE];
|
|
doLookupClass(classFinder, type, klassLayout as IClass, CLASS_TYPE, silent, customFinder, reportMissingClass);
|
|
} else {
|
|
doLookupClass(classFinder, klassLayout, classes, i, silent, customFinder, reportMissingClass);
|
|
}
|
|
}
|
|
}
|
|
|
|
function cacheMasks (data: IPackedFileData) {
|
|
const masks = data[File.SharedMasks];
|
|
if (masks) {
|
|
const classes = data[File.SharedClasses];
|
|
for (let i = 0; i < masks.length; ++i) {
|
|
const mask = masks[i];
|
|
// @ts-expect-error Complex typing.
|
|
mask[MASK_CLASS] = classes[mask[MASK_CLASS]];
|
|
}
|
|
}
|
|
}
|
|
|
|
function parseResult (data: IFileData) {
|
|
const instances = data[File.Instances];
|
|
const sharedStrings = data[File.SharedStrings];
|
|
const dependSharedUuids = data[File.SharedUuids];
|
|
|
|
const dependObjs = data[File.DependObjs];
|
|
const dependKeys = data[File.DependKeys];
|
|
const dependUuids = data[File.DependUuidIndices];
|
|
|
|
for (let i = 0; i < dependObjs.length; ++i) {
|
|
const obj: any = dependObjs[i];
|
|
if (typeof obj === 'number') {
|
|
dependObjs[i] = instances[obj];
|
|
} else {
|
|
// assigned by DataTypeID.AssetRefByInnerObj or added by Details object directly in _deserialize
|
|
}
|
|
let key: any = dependKeys[i];
|
|
if (typeof key === 'number') {
|
|
if (key >= 0) {
|
|
key = sharedStrings[key];
|
|
} else {
|
|
key = ~key;
|
|
}
|
|
dependKeys[i] = key;
|
|
} else {
|
|
// added by Details object directly in _deserialize
|
|
}
|
|
const uuid = dependUuids[i];
|
|
if (typeof uuid === 'number') {
|
|
dependUuids[i] = (dependSharedUuids as SharedString[])[uuid];
|
|
} else {
|
|
// added by Details object directly in _deserialize
|
|
}
|
|
}
|
|
}
|
|
|
|
export function isCompiledJson (json: unknown): boolean {
|
|
if (Array.isArray(json)) {
|
|
const version = json[0];
|
|
// array[0] will not be a number in the editor version
|
|
return typeof version === 'number' || version instanceof FileInfo;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @module cc
|
|
*/
|
|
|
|
/**
|
|
* @en Deserializes a previously serialized object to reconstruct it to the original.
|
|
* @zh 将序列化后的对象进行反序列化以使其复原。
|
|
*
|
|
* @param data Serialized data.
|
|
* @param details - Additional loading result.
|
|
* @param options Deserialization Options.
|
|
* @return The original object.
|
|
*/
|
|
export function deserialize (data: IFileData | string | CCON | any, details: Details | any, options?: IOptions | any): unknown {
|
|
if (typeof data === 'string') {
|
|
data = JSON.parse(data);
|
|
}
|
|
|
|
const borrowDetails = !details;
|
|
details = details || Details.pool.get();
|
|
let res;
|
|
|
|
if (!FORCE_COMPILED && !isCompiledJson(data)) {
|
|
res = deserializeDynamic(data, details, options);
|
|
} else {
|
|
details.init(data);
|
|
options = options || {};
|
|
|
|
let version = data[File.Version];
|
|
let preprocessed = false;
|
|
if (typeof version === 'object') {
|
|
preprocessed = version.preprocessed;
|
|
version = version.version;
|
|
}
|
|
if (version < SUPPORT_MIN_FORMAT_VERSION) {
|
|
throw new Error(getError(5304, version));
|
|
}
|
|
options._version = version;
|
|
options.result = details;
|
|
data[File.Context] = options;
|
|
|
|
if (!preprocessed) {
|
|
lookupClasses(data, false, options.classFinder, options.reportMissingClass ?? deserialize.reportMissingClass);
|
|
cacheMasks(data);
|
|
}
|
|
|
|
legacyCC.game._isCloning = true;
|
|
const instances = data[File.Instances];
|
|
const rootIndex = parseInstances(data);
|
|
legacyCC.game._isCloning = false;
|
|
|
|
if (data[File.Refs]) {
|
|
dereference(data[File.Refs] as IRefs, instances, data[File.SharedStrings]);
|
|
}
|
|
|
|
parseResult(data);
|
|
|
|
res = instances[rootIndex];
|
|
}
|
|
|
|
if (borrowDetails) {
|
|
Details.pool.put(details);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
export declare namespace deserialize {
|
|
export type SerializableClassConstructor = new () => unknown;
|
|
|
|
export type ReportMissingClass = (id: string) => void;
|
|
|
|
export type ClassFinder = {
|
|
(id: string, serialized: unknown, owner?: unknown[] | Record<PropertyKey, unknown>, propName?: string): SerializableClassConstructor | undefined;
|
|
|
|
onDereferenced?: (deserializedList: Array<Record<PropertyKey, unknown> | undefined>, id: number, object: Record<string, unknown> | unknown[], propName: string) => void;
|
|
};
|
|
}
|
|
|
|
deserialize.Details = Details;
|
|
deserialize.reportMissingClass = defaultReportMissingClass;
|
|
|
|
class FileInfo {
|
|
declare version: number;
|
|
preprocessed = true;
|
|
constructor (version: number) {
|
|
this.version = version;
|
|
}
|
|
}
|
|
|
|
export function unpackJSONs (data: IPackedFileData, classFinder?: ClassFinder, reportMissingClass?: deserialize.ReportMissingClass): IFileData[] {
|
|
if (data[File.Version] < SUPPORT_MIN_FORMAT_VERSION) {
|
|
throw new Error(getError(5304, data[File.Version]));
|
|
}
|
|
lookupClasses(data, true, classFinder, reportMissingClass ?? deserialize.reportMissingClass);
|
|
cacheMasks(data);
|
|
|
|
const version = new FileInfo(data[File.Version]);
|
|
const sharedUuids = data[File.SharedUuids];
|
|
const sharedStrings = data[File.SharedStrings];
|
|
const sharedClasses = data[File.SharedClasses];
|
|
const sharedMasks = data[File.SharedMasks];
|
|
|
|
const sections = data[PACKED_SECTIONS];
|
|
for (let i = 0; i < sections.length; ++i) {
|
|
sections[i].unshift(version, sharedUuids, sharedStrings, sharedClasses, sharedMasks);
|
|
}
|
|
return sections;
|
|
}
|
|
|
|
export function packCustomObjData (type: string, data: IClassObjectData|OtherObjectData, hasNativeDep?: boolean): IFileData {
|
|
return [
|
|
SUPPORT_MIN_FORMAT_VERSION, EMPTY_PLACEHOLDER, EMPTY_PLACEHOLDER,
|
|
[type],
|
|
EMPTY_PLACEHOLDER,
|
|
hasNativeDep ? [data, ~0] : [data],
|
|
[0],
|
|
EMPTY_PLACEHOLDER, [], [], [],
|
|
];
|
|
}
|
|
|
|
export function hasNativeDep (data: IFileData): boolean {
|
|
const instances = data[File.Instances];
|
|
const rootInfo = instances[instances.length - 1];
|
|
if (typeof rootInfo !== 'number') {
|
|
return false;
|
|
} else {
|
|
return rootInfo < 0;
|
|
}
|
|
}
|
|
|
|
function getDependUuidList (json: IFileData): string[] {
|
|
const sharedUuids = json[File.SharedUuids];
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
return json[File.DependUuidIndices].map((index) => sharedUuids[index]);
|
|
}
|
|
|
|
export function parseUuidDependencies (serialized: unknown) {
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
if (!DEV || isCompiledJson(serialized as object)) {
|
|
return getDependUuidList(serialized as IFileData);
|
|
} else {
|
|
return parseUuidDependenciesDynamic(serialized);
|
|
}
|
|
}
|
|
|
|
if (PREVIEW) {
|
|
deserialize.isCompiledJson = isCompiledJson;
|
|
}
|
|
|
|
if (EDITOR || TEST) {
|
|
deserialize._macros = {
|
|
EMPTY_PLACEHOLDER,
|
|
CUSTOM_OBJ_DATA_CLASS,
|
|
CUSTOM_OBJ_DATA_CONTENT,
|
|
CLASS_TYPE,
|
|
CLASS_KEYS,
|
|
CLASS_PROP_TYPE_OFFSET,
|
|
MASK_CLASS,
|
|
OBJ_DATA_MASK,
|
|
DICT_JSON_LAYOUT,
|
|
ARRAY_ITEM_VALUES,
|
|
PACKED_SECTIONS,
|
|
} as {
|
|
// freeze values (EMPTY_PLACEHOLDER: number -> EMPTY_PLACEHOLDER: 0)
|
|
EMPTY_PLACEHOLDER: typeof EMPTY_PLACEHOLDER,
|
|
CUSTOM_OBJ_DATA_CLASS: typeof CUSTOM_OBJ_DATA_CLASS,
|
|
CUSTOM_OBJ_DATA_CONTENT: typeof CUSTOM_OBJ_DATA_CONTENT,
|
|
CLASS_TYPE: typeof CLASS_TYPE,
|
|
CLASS_KEYS: typeof CLASS_KEYS,
|
|
CLASS_PROP_TYPE_OFFSET: typeof CLASS_PROP_TYPE_OFFSET,
|
|
MASK_CLASS: typeof MASK_CLASS,
|
|
OBJ_DATA_MASK: typeof OBJ_DATA_MASK,
|
|
DICT_JSON_LAYOUT: typeof DICT_JSON_LAYOUT,
|
|
ARRAY_ITEM_VALUES: typeof ARRAY_ITEM_VALUES,
|
|
PACKED_SECTIONS: typeof PACKED_SECTIONS,
|
|
};
|
|
deserialize._BuiltinValueTypes = BuiltinValueTypes;
|
|
deserialize._serializeBuiltinValueTypes = serializeBuiltinValueTypes;
|
|
}
|
|
|
|
if (TEST) {
|
|
legacyCC._Test.deserializeCompiled = {
|
|
deserialize,
|
|
dereference,
|
|
deserializeCCObject,
|
|
deserializeCustomCCObject,
|
|
parseInstances,
|
|
parseResult,
|
|
cacheMasks,
|
|
File: {
|
|
Version: File.Version,
|
|
Context: File.Context,
|
|
SharedUuids: File.SharedUuids,
|
|
SharedStrings: File.SharedStrings,
|
|
SharedClasses: File.SharedClasses,
|
|
SharedMasks: File.SharedMasks,
|
|
Instances: File.Instances,
|
|
InstanceTypes: File.InstanceTypes,
|
|
Refs: File.Refs,
|
|
DependObjs: File.DependObjs,
|
|
DependKeys: File.DependKeys,
|
|
DependUuidIndices: File.DependUuidIndices,
|
|
// ArrayLength: File.ArrayLength,
|
|
},
|
|
DataTypeID: {
|
|
SimpleType: DataTypeID.SimpleType,
|
|
InstanceRef: DataTypeID.InstanceRef,
|
|
Array_InstanceRef: DataTypeID.Array_InstanceRef,
|
|
Array_AssetRefByInnerObj: DataTypeID.Array_AssetRefByInnerObj,
|
|
Class: DataTypeID.Class,
|
|
ValueTypeCreated: DataTypeID.ValueTypeCreated,
|
|
AssetRefByInnerObj: DataTypeID.AssetRefByInnerObj,
|
|
TRS: DataTypeID.TRS,
|
|
ValueType: DataTypeID.ValueType,
|
|
Array_Class: DataTypeID.Array_Class,
|
|
CustomizedClass: DataTypeID.CustomizedClass,
|
|
Dict: DataTypeID.Dict,
|
|
Array: DataTypeID.Array,
|
|
// TypedArray: DataTypeID.TypedArray,
|
|
},
|
|
BuiltinValueTypes,
|
|
unpackJSONs,
|
|
};
|
|
}
|
|
|
|
legacyCC.deserialize = deserialize;
|