Skip to content

Commit

Permalink
ideas are fully working
Browse files Browse the repository at this point in the history
  • Loading branch information
konsumer committed May 29, 2024
1 parent 333a7a4 commit c0f0aa0
Showing 1 changed file with 102 additions and 41 deletions.
143 changes: 102 additions & 41 deletions test/ideas.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,30 +75,21 @@ export class ProtobufDecoder {
}
const tag = this.decodeVarint().int
const wireType = tag & 0x07
const number = tag >> 3
if (wireType === 4 && number === fieldNumber) {
const number = tag >> 0x03
if (wireType === 4) {
break // End of group
}
const value = this.decodeField(tag)
if (group[number]) {
if (!Array.isArray(group[number])) {
group[number] = [group[number]]
}
group[number].push(value)
} else {
group[number] = value
}
const value = this.decodeField(tag, wireType)
group[number] ||= []
value.fieldNumber = number
group[number].push(value)
}
return { group, value: this.buffer.slice(start, this.pos) }
}

decodeField (tag) {
const wireType = tag & 0x07
const fieldNumber = tag >> 3
decodeField (fieldNumber, wireType) {
this.debug(`Decoding field number ${fieldNumber} with wire type ${wireType}`)

let out = { wireType, fieldNumber }

switch (wireType) {
case 0: // Varint
out = { ...out, ...this.decodeVarint() }; break
Expand All @@ -107,32 +98,30 @@ export class ProtobufDecoder {
case 2: // Length-delimited (string, bytes, or nested message)
out.value = this.decodeBytes(this.decodeVarint().int); break
case 3: // Start group
out = { ...out, ...this.decodeGroup(fieldNumber) }; break
const d = this.decodeGroup(fieldNumber)
out = { ...out, ...d }; break
case 4: // End group
throw new Error('Unexpected end group tag')
case 5: // 32-bit
out = { ...out, ...this.decode32Bit() }; break
default:
throw new Error(`Unsupported wire type: ${wireType}`)
}

return out
}

decode () {
const result = {}
while (this.pos < this.buffer.length) {
const tag = this.decodeVarint().int
const fieldNumber = tag >> 3
const value = this.decodeField(tag)

if (result[fieldNumber]) {
if (!Array.isArray(result[fieldNumber])) {
result[fieldNumber] = [result[fieldNumber]]
}
result[fieldNumber].push(value)
const wireType = tag & 0x07
const fieldNumber = tag >> 0x03
result[fieldNumber] ||= []
const v = this.decodeField(fieldNumber, wireType)
if (Array.isArray(v)) {
result[fieldNumber].push(...v)
} else {
result[fieldNumber] = value
result[fieldNumber].push(v)
}
}
return result
Expand Down Expand Up @@ -195,33 +184,105 @@ export function query (root, q) {
for (const p of findPath) {
const nc = []
for (const tree of current) {
if (Array.isArray(tree[p])) {
nc.push(...tree[p].map(b => decodeMessage(b.value)))
} else {
nc.push(decodeMessage(tree[p].value))
try {
nc.push(...tree[p].map(t => {
if (t.group) {
return t.group
}
return decodeMessage(t.value)
}))
} catch (e) {
// console.error(e.message)
}
}
current = nc
}

return current.map(c => decoders[renderType](c[fieldId]))
const out = []
for (const c of current) {
for (const tree of c[fieldId]) {
out.push(decoders[renderType](tree))
}
}
return out
}

// USAGE TESTING BELOW

const root = decodeMessage(await readFile(join(dirname(fileURLToPath(import.meta.url)), 'hearthstone.bin')))

describe('Query', async () => {
test('Basic Fields', () => {
// this is how to manually delve into each field
describe('Manual', () => {
// 1.2.4
const sub1 = decodeMessage(root[1][0].value)
const sub2 = decodeMessage(sub1[2][0].value)
const appRoot = decodeMessage(sub2[4][0].value)

test('ID', () => {
expect(decoders.string(appRoot[1][0])).toEqual('com.blizzard.wtcg.hearthstone')
})

test('Title', () => {
expect(decoders.string(appRoot[5][0])).toEqual('Hearthstone')
})

test('Group Sub-query (media)', () => {
const mediaItems = []
for (const m of appRoot[10]) {
const mediaRoot = decodeMessage(m.value)
const type = decoders.uint(mediaRoot[1][0])
const url = decoders.string(mediaRoot[5][0])
let width
let height
try {
width = decoders.uint(mediaRoot[2][0].group[3][0])
height = decoders.uint(mediaRoot[2][0].group[4][0])
} catch (e) {}

mediaItems.push({ type, url, width, height })
}
expect(mediaItems.length).toEqual(10)
})
})

// this is how to do same with queries (probly better, in general)

describe('Query', () => {
test('ID', () => {
expect(query(root, '1.2.4.1:string').pop()).toEqual('com.blizzard.wtcg.hearthstone')
})

test('Title', () => {
expect(query(root, '1.2.4.5:string').pop()).toEqual('Hearthstone')
})

test('Group Sub-query (media)', () => {
const medias = query(root, '1.2.4.10:sub').map(r => ({
type: query(r, '1:uint').pop(),
url: query(r, '5:string').pop(),
width: query(r, '2.3:uint').pop(),
height: query(r, '2.4:uint').pop()
}))
expect(medias.length).toEqual(10)
const types = query(root, '1.2.4.10.1:uint')
expect(types.length).toEqual(10)

const urls = query(root, '1.2.4.10.5:string')
expect(urls.length).toEqual(10)

// not all types have height/width, so it's only 8

const widths = query(root, '1.2.4.10.2.3:uint')
expect(widths.length).toEqual(8)

const heights = query(root, '1.2.4.10.2.4:uint')
expect(heights.length).toEqual(8)

// here is how to build a cohesive object, since types 3/13 do not have dims
let type
const mediaItems = []
while (type = types.pop()) {
let width
let height
if (![3, 13].includes(type)) {
width = widths.pop()
height = heights.pop()
}
mediaItems.push({ type, url: urls.pop(), width, height })
}
expect(mediaItems.length).toEqual(10)
})
})

0 comments on commit c0f0aa0

Please sign in to comment.