diff --git a/src/config/Endpoints.ts b/src/config/Endpoints.ts new file mode 100644 index 0000000..5027973 --- /dev/null +++ b/src/config/Endpoints.ts @@ -0,0 +1,50 @@ +import { getClient, ResponseType } from '@tauri-apps/api/http' + +import type { DomainTypes, GameEndPointsTypes } from '../types' + +const PROD_VERSION_REGEX = /(production-[^/]+)/im +const STABLE_PROD_VERSION = 'PRODUCTION-202304181630-471782382' + +export let PROD_VERSION: string | undefined + +const HABBO_URL = (domain: DomainTypes): string => { + return `https://www.habbo.${domain}` +} + +export const HABBO_GORDON_URL = `https://images.habbo.com/gordon/${PROD_VERSION ?? STABLE_PROD_VERSION}` + +export const client = await getClient() +await client + .get(`${HABBO_URL('com')}/gamedata/external_variables/0`, { + responseType: ResponseType.Text + }) + .then(({ data }) => { + return (PROD_VERSION = (data as string).match(PROD_VERSION_REGEX)?.[0]) + }) + +export const GAME_ENDPOINTS = (domain: DomainTypes): GameEndPointsTypes => { + return [ + /* { + src: `${HABBO_URL(domain)}/gamedata/figuredata/0`, // check + convert: 'XML', + fileName: 'FigureData' + }, */ + /* { + src: `${HABBO_GORDON_URL}/figuremap.xml`, + convert: 'XML', + fileName: 'FigureMap' + }, */ + /* { + src: `${HABBO_URL(domain)}/gamedata/furnidata_json/0`, // check + fileName: 'FurniData' + }, */ { + src: `${HABBO_GORDON_URL}/effectmap.xml`, + convert: 'XML', + fileName: 'EffectMap' + } + ] +} + +export const ASSETS_ENDPOINTS = (domain: DomainTypes): string[] => { + return [`${HABBO_URL(domain)}/`] +} diff --git a/src/config/GameDownloader.ts b/src/config/GameDownloader.ts index ecc0c5f..484a25c 100644 --- a/src/config/GameDownloader.ts +++ b/src/config/GameDownloader.ts @@ -1,4 +1,4 @@ -import type { IDownloaderContent } from '../components' +import type { IDownloaderContent } from '../components/layout/Downloaders/Downloader' export const GameDataDownloader: IDownloaderContent = { title: 'Converts and bundles:', diff --git a/src/styles.css b/src/styles.css index 250433c..1323197 100644 --- a/src/styles.css +++ b/src/styles.css @@ -4,6 +4,8 @@ #root { font-family: 'Press Start 2P'; + overflow: hidden; + height: 100vh; } @layer utilities { diff --git a/src/tools/convertTXT.ts b/src/tools/convertTXT.ts new file mode 100644 index 0000000..e08f535 --- /dev/null +++ b/src/tools/convertTXT.ts @@ -0,0 +1,54 @@ +import { parseData } from './parseData' + +interface IBadge { + code: string + name?: string + description?: string +} + +const badgePattern = /^badge_(?:name|desc)_([^=]+)/gim +const descriptionPattern = /^badge_desc_(\s*\w+)/ +const namePattern = /^badge_name_(\s*\w+)/ + +export const convertTXT = async (path: string, data: string): Promise => { + const badges: IBadge[] = [] + + const lines = data.split(/\r?\n/) + + for (const line of lines) { + const [key, value] = line.split('=') + const badge = key.match(badgePattern) + + if (badge != null) { + if (key.match(namePattern) != null) { + const badgeCode = key.match(namePattern)?.[1] as string + const existingBadge = badges.filter((badge) => { + return badge.code === badgeCode + })[0] + + if (Boolean(existingBadge)) { + const index = badges.indexOf(existingBadge) + badges[index].name = value + } else { + badges.push({ code: badgeCode, name: value }) + } + } else if (key.match(descriptionPattern) != null) { + const badgeCode = key.match(descriptionPattern)?.[1] as string + const existingBadge = badges.filter((badge) => { + return badge.code === badgeCode + })[0] + + if (Boolean(existingBadge)) { + const index = badges.indexOf(existingBadge) + badges[index].description = value + } else { + badges.push({ code: badgeCode, description: value }) + } + } + + lines.splice(lines.indexOf(line), 1) + } + } + + return await parseData(path, 'Badges', badges) +} diff --git a/src/tools/fetchGamedata.ts b/src/tools/fetchGamedata.ts new file mode 100644 index 0000000..24ade8b --- /dev/null +++ b/src/tools/fetchGamedata.ts @@ -0,0 +1,55 @@ +import { downloadDir } from '@tauri-apps/api/path' +import { XMLParser } from 'fast-xml-parser' +import { writeTextFile } from '@tauri-apps/api/fs' + +import { PROD_VERSION } from '../config/Endpoints' +import { FigureMap } from '../controllers/FigureMap' +import { EffectMap } from '../controllers/EffectMap' +import type { GameEndPointsTypes, IEffectMap, IFigureData, IFigureMap } from '../types' +import { parseData } from './parseData' +import { convertTXT } from './convertTXT' +import { FigureData } from '../controllers/FigureData' +import { FurniData } from '../controllers/FurniData' + +export const fetchGamedata = async (data: string, endpoint: GameEndPointsTypes[number]): Promise => { + const subDir = '/config' + + const outputDir = async (path: string): Promise => { + return (await downloadDir()).concat(String(PROD_VERSION) + path) + } + + switch (endpoint.convert) { + case 'XML': + const convertedData = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '' }).parse(data) + + const XML2JSON = () => { + if (endpoint.fileName === 'FigureData') { + return new FigureData(convertedData).data + } else if (endpoint.fileName === 'FigureMap') { + return new FigureMap(convertedData).data + } else if (endpoint.fileName === 'EffectMap') { + return new EffectMap(convertedData) + } + } + + XML2JSON() + + break + + /* return await parseData(await outputDir(subDir), endpoint.fileName, XML2JSON) */ + case 'TXT': + return await convertTXT(await outputDir(subDir), data) + + default: + const handleJSON = () => { + if (endpoint.fileName === 'FurniData') { + return new FurniData(JSON.parse(data)) + } + } + + handleJSON() + break + + /* return await parseData(await outputDir(subDir), endpoint.fileName, data) */ + } +} diff --git a/src/tools/handleConvertion.ts b/src/tools/handleConvertion.ts new file mode 100644 index 0000000..45e2347 --- /dev/null +++ b/src/tools/handleConvertion.ts @@ -0,0 +1,36 @@ +import { ResponseType } from '@tauri-apps/api/http' + +import { GAME_ENDPOINTS, client } from '../config/Endpoints' +import type { DomainTypes } from '../types/Domain' +import { fetchGamedata } from './fetchGamedata' +import type { StateTypes } from '../types/global' + +export const handleConvertion = async ( + domain: DomainTypes, + callback: (message: string, state: StateTypes) => void, + assetsOption = false +): Promise => { + if (!assetsOption) { + callback('Converting Gamedata configuration...', 'loading') + + await Promise.all( + GAME_ENDPOINTS(domain).map(async (endpoint) => { + await client + .get(endpoint.src, { responseType: ResponseType.Text }) + .then(async ({ data }) => { + return await fetchGamedata(data as string, endpoint) + }) + .catch((error) => { + console.error(error) + return callback(error, 'error') + }) + }) + ) + + callback('Converting shockwave files...', 'loading') + } else { + /* ASSETS_ENDPOINTS(domain).map((endpoint) => { + client.get() + }) */ + } +} diff --git a/src/tools/parseData.ts b/src/tools/parseData.ts new file mode 100644 index 0000000..ec99ece --- /dev/null +++ b/src/tools/parseData.ts @@ -0,0 +1,9 @@ +import { createDir, exists, writeTextFile } from '@tauri-apps/api/fs' + +export const parseData = async (path: string, fileName: string, fileContent: string | object): Promise => { + const fileDir = path.concat(`/${fileName}.json`) + + if (!(await exists(path))) await createDir(path) + + await writeTextFile(fileDir, typeof fileContent === 'object' ? JSON.stringify(fileContent) : fileContent) +} diff --git a/src/types/Domain.d.ts b/src/types/Domain.d.ts index 7d13b7f..4bc5cb8 100644 --- a/src/types/Domain.d.ts +++ b/src/types/Domain.d.ts @@ -1,10 +1 @@ -export type DomainTypes = - | 'com.br' - | 'com.tr' - | 'com' - | 'de' - | 'es' - | 'fi' - | 'fr' - | 'it' - | 'nl' +export type DomainTypes = 'com.br' | 'com.tr' | 'com' | 'de' | 'es' | 'fi' | 'fr' | 'it' | 'nl' diff --git a/src/types/index.d.ts b/src/types/index.d.ts index e2826e7..3343752 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -2,3 +2,4 @@ export * from './Converters' export * from './SubConverters' export * from './Endpoint' export * from './Domain' +export * from './global'