微信小程序编译
All checks were successful
Plugin Library CI / publish (00.BuildOriginality) (push) Successful in 13s
Plugin Library CI / publish (00.StaryEvo) (push) Successful in 17s
Plugin Library CI / publish (00.StaryEvoTools) (push) Successful in 35s
Plugin Library CI / publish (01.HybridCLR) (push) Successful in 15s
Plugin Library CI / publish (02.InformationSave) (push) Successful in 3s
Plugin Library CI / publish (03.YooAsset) (push) Successful in 33s
Plugin Library CI / publish (04.AudioCore) (push) Successful in 3s
Plugin Library CI / publish (05.TableTextConversion) (push) Successful in 5s
Plugin Library CI / publish (06.UIFarme) (push) Successful in 15s
Plugin Library CI / publish (07.RKTools) (push) Successful in 2s
Plugin Library CI / publish (08.UniTask) (push) Successful in 3s
Plugin Library CI / publish (09.CodeChecker) (push) Successful in 16s
Plugin Library CI / publish (10.StoryEditor) (push) Successful in 3s
Plugin Library CI / publish (10.XNode) (push) Successful in 3s
Plugin Library CI / publish (11.PointCloudTools) (push) Successful in 2s
Plugin Library CI / publish (12.WeixinMinigame) (push) Successful in 2m32s
All checks were successful
Plugin Library CI / publish (00.BuildOriginality) (push) Successful in 13s
Plugin Library CI / publish (00.StaryEvo) (push) Successful in 17s
Plugin Library CI / publish (00.StaryEvoTools) (push) Successful in 35s
Plugin Library CI / publish (01.HybridCLR) (push) Successful in 15s
Plugin Library CI / publish (02.InformationSave) (push) Successful in 3s
Plugin Library CI / publish (03.YooAsset) (push) Successful in 33s
Plugin Library CI / publish (04.AudioCore) (push) Successful in 3s
Plugin Library CI / publish (05.TableTextConversion) (push) Successful in 5s
Plugin Library CI / publish (06.UIFarme) (push) Successful in 15s
Plugin Library CI / publish (07.RKTools) (push) Successful in 2s
Plugin Library CI / publish (08.UniTask) (push) Successful in 3s
Plugin Library CI / publish (09.CodeChecker) (push) Successful in 16s
Plugin Library CI / publish (10.StoryEditor) (push) Successful in 3s
Plugin Library CI / publish (10.XNode) (push) Successful in 3s
Plugin Library CI / publish (11.PointCloudTools) (push) Successful in 2s
Plugin Library CI / publish (12.WeixinMinigame) (push) Successful in 2m32s
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
export default function fixCmapTable(arrayBuffer) {
|
||||
|
||||
const font = new DataView(arrayBuffer);
|
||||
const tableCount = font.getUint16(4);
|
||||
|
||||
let cmapOffset = 0;
|
||||
let cmapLength = 0;
|
||||
let cmapCheckSumOffset = 0;
|
||||
let cmapCheckSum = 0;
|
||||
|
||||
for (let i = 0; i < tableCount; i++) {
|
||||
const tag = font.getUint32(12 + i * 16);
|
||||
|
||||
if (tag === 0x636D6170) {
|
||||
cmapCheckSumOffset = 12 + i * 16 + 4;
|
||||
cmapCheckSum = font.getUint32(cmapCheckSumOffset);
|
||||
cmapOffset = font.getUint32(12 + i * 16 + 8);
|
||||
cmapLength = font.getUint32(12 + i * 16 + 12);
|
||||
GameGlobal.manager.Logger.pluginLog(`[font]cmapCheckSubOffset [${cmapCheckSumOffset}], cmapCheckSum [${cmapCheckSum}], cmapOffset [${cmapOffset}], cmapLength [${cmapLength}]`);
|
||||
}
|
||||
}
|
||||
if (cmapOffset === 0) {
|
||||
GameGlobal.manager.Logger.pluginError('[font]not found cmap');
|
||||
return false;
|
||||
}
|
||||
|
||||
const cmap = new DataView(arrayBuffer, cmapOffset, cmapLength);
|
||||
const numTables = cmap.getUint16(2);
|
||||
let subtableOffset = 4;
|
||||
let targetSubtableOffset = 0;
|
||||
|
||||
for (let i = 0; i < numTables; i++) {
|
||||
const platformId = cmap.getUint16(subtableOffset);
|
||||
const encodingId = cmap.getUint16(subtableOffset + 2);
|
||||
|
||||
|
||||
if (platformId === 0 && encodingId === 5) {
|
||||
if (i === (numTables - 1)) {
|
||||
targetSubtableOffset = subtableOffset;
|
||||
GameGlobal.manager.Logger.pluginLog(`[font]targetSubtableOffset ${targetSubtableOffset}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
subtableOffset += 8;
|
||||
}
|
||||
|
||||
if (targetSubtableOffset > 0) {
|
||||
|
||||
|
||||
|
||||
const newCmapView = new DataView(arrayBuffer, cmapOffset, cmapLength - 8);
|
||||
|
||||
newCmapView.setUint16(2, numTables - 1);
|
||||
let sum = 0;
|
||||
const lengthInUint32 = (newCmapView.byteLength + 3) / 4;
|
||||
for (let i = 0; i < lengthInUint32; i++) {
|
||||
sum += newCmapView.getUint32(i);
|
||||
}
|
||||
|
||||
font.setUint32(cmapCheckSumOffset, sum);
|
||||
return true;
|
||||
}
|
||||
GameGlobal.manager.Logger.pluginLog('[font]not found cmap subtable');
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9be3b757d4904044b9ae5accecdb013a
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,198 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
import moduleHelper from '../module-helper';
|
||||
import { formatJsonStr } from '../utils';
|
||||
import fixCmapTable from './fix-cmap';
|
||||
import readMetrics from './read-metrics';
|
||||
import splitTTCToBufferOnlySC from './split-sc';
|
||||
|
||||
const { platform } = wx.getDeviceInfo ? wx.getDeviceInfo() : wx.getSystemInfoSync();
|
||||
|
||||
const tempCacheObj = {};
|
||||
let fontDataCache;
|
||||
let getFontPromise;
|
||||
let isReadFromCache = false;
|
||||
const isIOS = platform === 'ios';
|
||||
const isAndroid = platform === 'android';
|
||||
const fontOptions = {
|
||||
CJK_Unified_Ideographs: {
|
||||
include: $CJK_Unified_Ideographs,
|
||||
unicodeRange: [0x4E00, 0x9FFF],
|
||||
},
|
||||
C0_Controls_and_Basic_Latin: {
|
||||
include: $C0_Controls_and_Basic_Latin,
|
||||
unicodeRange: [0x0000, 0x007F],
|
||||
},
|
||||
CJK_Symbols_and_Punctuation: {
|
||||
include: $CJK_Symbols_and_Punctuation,
|
||||
unicodeRange: [0x3000, 0x303F],
|
||||
},
|
||||
General_Punctuation: {
|
||||
include: $General_Punctuation,
|
||||
unicodeRange: [0x2000, 0x206F],
|
||||
},
|
||||
Enclosed_CJK_Letters_and_Months: {
|
||||
include: $Enclosed_CJK_Letters_and_Months,
|
||||
unicodeRange: [0x3200, 0x32FF],
|
||||
},
|
||||
Vertical_Forms: {
|
||||
include: $Vertical_Forms,
|
||||
unicodeRange: [0xFE10, 0xFE1F],
|
||||
},
|
||||
CJK_Compatibility_Forms: {
|
||||
include: $CJK_Compatibility_Forms,
|
||||
unicodeRange: [0xFE30, 0xFE4F],
|
||||
},
|
||||
Miscellaneous_Symbols: {
|
||||
include: $Miscellaneous_Symbols,
|
||||
unicodeRange: [0x2600, 0x26FF],
|
||||
},
|
||||
CJK_Compatibility: {
|
||||
include: $CJK_Compatibility,
|
||||
unicodeRange: [0x3300, 0x33FF],
|
||||
},
|
||||
Halfwidth_and_Fullwidth_Forms: {
|
||||
include: $Halfwidth_and_Fullwidth_Forms,
|
||||
unicodeRange: [0xFF00, 0xFFEF],
|
||||
},
|
||||
Dingbats: {
|
||||
include: $Dingbats,
|
||||
unicodeRange: [0x2700, 0x27BF],
|
||||
},
|
||||
Letterlike_Symbols: {
|
||||
include: $Letterlike_Symbols,
|
||||
unicodeRange: [0x2100, 0x214F],
|
||||
},
|
||||
Enclosed_Alphanumerics: {
|
||||
include: $Enclosed_Alphanumerics,
|
||||
unicodeRange: [0x2460, 0x24FF],
|
||||
},
|
||||
Number_Forms: {
|
||||
include: $Number_Forms,
|
||||
unicodeRange: [0x2150, 0x218F],
|
||||
},
|
||||
Currency_Symbols: {
|
||||
include: $Currency_Symbols,
|
||||
unicodeRange: [0x20A0, 0x20CF],
|
||||
},
|
||||
Arrows: {
|
||||
include: $Arrows,
|
||||
unicodeRange: [0x2190, 0x21FF],
|
||||
},
|
||||
Geometric_Shapes: {
|
||||
include: $Geometric_Shapes,
|
||||
unicodeRange: [0x25A0, 0x25FF],
|
||||
},
|
||||
Mathematical_Operators: {
|
||||
include: $Mathematical_Operators,
|
||||
unicodeRange: [0x2200, 0x22FF],
|
||||
},
|
||||
CustomUnicodeRange: $CustomUnicodeRange,
|
||||
};
|
||||
function handleGetFontData(config, forceFallback) {
|
||||
|
||||
const canGetWxCommonFont = !!GameGlobal.manager?.font?.getCommonFont;
|
||||
|
||||
if (!config && !canGetWxCommonFont) {
|
||||
return Promise.reject('invalid usage');
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
if (!getFontPromise || forceFallback) {
|
||||
getFontPromise = new Promise((resolve, reject) => {
|
||||
|
||||
if ((!canGetWxCommonFont || forceFallback) && !!config) {
|
||||
const xhr = new GameGlobal.unityNamespace.UnityLoader.UnityCache.XMLHttpRequest();
|
||||
xhr.open('GET', config.fallbackUrl, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.onload = () => {
|
||||
|
||||
if ((xhr.status === 200 || xhr.status === 0) && xhr.response) {
|
||||
const notoFontData = xhr.response;
|
||||
fontDataCache = notoFontData;
|
||||
isReadFromCache = xhr.isReadFromCache;
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
xhr.onerror = reject;
|
||||
xhr.send();
|
||||
return;
|
||||
}
|
||||
let unicodeRange = [];
|
||||
|
||||
Object.keys(fontOptions).forEach((key) => {
|
||||
if (fontOptions[key].include) {
|
||||
unicodeRange.push(fontOptions[key].unicodeRange);
|
||||
}
|
||||
});
|
||||
|
||||
unicodeRange = unicodeRange.concat(fontOptions.CustomUnicodeRange);
|
||||
|
||||
GameGlobal.manager.font.getCommonFont({
|
||||
success(fontData) {
|
||||
|
||||
if (isIOS) {
|
||||
fixCmapTable(fontData);
|
||||
}
|
||||
|
||||
if (isAndroid) {
|
||||
const tempData = splitTTCToBufferOnlySC(fontData);
|
||||
if (tempData) {
|
||||
fontData = tempData;
|
||||
}
|
||||
}
|
||||
fontDataCache = fontData;
|
||||
resolve();
|
||||
},
|
||||
fail: reject,
|
||||
}, unicodeRange);
|
||||
});
|
||||
}
|
||||
return getFontPromise;
|
||||
}
|
||||
function WXGetFontRawData(conf, callbackId, forceFallback = false) {
|
||||
const config = formatJsonStr(conf);
|
||||
const loadFromRemote = !GameGlobal.manager?.font?.getCommonFont;
|
||||
GameGlobal.manager.TimeLogger.timeStart('WXGetFontRawData');
|
||||
|
||||
handleGetFontData(config, forceFallback).then(() => {
|
||||
if (fontDataCache) {
|
||||
|
||||
GameGlobal.manager.font.reportGetFontCost(GameGlobal.manager.TimeLogger.timeEnd('WXGetFontRawData'), { loadFromRemote: forceFallback || loadFromRemote, isReadFromCache, preloadWXFont: GameGlobal.unityNamespace.preloadWXFont });
|
||||
const { ascent, descent, lineGap, unitsPerEm } = readMetrics(fontDataCache) || {};
|
||||
tempCacheObj[callbackId] = fontDataCache;
|
||||
moduleHelper.send('GetFontRawDataCallback', JSON.stringify({ callbackId, type: 'success', res: JSON.stringify({ byteLength: fontDataCache.byteLength, ascent, descent, lineGap, unitsPerEm }) }));
|
||||
GameGlobal.manager.Logger.pluginLog(`[font] load font from ${forceFallback || loadFromRemote ? `network, url=${config.fallbackUrl}` : 'local'}`);
|
||||
|
||||
fontDataCache = null;
|
||||
}
|
||||
else {
|
||||
GameGlobal.manager.Logger.pluginError('[font] load font error: empty content');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
if (!loadFromRemote && !!config && forceFallback === false) {
|
||||
WXGetFontRawData(conf, callbackId, true);
|
||||
}
|
||||
else {
|
||||
GameGlobal.manager.Logger.pluginError('[font] load font error: ', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
function WXShareFontBuffer(buffer, offset, callbackId) {
|
||||
if (typeof tempCacheObj[callbackId] === 'string') {
|
||||
GameGlobal.manager.Logger.pluginError('[font]内存写入异常');
|
||||
}
|
||||
buffer.set(new Uint8Array(tempCacheObj[callbackId]), offset);
|
||||
delete tempCacheObj[callbackId];
|
||||
}
|
||||
export function preloadWxCommonFont() {
|
||||
|
||||
if (!!GameGlobal.unityNamespace.preloadWXFont && !!GameGlobal.manager?.font?.getCommonFont) {
|
||||
handleGetFontData();
|
||||
}
|
||||
}
|
||||
export default {
|
||||
WXGetFontRawData,
|
||||
WXShareFontBuffer,
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0908c0a7c26984741918b3b16f28fb4b
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,41 @@
|
||||
import { toBytesInt32 } from './util';
|
||||
export default function readMetrics(arrayBuffer) {
|
||||
const font = new DataView(arrayBuffer);
|
||||
const tableCount = font.getUint16(4);
|
||||
const ppem = 1;
|
||||
let ascent = 0;
|
||||
let descent = 0;
|
||||
let lineGap = 0;
|
||||
let unitsPerEm;
|
||||
for (let i = 0; i < tableCount; i++) {
|
||||
const tag = font.getUint32(12 + i * 16);
|
||||
const tagStr = toBytesInt32(tag);
|
||||
|
||||
if (tagStr === 'hhea') {
|
||||
const offset = font.getUint32(12 + i * 16 + 8);
|
||||
const length = font.getUint32(12 + i * 16 + 12);
|
||||
|
||||
const hhea = new DataView(arrayBuffer, offset, length);
|
||||
ascent = hhea.getInt16(4);
|
||||
descent = hhea.getInt16(6);
|
||||
lineGap = hhea.getInt16(8);
|
||||
}
|
||||
else if (tagStr === 'head') {
|
||||
const offset = font.getUint32(12 + i * 16 + 8);
|
||||
const length = font.getUint32(12 + i * 16 + 12);
|
||||
|
||||
const head = new DataView(arrayBuffer, offset, length);
|
||||
unitsPerEm = head.getUint16(18);
|
||||
}
|
||||
}
|
||||
if (!ascent || !descent || !unitsPerEm) {
|
||||
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
ascent: ascent * ppem / unitsPerEm,
|
||||
descent: descent * ppem / unitsPerEm,
|
||||
lineGap: lineGap * ppem / unitsPerEm,
|
||||
unitsPerEm,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0945eca3c40382428fb5a3f5b9ed58a
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,140 @@
|
||||
import { ceil4, toBytesInt32, decodeUnicode } from './util';
|
||||
function extractTTF(ttcView, tableHeaderOffset) {
|
||||
|
||||
|
||||
|
||||
|
||||
const subFontTableCount = ttcView.getUint16(tableHeaderOffset + 0x04);
|
||||
|
||||
const subFontHeaderLength = 0x0C + subFontTableCount * 0x10;
|
||||
|
||||
let tableLength = 0;
|
||||
for (let j = 0; j < subFontTableCount; j++) {
|
||||
|
||||
const length = ttcView.getUint32(tableHeaderOffset + 0x0C + 0x0C + j * 0x10);
|
||||
tableLength += ceil4(length);
|
||||
}
|
||||
|
||||
const totalLength = subFontHeaderLength + tableLength;
|
||||
|
||||
|
||||
const newBuf = new ArrayBuffer(totalLength);
|
||||
const newBufUint = new Uint8Array(newBuf);
|
||||
const newBufData = new DataView(newBuf);
|
||||
|
||||
|
||||
newBufUint.set(new Uint8Array(ttcView.buffer, tableHeaderOffset, subFontHeaderLength), 0);
|
||||
|
||||
let currentOffset = subFontHeaderLength;
|
||||
for (let j = 0; j < subFontTableCount; j++) {
|
||||
|
||||
const offset = ttcView.getUint32(tableHeaderOffset + 0x0C + 0x08 + j * 0x10);
|
||||
const length = ttcView.getUint32(tableHeaderOffset + 0x0C + 0x0C + j * 0x10);
|
||||
|
||||
|
||||
newBufData.setUint32(0x0C + 0x08 + j * 0x10, currentOffset);
|
||||
newBufUint.set(new Uint8Array(ttcView.buffer, offset, length), currentOffset);
|
||||
currentOffset += ceil4(length);
|
||||
}
|
||||
return newBufData;
|
||||
}
|
||||
|
||||
|
||||
function parseTableToDataView(fontDataView, tableName, startOffset = 0) {
|
||||
const font = fontDataView;
|
||||
const tableCount = font.getUint16(startOffset + 4);
|
||||
for (let i = 0; i < tableCount; i++) {
|
||||
const tag = font.getUint32(startOffset + 12 + i * 16);
|
||||
const tagStr = toBytesInt32(tag);
|
||||
|
||||
if (tagStr === tableName) {
|
||||
const offset = font.getUint32(startOffset + 12 + i * 16 + 8);
|
||||
const length = font.getUint32(startOffset + 12 + i * 16 + 12);
|
||||
return new DataView(fontDataView.buffer, offset, length);
|
||||
}
|
||||
}
|
||||
GameGlobal.manager.Logger.pluginError(`\tTable#${tableName} not found in DataView`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
function parseNameTable(fontDataView, startOffset = 0) {
|
||||
const nameTable = parseTableToDataView(fontDataView, 'name', startOffset);
|
||||
if (!nameTable) {
|
||||
return undefined;
|
||||
}
|
||||
const result = {};
|
||||
result.data = nameTable;
|
||||
result.format = nameTable.getUint16(0);
|
||||
result.count = nameTable.getUint16(2);
|
||||
result.stringOffset = nameTable.getUint16(4);
|
||||
const nameRecords = [];
|
||||
for (let i = 0; i < result.count; i++) {
|
||||
const offset = 6 + i * 12;
|
||||
nameRecords.push({
|
||||
platformID: nameTable.getUint16(offset),
|
||||
platformSpecificID: nameTable.getUint16(offset + 2),
|
||||
languageID: nameTable.getUint16(offset + 4),
|
||||
nameID: nameTable.getUint16(offset + 6),
|
||||
length: nameTable.getUint16(offset + 8),
|
||||
offset: nameTable.getUint16(offset + 10),
|
||||
});
|
||||
}
|
||||
result.nameRecords = nameRecords;
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseFamilyName(fontDataView, startOffset = 0) {
|
||||
const nameTable = parseNameTable(fontDataView, startOffset);
|
||||
if (!nameTable) {
|
||||
return undefined;
|
||||
}
|
||||
if (nameTable.nameRecords) {
|
||||
for (const record of nameTable.nameRecords) {
|
||||
const { nameID } = record;
|
||||
if (nameID === 1) {
|
||||
const { offset } = record;
|
||||
const byteLength = record.length;
|
||||
|
||||
return decodeUnicode(fontDataView.buffer, (nameTable.data?.byteOffset || 0) + (nameTable.stringOffset || 0) + offset, byteLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export default function splitTTCToBufferOnlySC(arrayBuffer) {
|
||||
const ttc = new DataView(arrayBuffer);
|
||||
const tag = ttc.getUint32(0);
|
||||
|
||||
if (toBytesInt32(tag) !== 'ttcf') {
|
||||
GameGlobal.manager.Logger.pluginError('input not a valid ttc file');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ttfCount = ttc.getInt32(8);
|
||||
|
||||
|
||||
let fontSCHeaderOffset = undefined;
|
||||
const reg = /S\0?C/;
|
||||
for (let i = 0; i < ttfCount; i++) {
|
||||
|
||||
const tableHeaderOffset = ttc.getUint32(0x0C + i * 4);
|
||||
|
||||
|
||||
const familyName = parseFamilyName(ttc, tableHeaderOffset);
|
||||
|
||||
if (typeof familyName === 'string' && reg.test(familyName)) {
|
||||
fontSCHeaderOffset = tableHeaderOffset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!fontSCHeaderOffset) {
|
||||
GameGlobal.manager.Logger.pluginError('SC Font not found in TTC File.');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return extractTTF(ttc, fontSCHeaderOffset).buffer;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c682656cb5c8380418380a7cbaf79e55
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
export function toBytesInt32(num) {
|
||||
let ascii = '';
|
||||
for (let i = 3; i >= 0; i--) {
|
||||
ascii += String.fromCharCode((num >> (8 * i)) & 255);
|
||||
}
|
||||
return ascii;
|
||||
}
|
||||
;
|
||||
export function ceil4(n) {
|
||||
|
||||
return (n + 3) & ~3;
|
||||
}
|
||||
export function decodeUnicode(buffer, byteOffset, byteLength) {
|
||||
const dataview = new Uint8Array(buffer, byteOffset, byteLength);
|
||||
const ret = String.fromCharCode.apply(null, Array.from(dataview));
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12c1f0f840ec4a248858b769d63ca766
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user