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

SamboyCoding / Cpp2IL / 12324180199

13 Dec 2024 10:46PM UTC coverage: 27.497% (-0.3%) from 27.79%
12324180199

push

github

SamboyCoding
Core: Fix more test failures

1251 of 6374 branches covered (19.63%)

Branch coverage included in aggregate %.

18 of 21 new or added lines in 1 file covered. (85.71%)

429 existing lines in 16 files now uncovered.

3360 of 10395 relevant lines covered (32.32%)

124828.12 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 abstract 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
    public abstract float MetadataVersion { get; }
31

32

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

38
    public ClassReadingBinaryReader(Stream input) : base(input)
×
39
    {
UNCOV
40
        _memoryStream = null;
×
UNCOV
41
    }
×
42

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

49
    public long Length => BaseStream.Length;
978,082✔
50

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

56
        if (type == typeof(char))
1,777,105!
UNCOV
57
            return ReadChar();
×
58

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

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

65
        if (type == typeof(short))
109,476!
UNCOV
66
            return ReadInt16();
×
67

68
        if (type == typeof(ushort))
109,476!
UNCOV
69
            return ReadUInt16();
×
70

71
        if (type == typeof(sbyte))
109,476!
UNCOV
72
            return ReadSByte();
×
73

74
        if (type == typeof(byte))
109,476!
UNCOV
75
            return ReadByte();
×
76

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

UNCOV
80
        if (type == typeof(ulong))
×
81
            return is32Bit && !overrideArchCheck ? ReadUInt32() : ReadUInt64();
×
82

UNCOV
83
        if (type == typeof(float))
×
84
            return ReadSingle();
×
85

UNCOV
86
        if (type == typeof(double))
×
87
            return ReadDouble();
×
88

UNCOV
89
        return null;
×
90
    }
91

92
    public uint ReadUnityCompressedUIntAtRawAddr(long offset, out int bytesRead)
93
    {
UNCOV
94
        GetLockOrThrow();
×
95

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

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

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

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

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

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

144

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

148
    public int ReadUnityCompressedIntAtRawAddr(long position, out int bytesRead)
UNCOV
149
        => ReadUnityCompressedIntAtRawAddr(position, true, out bytesRead);
×
150

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

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

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

168
        return (int)unsigned;
18,774✔
169
    }
170

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

176
    private T InternalReadReadableClass<T>() where T : ReadableClass, new()
177
    {
178
        var t = new T { MetadataVersion = MetadataVersion };
9,407,384✔
179

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

191
        return t;
9,407,384✔
192
    }
193

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

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

UNCOV
205
            return value!;
×
206
        }
207

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

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

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

221
        return value!;
1,606,353✔
222
    }
223

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

228
        GetLockOrThrow();
141✔
229

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

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

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

247
    public string ReadStringToNull(ulong offset) => ReadStringToNull((long)offset);
7,644✔
248

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

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

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

267
        if (offset != -1)
840,682✔
268
            Position = offset;
840,682✔
269

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

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

285
    public string ReadStringToNullAtCurrentPos()
UNCOV
286
        => ReadStringToNullNoLock(-1);
×
287

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

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

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

307
        if (count == 0)
19,618!
UNCOV
308
            return [];
×
309

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

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

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

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

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

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

UNCOV
342
        GetLockOrThrow();
×
343

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

353
            TrackRead<ulong>((int)PointerSize, false);
×
UNCOV
354
        }
×
UNCOV
355
    }
×
356

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

362
        var inBounds = offset + count * (int)PointerSize <= Length;
489,041✔
363
        if (!inBounds)
489,041!
UNCOV
364
            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})");
×
365

366
        GetLockOrThrow();
489,041✔
367

368
        try
369
        {
370
            Position = offset;
489,041✔
371

372
            var ret = new ulong[count];
489,041✔
373

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

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

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

390

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

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

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

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

411
        GetLockOrThrow();
441,488✔
412

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

426
            if (ShouldReverseArrays)
441,488!
UNCOV
427
                rawBytes = rawBytes.Reverse();
×
428

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

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

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

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

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

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

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

465
        var initialPos = Position;
2,027,132✔
466

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

476
            ReleaseLock();
2,027,132✔
477
        }
2,027,132✔
478
    }
2,027,132✔
479

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

484
        GetLockOrThrow();
617✔
485

486
        if (offset != -1)
617✔
487
            Position = offset;
598✔
488

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

499
        return t;
617✔
500
    }
501

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

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

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

UNCOV
526
        if (!trackIfInReadableRead && _inReadableRead)
×
527
            return;
×
528

UNCOV
529
        if (_hasFinishedInitialRead && !trackIfFinishedReading)
×
530
            return;
×
531

UNCOV
532
        BytesReadPerClass[typeof(T)] = BytesReadPerClass.GetOrDefault(typeof(T)) + bytesRead;
×
UNCOV
533
    }
×
534
}
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

© 2026 Coveralls, Inc