• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SamboyCoding / Cpp2IL / 12284614548

11 Dec 2024 08:46PM UTC coverage: 28.154% (-0.02%) from 28.172%
12284614548

Pull #390

github

web-flow
Merge 158ed402d into edbb9949b
Pull Request #390: Set AssemblyDefinition::PublicKey when generating AsmResolver assemblies

1266 of 6242 branches covered (20.28%)

Branch coverage included in aggregate %.

6 of 14 new or added lines in 4 files covered. (42.86%)

4 existing lines in 2 files now uncovered.

3385 of 10278 relevant lines covered (32.93%)

124847.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.49
/LibCpp2IL/ClassReadingBinaryReader.cs
1
using System;
2
using System.Collections.Concurrent;
3
using System.Collections.Generic;
4
using System.IO;
5
using System.Linq;
6
using System.Reflection;
7
using System.Text;
8
using System.Threading;
9

10
namespace LibCpp2IL;
11

12
public class ClassReadingBinaryReader : EndianAwareBinaryReader
13
{
14
    /// <summary>
15
    /// Set this to true to enable storing of amount of bytes read of each readable structure.
16
    /// </summary>
17
    public static bool EnableReadableSizeInformation = false;
18

19
    private SpinLock PositionShiftLock;
20

21
    public bool is32Bit;
22
    private MemoryStream? _memoryStream;
23

24
    public ulong PointerSize => is32Bit ? 4ul : 8ul;
978,082✔
25

26
    protected bool _hasFinishedInitialRead;
27
    private bool _inReadableRead;
28
    public ConcurrentDictionary<Type, int> BytesReadPerClass = new();
42✔
29

30

31
    public ClassReadingBinaryReader(MemoryStream input) : base(input)
42✔
32
    {
33
        _memoryStream = input;
42✔
34
    }
42✔
35

36
    public ClassReadingBinaryReader(Stream input) : base(input)
×
37
    {
38
        _memoryStream = null;
×
39
    }
×
40

41
    public long Position
42
    {
43
        get => BaseStream.Position;
7,316,722✔
44
        set => BaseStream.Position = value;
6,556,819✔
45
    }
46

47
    public long Length => BaseStream.Length;
978,082✔
48

49
    internal virtual object? ReadPrimitive(Type type, bool overrideArchCheck = false)
50
    {
51
        if (type == typeof(bool))
1,777,105!
52
            return ReadBoolean();
×
53

54
        if (type == typeof(char))
1,777,105!
55
            return ReadChar();
×
56

57
        if (type == typeof(int))
1,777,105✔
58
            return ReadInt32();
500,055✔
59

60
        if (type == typeof(uint))
1,277,050✔
61
            return ReadUInt32();
1,167,574✔
62

63
        if (type == typeof(short))
109,476!
64
            return ReadInt16();
×
65

66
        if (type == typeof(ushort))
109,476!
67
            return ReadUInt16();
×
68

69
        if (type == typeof(sbyte))
109,476!
70
            return ReadSByte();
×
71

72
        if (type == typeof(byte))
109,476!
73
            return ReadByte();
×
74

75
        if (type == typeof(long))
109,476!
76
            return is32Bit && !overrideArchCheck ? ReadInt32() : ReadInt64();
109,476✔
77

78
        if (type == typeof(ulong))
×
79
            return is32Bit && !overrideArchCheck ? ReadUInt32() : ReadUInt64();
×
80

81
        if (type == typeof(float))
×
82
            return ReadSingle();
×
83

84
        if (type == typeof(double))
×
85
            return ReadDouble();
×
86

87
        return null;
×
88
    }
89

90
    public uint ReadUnityCompressedUIntAtRawAddr(long offset, out int bytesRead)
91
    {
92
        GetLockOrThrow();
×
93

94
        try
95
        {
96
            return ReadUnityCompressedUIntAtRawAddrNoLock(offset, out bytesRead);
×
97
        }
98
        finally
99
        {
100
            ReleaseLock();
×
101
        }
×
102
    }
×
103

104
    protected internal uint ReadUnityCompressedUIntAtRawAddrNoLock(long offset, out int bytesRead)
105
    {
106
        if (offset >= 0)
19,140✔
107
            Position = offset;
19,140✔
108

109
        //Ref Unity.IL2CPP.dll, Unity.IL2CPP.Metadata.MetadataUtils::WriteCompressedUInt32
110
        //Read first byte
111
        var b = ReadByte();
19,140✔
112
        bytesRead = 1;
19,140✔
113
        if (b < 128)
19,140✔
114
            return b;
12,018✔
115
        if (b == 240)
7,122✔
116
        {
117
            //Full Uint
118
            bytesRead = 5;
120✔
119
            return ReadUInt32();
120✔
120
        }
121

122
        //Special constant values
123
        if (b == byte.MaxValue)
7,002✔
124
            return uint.MaxValue;
36✔
125
        if (b == 254)
6,966✔
126
            return uint.MaxValue - 1;
12✔
127

128
        if ((b & 192) == 192)
6,954✔
129
        {
130
            //3 more to read
131
            bytesRead = 4;
690✔
132
            return (b & ~192U) << 24 | (uint)(ReadByte() << 16) | (uint)(ReadByte() << 8) | ReadByte();
690✔
133
        }
134

135
        if ((b & 128) == 128)
6,264!
136
        {
137
            //1 more to read
138
            bytesRead = 2;
6,264✔
139
            return (b & ~128U) << 8 | ReadByte();
6,264✔
140
        }
141

142

143
        throw new Exception($"How did we even get here? Invalid compressed int first byte {b}");
×
144
    }
145

146
    public int ReadUnityCompressedIntAtRawAddr(long position, out int bytesRead)
147
        => ReadUnityCompressedIntAtRawAddr(position, true, out bytesRead);
×
148

149
    protected internal int ReadUnityCompressedIntAtRawAddr(long position, bool doLock, out int bytesRead)
150
    {
151
        //Ref libil2cpp, il2cpp\utils\ReadCompressedInt32
152
        uint unsigned;
153
        if (doLock)
18,978!
154
            unsigned = ReadUnityCompressedUIntAtRawAddr(position, out bytesRead);
×
155
        else
156
            unsigned = ReadUnityCompressedUIntAtRawAddrNoLock(position, out bytesRead);
18,978✔
157

158
        if (unsigned == uint.MaxValue)
18,978✔
159
            return int.MinValue;
30✔
160

161
        var isNegative = (unsigned & 1) == 1;
18,948✔
162
        unsigned >>= 1;
18,948✔
163
        if (isNegative)
18,948✔
164
            return -(int)(unsigned + 1);
174✔
165

166
        return (int)unsigned;
18,774✔
167
    }
168

169
    private T InternalReadClass<T>(bool overrideArchCheck = false) where T : new()
170
    {
171
        return (T)InternalReadClass(typeof(T), overrideArchCheck);
1,606,353✔
172
    }
173

174
    private T InternalReadReadableClass<T>() where T : ReadableClass, new()
175
    {
176
        var t = new T();
9,407,384✔
177

178
        if (!_inReadableRead)
9,407,384✔
179
        {
180
            _inReadableRead = true;
8,826,953✔
181
            t.Read(this);
8,826,953✔
182
            _inReadableRead = false;
8,826,953✔
183
        }
184
        else
185
        {
186
            t.Read(this);
580,431✔
187
        }
188

189
        return t;
9,407,384✔
190
    }
191

192
    private object InternalReadClass(Type type, bool overrideArchCheck = false)
193
    {
194
        if (type.IsPrimitive)
1,606,353!
195
        {
196
            return ReadAndConvertPrimitive(overrideArchCheck, type);
1,606,353✔
197
        }
198

199
        if (type.IsEnum)
×
200
        {
201
            var value = ReadPrimitive(type.GetEnumUnderlyingType());
×
202

203
            return value!;
×
204
        }
205

206
        throw new("Support for reading classes has been removed. Please inherit from ReadableClass and call ReadReadable on your local binary reader.");
×
207
    }
208

209
    private object ReadAndConvertPrimitive(bool overrideArchCheck, Type type)
210
    {
211
        var value = ReadPrimitive(type, overrideArchCheck);
1,606,353✔
212

213
        //32-bit fixes...
214
        if (value is uint && type == typeof(ulong))
1,606,353!
215
            value = Convert.ToUInt64(value);
×
216
        if (value is int && type == typeof(long))
1,606,353!
217
            value = Convert.ToInt64(value);
×
218

219
        return value!;
1,606,353✔
220
    }
221

222
    public T[] ReadClassArrayAtRawAddr<T>(long offset, long count) where T : new()
223
    {
224
        var t = new T[count];
141✔
225

226
        GetLockOrThrow();
141✔
227

228
        try
229
        {
230
            if (offset != -1) Position = offset;
282✔
231

232
            for (var i = 0; i < count; i++)
3,212,988✔
233
            {
234
                t[i] = InternalReadClass<T>();
1,606,353✔
235
            }
236

237
            return t;
141✔
238
        }
239
        finally
240
        {
241
            ReleaseLock();
141✔
242
        }
141✔
243
    }
141✔
244

245
    public string ReadStringToNull(ulong offset) => ReadStringToNull((long)offset);
7,644✔
246

247
    public virtual string ReadStringToNull(long offset)
248
    {
249
        GetLockOrThrow();
8,608✔
250

251
        try
252
        {
253
            return ReadStringToNullNoLock(offset);
8,608✔
254
        }
255
        finally
256
        {
257
            ReleaseLock();
8,608✔
258
        }
8,608✔
259
    }
8,608✔
260

261
    internal string ReadStringToNullNoLock(long offset)
262
    {
263
        var builder = new List<byte>();
840,682✔
264

265
        if (offset != -1)
840,682✔
266
            Position = offset;
840,682✔
267

268
        try
269
        {
270
            byte b;
271
            while ((b = ReadByte()) != 0)
17,901,193✔
272
                builder.Add(b);
17,060,511✔
273

274
            return Encoding.UTF8.GetString(builder.ToArray());
840,682✔
275
        }
276
        finally
277
        {
278
            var bytesRead = (int)(Position - offset);
840,682✔
279
            TrackRead<string>(bytesRead);
840,682✔
280
        }
840,682✔
281
    }
840,682✔
282

283
    public string ReadStringToNullAtCurrentPos()
284
        => ReadStringToNullNoLock(-1);
×
285

286
    public byte[] ReadByteArrayAtRawAddress(long offset, int count)
287
    {
288
        GetLockOrThrow();
18,564✔
289

290
        try
291
        {
292
            return ReadByteArrayAtRawAddressNoLock(offset, count);
18,564✔
293
        }
294
        finally
295
        {
296
            ReleaseLock();
18,564✔
297
        }
18,564✔
298
    }
18,564✔
299

300
    protected internal byte[] ReadByteArrayAtRawAddressNoLock(long offset, int count)
301
    {
302
        if (offset != -1)
19,618✔
303
            Position = offset;
19,618✔
304

305
        if (count == 0)
19,618!
NEW
306
            return [];
×
307

308
        try
309
        {
310
            var ret = new byte[count];
19,618✔
311
            Read(ret, 0, count);
19,618✔
312

313
            return ret;
19,618✔
314
        }
315
        finally
316
        {
317
            TrackRead<byte>(count, false);
19,618✔
318
        }
19,618✔
319
    }
19,618✔
320

321
    protected internal void GetLockOrThrow()
322
    {
323
        var obtained = false;
4,274,377✔
324
        PositionShiftLock.Enter(ref obtained);
4,274,377✔
325

326
        if (!obtained)
4,274,377!
327
            throw new Exception("Failed to obtain lock");
×
328
    }
4,274,377✔
329

330
    protected internal void ReleaseLock()
331
    {
332
        PositionShiftLock.Exit();
4,274,377✔
333
    }
4,274,377✔
334

335
    public ulong ReadNUintAtRawAddress(long offset)
336
    {
337
        if (offset > Length)
×
338
            throw new EndOfStreamException($"ReadNUintAtRawAddress: Offset 0x{offset:X} is beyond the end of the stream (length 0x{Length:X})");
×
339

340
        GetLockOrThrow();
×
341

342
        try
343
        {
344
            Position = offset;
×
345
            return ReadNUint();
×
346
        }
347
        finally
348
        {
349
            ReleaseLock();
×
350

351
            TrackRead<ulong>((int)PointerSize, false);
×
352
        }
×
353
    }
×
354

355
    public ulong[] ReadNUintArrayAtRawAddress(long offset, int count)
356
    {
357
        if (offset > Length)
489,041!
358
            throw new EndOfStreamException($"ReadNUintArrayAtRawAddress: Offset 0x{offset:X} is beyond the end of the stream (length 0x{Length:X})");
×
359

360
        var inBounds = offset + count * (int)PointerSize <= Length;
489,041✔
361
        if (!inBounds)
489,041!
362
            throw new EndOfStreamException($"ReadNUintArrayAtRawAddress: Attempted to read {count} pointers (pointer length {PointerSize}) at offset 0x{offset:X}, but this goes beyond the end of the stream (length 0x{Length:X})");
×
363

364
        GetLockOrThrow();
489,041✔
365

366
        try
367
        {
368
            Position = offset;
489,041✔
369

370
            var ret = new ulong[count];
489,041✔
371

372
            for (var i = 0; i < count; i++)
7,830,316✔
373
            {
374
                ret[i] = ReadNUint();
3,426,117✔
375
            }
376

377
            return ret;
489,041✔
378
        }
379
        finally
380
        {
381
            ReleaseLock();
489,041✔
382

383
            var bytesRead = count * (int)PointerSize;
489,041✔
384
            TrackRead<ulong>(bytesRead, false);
489,041✔
385
        }
489,041✔
386
    }
489,041✔
387

388

389
    /// <summary>
390
    /// Read a native-sized integer (i.e. 32 or 64 bit, depending on platform) at the current position
391
    /// </summary>
392
    public virtual long ReadNInt() => is32Bit ? ReadInt32() : ReadInt64();
×
393

394
    /// <summary>
395
    /// Read a native-sized unsigned integer (i.e. 32 or 64 bit, depending on platform) at the current position
396
    /// </summary>
397
    public virtual ulong ReadNUint() => is32Bit ? ReadUInt32() : ReadUInt64();
×
398

399
    protected void WriteWord(int position, ulong word) => WriteWord(position, (long)word);
441,488✔
400

401
    /// <summary>
402
    /// Used for ELF Relocations.
403
    /// </summary>
404
    protected void WriteWord(int position, long word)
405
    {
406
        if (_memoryStream == null)
441,488!
407
            throw new("WriteWord is not supported in non-memory-backed readers");
×
408

409
        GetLockOrThrow();
441,488✔
410

411
        try
412
        {
413
            byte[] rawBytes;
414
            if (is32Bit)
441,488✔
415
            {
416
                var value = (int)word;
1,579✔
417
                rawBytes = BitConverter.GetBytes(value);
1,579✔
418
            }
419
            else
420
            {
421
                rawBytes = BitConverter.GetBytes(word);
439,909✔
422
            }
423

424
            if (ShouldReverseArrays)
441,488!
425
                rawBytes = rawBytes.Reverse();
×
426

427
            if (position > _memoryStream.Length)
441,488!
428
                throw new Exception($"WriteWord: Position {position} beyond length {_memoryStream.Length}");
×
429

430
            var count = is32Bit ? 4 : 8;
441,488✔
431
            if (position + count > _memoryStream.Length)
441,488!
432
                throw new Exception($"WriteWord: Writing {count} bytes at {position} would go beyond length {_memoryStream.Length}");
×
433

434
            if (rawBytes.Length != count)
441,488!
435
                throw new Exception($"WriteWord: Expected {count} bytes from BitConverter, got {position}");
×
436

437
            try
438
            {
439
                _memoryStream.Seek(position, SeekOrigin.Begin);
441,488✔
440
                _memoryStream.Write(rawBytes, 0, count);
441,488✔
441
            }
441,488✔
442
            catch
×
443
            {
444
                Logging.LibLogger.ErrorNewline("WriteWord: Unexpected exception!");
×
445
                throw;
×
446
            }
447
        }
448
        finally
449
        {
450
            ReleaseLock();
441,488✔
451
        }
441,488✔
452
    }
441,488✔
453

454
    public T ReadReadableHereNoLock<T>() where T : ReadableClass, new() => InternalReadReadableClass<T>();
580,788✔
455

456
    public T ReadReadable<T>(long offset = -1) where T : ReadableClass, new()
457
    {
458
        GetLockOrThrow();
2,027,132✔
459

460
        if (offset >= 0)
2,027,132✔
461
            Position = offset;
2,027,073✔
462

463
        var initialPos = Position;
2,027,132✔
464

465
        try
466
        {
467
            return InternalReadReadableClass<T>();
2,027,132✔
468
        }
469
        finally
470
        {
471
            var bytesRead = (int)(Position - initialPos);
2,027,132✔
472
            TrackRead<T>(bytesRead, trackIfFinishedReading: true);
2,027,132✔
473

474
            ReleaseLock();
2,027,132✔
475
        }
2,027,132✔
476
    }
2,027,132✔
477

478
    public T[] ReadReadableArrayAtRawAddr<T>(long offset, long count) where T : ReadableClass, new()
479
    {
480
        var t = new T[count];
617✔
481

482
        GetLockOrThrow();
617✔
483

484
        if (offset != -1)
617✔
485
            Position = offset;
598✔
486

487
        try
488
        {
489
            //This handles the actual reading into the array, and tracking read counts, for us.
490
            FillReadableArrayHereNoLock(t);
617✔
491
        }
617✔
492
        finally
493
        {
494
            ReleaseLock();
617✔
495
        }
617✔
496

497
        return t;
617✔
498
    }
499

500
    public void FillReadableArrayHereNoLock<T>(T[] array, int startOffset = 0) where T : ReadableClass, new()
501
    {
502
        var initialPos = Position;
974✔
503

504
        try
505
        {
506
            var i = startOffset;
974✔
507
            for (; i < array.Length; i++)
13,599,902✔
508
            {
509
                array[i] = InternalReadReadableClass<T>();
6,799,464✔
510
            }
511
        }
974✔
512
        finally
513
        {
514
            var bytesRead = (int)(Position - initialPos);
974✔
515
            TrackRead<T>(bytesRead, trackIfFinishedReading: true);
974✔
516
        }
974✔
517
    }
974✔
518

519
    public void TrackRead<T>(int bytesRead, bool trackIfInReadableRead = true, bool trackIfFinishedReading = false)
520
    {
521
        if (!EnableReadableSizeInformation)
3,377,804!
522
            return;
3,377,804✔
523

524
        if (!trackIfInReadableRead && _inReadableRead)
×
525
            return;
×
526

527
        if (_hasFinishedInitialRead && !trackIfFinishedReading)
×
528
            return;
×
529

530
        BytesReadPerClass[typeof(T)] = BytesReadPerClass.GetOrDefault(typeof(T)) + bytesRead;
×
531
    }
×
532
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc