Coverage Report

Created: 2025-08-28 06:38

/src/assimp/code/AssetLib/MMD/MMDPmdParser.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2025, assimp team
6
7
All rights reserved.
8
9
Redistribution and use of this software in source and binary forms,
10
with or without modification, are permitted provided that the
11
following conditions are met:
12
13
* Redistributions of source code must retain the above
14
copyright notice, this list of conditions and the
15
following disclaimer.
16
17
* Redistributions in binary form must reproduce the above
18
copyright notice, this list of conditions and the
19
following disclaimer in the documentation and/or other
20
materials provided with the distribution.
21
22
* Neither the name of the assimp team, nor the names of its
23
contributors may be used to endorse or promote products
24
derived from this software without specific prior
25
written permission of the assimp team.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39
----------------------------------------------------------------------
40
*/
41
#pragma once
42
43
#include <vector>
44
#include <string>
45
#include <memory>
46
#include <iostream>
47
#include <fstream>
48
#include "MMDCpp14.h"
49
50
namespace pmd {
51
52
struct PmdHeader {
53
    std::string name;
54
    std::string name_english;
55
    std::string comment;
56
    std::string comment_english;
57
58
    PmdHeader() = default;
59
    ~PmdHeader() = default;
60
61
0
    bool Read(std::ifstream *stream) {
62
0
        if (stream == nullptr) {
63
0
            return false;
64
0
        }
65
0
        char buffer[256] = {};
66
0
        stream->read(buffer, 20);
67
0
        name = std::string(buffer);
68
0
        stream->read(buffer, 256);
69
0
        comment = std::string(buffer);
70
0
        return true;
71
0
    }
72
73
0
    bool ReadExtension(std::ifstream *stream) {
74
0
        if (stream == nullptr) {
75
0
            return false;
76
0
        }
77
0
78
0
        char buffer[256] = {};
79
0
        stream->read(buffer, 20);
80
0
        name_english = std::string(buffer);
81
0
        stream->read(buffer, 256);
82
0
        comment_english = std::string(buffer);
83
0
84
0
        return true;
85
0
    }
86
};
87
88
struct PmdVertex {
89
    float position[3];
90
    float normal[3];
91
    float uv[2];
92
    uint16_t bone_index[2];
93
    uint8_t bone_weight;
94
    bool edge_invisible;
95
96
    PmdVertex() :
97
0
            position{ 0.0f }, normal{ 0.0f }, uv{ 0.0f }, bone_index{ 0 }, bone_weight(0), edge_invisible(false) {}
98
99
    ~PmdVertex() = default;
100
101
0
    bool Read(std::ifstream *stream) {
102
0
        if (stream == nullptr) {
103
0
            return false;
104
0
        }
105
0
106
0
        stream->read((char *)position, sizeof(float) * 3);
107
0
        stream->read((char *)normal, sizeof(float) * 3);
108
0
        stream->read((char *)uv, sizeof(float) * 2);
109
0
        stream->read((char *)bone_index, sizeof(uint16_t) * 2);
110
0
        stream->read((char *)&bone_weight, sizeof(uint8_t));
111
0
        stream->read((char *)&edge_invisible, sizeof(uint8_t));
112
0
        return true;
113
0
    }
114
};
115
116
struct PmdMaterial {
117
    float diffuse[4];
118
    float power;
119
    float specular[3];
120
    float ambient[3];
121
    uint8_t toon_index;
122
    uint8_t edge_flag;
123
    uint32_t index_count;
124
    std::string texture_filename;
125
    std::string sphere_filename;
126
127
    PmdMaterial() :
128
0
            diffuse{ 0.0f }, power(0.0f), specular{ 0.0f }, ambient{ 0.0f }, toon_index(0), edge_flag(0), index_count(0), texture_filename(), sphere_filename() {}
129
130
    ~PmdMaterial() = default;
131
132
0
    bool Read(std::ifstream *stream) {
133
0
        if (stream == nullptr) {
134
0
            return false;
135
0
        }
136
0
        constexpr size_t BufferSize = 20;
137
0
138
0
        char buffer[BufferSize] = {};
139
0
        stream->read((char *)&diffuse, sizeof(float) * 4);
140
0
        stream->read((char *)&power, sizeof(float));
141
0
        stream->read((char *)&specular, sizeof(float) * 3);
142
0
        stream->read((char *)&ambient, sizeof(float) * 3);
143
0
        stream->read((char *)&toon_index, sizeof(uint8_t));
144
0
        stream->read((char *)&edge_flag, sizeof(uint8_t));
145
0
        stream->read((char *)&index_count, sizeof(uint32_t));
146
0
        stream->read((char *)&buffer, sizeof(char) * BufferSize);
147
0
        char *pstar = strchr(buffer, '*');
148
0
        if (nullptr == pstar) {
149
0
            texture_filename = std::string(buffer);
150
0
            sphere_filename.clear();
151
0
        } else {
152
0
            *pstar = 0;
153
0
            texture_filename = std::string(buffer);
154
0
            sphere_filename = std::string(pstar + 1);
155
0
        }
156
0
157
0
        return true;
158
0
    }
159
};
160
161
enum class BoneType : uint8_t {
162
    Rotation,
163
    RotationAndMove,
164
    IkEffector,
165
    Unknown,
166
    IkEffectable,
167
    RotationEffectable,
168
    IkTarget,
169
    Invisible,
170
    Twist,
171
    RotationMovement
172
173
};
174
175
struct PmdBone {
176
    std::string name;
177
    std::string name_english;
178
    uint16_t parent_bone_index;
179
    uint16_t tail_pos_bone_index;
180
    BoneType bone_type;
181
    uint16_t ik_parent_bone_index;
182
    float bone_head_pos[3];
183
184
    PmdBone() :
185
0
            name(), name_english(), parent_bone_index(0), tail_pos_bone_index(0), bone_type(BoneType::Unknown), ik_parent_bone_index(0), bone_head_pos{ 0.0f } {}
186
187
    ~PmdBone() = default;
188
189
0
    void Read(std::istream *stream) {
190
0
        if (stream == nullptr) {
191
0
            return;
192
0
        }
193
0
        constexpr size_t BufferSize = 20;
194
0
        char buffer[BufferSize] = {};
195
0
        stream->read(buffer, BufferSize);
196
0
        name = std::string(buffer);
197
0
        stream->read((char *)&parent_bone_index, sizeof(uint16_t));
198
0
        stream->read((char *)&tail_pos_bone_index, sizeof(uint16_t));
199
0
        stream->read((char *)&bone_type, sizeof(uint8_t));
200
0
        stream->read((char *)&ik_parent_bone_index, sizeof(uint16_t));
201
0
        stream->read((char *)&bone_head_pos, sizeof(float) * 3);
202
0
    }
203
204
0
    void ReadExpantion(std::istream *stream) {
205
0
        if (stream == nullptr) {
206
0
            return;
207
0
        }
208
0
        constexpr size_t BufferSize = 20;
209
0
        char buffer[BufferSize] = {};
210
0
        stream->read(buffer, BufferSize);
211
0
        name_english = std::string(buffer);
212
0
    }
213
};
214
215
struct PmdIk {
216
    uint16_t ik_bone_index;
217
    uint16_t target_bone_index;
218
    uint16_t iterations;
219
    float angle_limit;
220
    std::vector<uint16_t> ik_child_bone_index;
221
222
0
    PmdIk() : ik_bone_index(0), target_bone_index(0), iterations(0), angle_limit(0.0f) {}
223
224
    ~PmdIk() = default;
225
226
0
    void Read(std::istream *stream) {
227
0
        if (stream == nullptr) {
228
0
            return;
229
0
        }
230
0
231
0
        stream->read((char *)&ik_bone_index, sizeof(uint16_t));
232
0
        stream->read((char *)&target_bone_index, sizeof(uint16_t));
233
0
        uint8_t ik_chain_length;
234
0
        stream->read((char *)&ik_chain_length, sizeof(uint8_t));
235
0
        stream->read((char *)&iterations, sizeof(uint16_t));
236
0
        stream->read((char *)&angle_limit, sizeof(float));
237
0
        ik_child_bone_index.resize(ik_chain_length);
238
0
        for (int i = 0; i < ik_chain_length; i++) {
239
0
            stream->read((char *)&ik_child_bone_index[i], sizeof(uint16_t));
240
0
        }
241
0
    }
242
};
243
244
struct PmdFaceVertex {
245
    int vertex_index;
246
    float position[3];
247
248
    PmdFaceVertex() :
249
0
            vertex_index(0), position{ 0.0f } {}
250
251
    ~PmdFaceVertex() = default;
252
253
0
    void Read(std::istream *stream) {
254
0
        if (stream == nullptr) {
255
0
            return;
256
0
        }
257
0
        stream->read((char *)&vertex_index, sizeof(int));
258
0
        stream->read((char *)position, sizeof(float) * 3);
259
0
    }
260
};
261
262
enum class FaceCategory : uint8_t {
263
    Base,
264
    Eyebrow,
265
    Eye,
266
    Mouth,
267
    Other
268
};
269
270
struct PmdFace {
271
    std::string name;
272
    FaceCategory type;
273
    std::vector<PmdFaceVertex> vertices;
274
    std::string name_english;
275
276
    PmdFace() :
277
0
            name(), type(FaceCategory::Other), vertices(), name_english() {}
278
279
    ~PmdFace() = default;
280
281
0
    void Read(std::istream *stream) {
282
0
        if (stream == nullptr) {
283
0
            return;
284
0
        }
285
0
        constexpr size_t BufferSize = 20;
286
0
        char buffer[BufferSize];
287
0
        stream->read(buffer, BufferSize);
288
0
        name = std::string(buffer);
289
0
        int vertex_count;
290
0
        stream->read((char *)&vertex_count, sizeof(int));
291
0
        stream->read((char *)&type, sizeof(uint8_t));
292
0
        vertices.resize(vertex_count);
293
0
        for (int i = 0; i < vertex_count; i++) {
294
0
            vertices[i].Read(stream);
295
0
        }
296
0
    }
297
298
0
    void ReadExpantion(std::istream *stream) {
299
0
        if (stream == nullptr) {
300
0
            return;
301
0
        }
302
0
303
0
        char buffer[20];
304
0
        stream->read(buffer, 20);
305
0
        name_english = std::string(buffer);
306
0
    }
307
};
308
309
struct PmdBoneDispName {
310
    std::string bone_disp_name;
311
    std::string bone_disp_name_english;
312
313
    PmdBoneDispName() = default;
314
315
    ~PmdBoneDispName() = default;
316
317
0
    void Read(std::istream *stream) {
318
0
        if (stream == nullptr) {
319
0
            return;
320
0
        }
321
0
        char buffer[50];
322
0
        stream->read(buffer, 50);
323
0
        bone_disp_name = std::string(buffer);
324
0
        bone_disp_name_english.clear();
325
0
    }
326
327
0
    void ReadExpantion(std::istream *stream) {
328
0
        if (stream == nullptr) {
329
0
            return;
330
0
        }
331
0
        char buffer[50];
332
0
        stream->read(buffer, 50);
333
0
        bone_disp_name_english = std::string(buffer);
334
0
    }
335
};
336
337
struct PmdBoneDisp {
338
    uint16_t bone_index;
339
    uint8_t bone_disp_index;
340
341
    PmdBoneDisp() :
342
0
            bone_index(0), bone_disp_index(0) {}
343
344
    ~PmdBoneDisp() = default;
345
346
0
    void Read(std::istream *stream) {
347
0
        if (stream == nullptr) {
348
0
            return;
349
0
        }
350
0
351
0
        stream->read((char *)&bone_index, sizeof(uint16_t));
352
0
        stream->read((char *)&bone_disp_index, sizeof(uint8_t));
353
0
    }
354
};
355
356
enum class RigidBodyShape : uint8_t {
357
    Sphere = 0,
358
    Box = 1,
359
    Cpusel = 2
360
};
361
362
enum class RigidBodyType : uint8_t {
363
    BoneConnected = 0,
364
    Physics = 1,
365
    ConnectedPhysics = 2
366
};
367
368
struct PmdRigidBody {
369
    std::string name;
370
    uint16_t related_bone_index;
371
    uint8_t group_index;
372
    uint16_t mask;
373
    RigidBodyShape shape;
374
    float size[3];
375
    float position[3];
376
    float orientation[3];
377
    float weight;
378
    float linear_damping;
379
    float anglar_damping;
380
    float restitution;
381
    float friction;
382
    RigidBodyType rigid_type;
383
384
    PmdRigidBody() :
385
0
            name(), related_bone_index(0), group_index(0), mask(0), shape(RigidBodyShape::Box), size{ 0.0f }, position{ 0.0f }, weight(0.0f), linear_damping(0.0f), anglar_damping(0.0f), restitution(0.0f), friction(0.0f), rigid_type(RigidBodyType::BoneConnected) {}
386
387
    ~PmdRigidBody() = default;
388
389
0
    void Read(std::istream *stream) {
390
0
        if (stream == nullptr) {
391
0
            return;
392
0
        }
393
0
394
0
        char buffer[20];
395
0
        stream->read(buffer, sizeof(char) * 20);
396
0
        name = (std::string(buffer));
397
0
        stream->read((char *)&related_bone_index, sizeof(uint16_t));
398
0
        stream->read((char *)&group_index, sizeof(uint8_t));
399
0
        stream->read((char *)&mask, sizeof(uint16_t));
400
0
        stream->read((char *)&shape, sizeof(uint8_t));
401
0
        stream->read((char *)size, sizeof(float) * 3);
402
0
        stream->read((char *)position, sizeof(float) * 3);
403
0
        stream->read((char *)orientation, sizeof(float) * 3);
404
0
        stream->read((char *)&weight, sizeof(float));
405
0
        stream->read((char *)&linear_damping, sizeof(float));
406
0
        stream->read((char *)&anglar_damping, sizeof(float));
407
0
        stream->read((char *)&restitution, sizeof(float));
408
0
        stream->read((char *)&friction, sizeof(float));
409
0
        stream->read((char *)&rigid_type, sizeof(char));
410
0
    }
411
};
412
413
struct PmdConstraint {
414
    std::string name;
415
    uint32_t rigid_body_index_a;
416
    uint32_t rigid_body_index_b;
417
    float position[3];
418
    float orientation[3];
419
    float linear_lower_limit[3];
420
    float linear_upper_limit[3];
421
    float angular_lower_limit[3];
422
    float angular_upper_limit[3];
423
    float linear_stiffness[3];
424
    float angular_stiffness[3];
425
426
    PmdConstraint() :
427
0
            name(), rigid_body_index_a(0), rigid_body_index_b(0), position{ 0.0f }, orientation{ 0.0f }, linear_lower_limit{ 0.0f }, linear_upper_limit{ 0.0f }, angular_lower_limit{ 0.0f }, angular_upper_limit{ 0.0f }, linear_stiffness{ 0.0f }, angular_stiffness{ 0.0f } {}
428
429
    ~PmdConstraint() = default;
430
431
0
    void Read(std::istream *stream) {
432
0
        if (stream == nullptr) {
433
0
            return;
434
0
        }
435
0
436
0
        char buffer[20];
437
0
        stream->read(buffer, 20);
438
0
        name = std::string(buffer);
439
0
        stream->read((char *)&rigid_body_index_a, sizeof(uint32_t));
440
0
        stream->read((char *)&rigid_body_index_b, sizeof(uint32_t));
441
0
        stream->read((char *)position, sizeof(float) * 3);
442
0
        stream->read((char *)orientation, sizeof(float) * 3);
443
0
        stream->read((char *)linear_lower_limit, sizeof(float) * 3);
444
0
        stream->read((char *)linear_upper_limit, sizeof(float) * 3);
445
0
        stream->read((char *)angular_lower_limit, sizeof(float) * 3);
446
0
        stream->read((char *)angular_upper_limit, sizeof(float) * 3);
447
0
        stream->read((char *)linear_stiffness, sizeof(float) * 3);
448
0
        stream->read((char *)angular_stiffness, sizeof(float) * 3);
449
0
    }
450
};
451
452
struct PmdModel {
453
    float version;
454
    PmdHeader header;
455
    std::vector<PmdVertex> vertices;
456
    std::vector<uint16_t> indices;
457
    std::vector<PmdMaterial> materials;
458
    std::vector<PmdBone> bones;
459
    std::vector<PmdIk> iks;
460
    std::vector<PmdFace> faces;
461
    std::vector<uint16_t> faces_indices;
462
    std::vector<PmdBoneDispName> bone_disp_name;
463
    std::vector<PmdBoneDisp> bone_disp;
464
    std::vector<std::string> toon_filenames;
465
    std::vector<PmdRigidBody> rigid_bodies;
466
    std::vector<PmdConstraint> constraints;
467
468
    PmdModel() :
469
0
            version(0.0f) {}
470
471
    ~PmdModel() = default;
472
473
0
    static std::unique_ptr<PmdModel> LoadFromFile(const char *filename) {
474
0
        if (filename == nullptr) {
475
0
            return nullptr;
476
0
        }
477
0
478
0
        std::ifstream stream(filename, std::ios::binary);
479
0
        if (stream.fail()) {
480
0
            std::cerr << "could not open \"" << filename << "\"" << std::endl;
481
0
            return nullptr;
482
0
        }
483
0
        auto result = LoadFromStream(&stream);
484
0
        stream.close();
485
0
        return result;
486
0
    }
487
488
0
    static std::unique_ptr<PmdModel> LoadFromStream(std::ifstream *stream) {
489
0
        auto result = mmd::make_unique<PmdModel>();
490
0
        char buffer[100];
491
0
492
0
        // magic
493
0
        char magic[3];
494
0
        stream->read(magic, 3);
495
0
        if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd') {
496
0
            std::cerr << "invalid file" << std::endl;
497
0
            return nullptr;
498
0
        }
499
0
500
0
        // version
501
0
        stream->read((char *)&(result->version), sizeof(float));
502
0
        if (result->version != 1.0f) {
503
0
            std::cerr << "invalid version" << std::endl;
504
0
            return nullptr;
505
0
        }
506
0
507
0
        // header
508
0
        result->header.Read(stream);
509
0
510
0
        // vertices
511
0
        uint32_t vertex_num;
512
0
        stream->read((char *)&vertex_num, sizeof(uint32_t));
513
0
        result->vertices.resize(vertex_num);
514
0
        for (uint32_t i = 0; i < vertex_num; i++) {
515
0
            result->vertices[i].Read(stream);
516
0
        }
517
0
518
0
        // indices
519
0
        uint32_t index_num;
520
0
        stream->read((char *)&index_num, sizeof(uint32_t));
521
0
        result->indices.resize(index_num);
522
0
        for (uint32_t i = 0; i < index_num; i++) {
523
0
            stream->read((char *)&result->indices[i], sizeof(uint16_t));
524
0
        }
525
0
526
0
        // materials
527
0
        uint32_t material_num;
528
0
        stream->read((char *)&material_num, sizeof(uint32_t));
529
0
        result->materials.resize(material_num);
530
0
        for (uint32_t i = 0; i < material_num; i++) {
531
0
            result->materials[i].Read(stream);
532
0
        }
533
0
534
0
        // bones
535
0
        uint16_t bone_num;
536
0
        stream->read((char *)&bone_num, sizeof(uint16_t));
537
0
        result->bones.resize(bone_num);
538
0
        for (uint32_t i = 0; i < bone_num; i++) {
539
0
            result->bones[i].Read(stream);
540
0
        }
541
0
542
0
        // iks
543
0
        uint16_t ik_num;
544
0
        stream->read((char *)&ik_num, sizeof(uint16_t));
545
0
        result->iks.resize(ik_num);
546
0
        for (uint32_t i = 0; i < ik_num; i++) {
547
0
            result->iks[i].Read(stream);
548
0
        }
549
0
550
0
        // faces
551
0
        uint16_t face_num;
552
0
        stream->read((char *)&face_num, sizeof(uint16_t));
553
0
        result->faces.resize(face_num);
554
0
        for (uint32_t i = 0; i < face_num; i++) {
555
0
            result->faces[i].Read(stream);
556
0
        }
557
0
558
0
        // face frames
559
0
        uint8_t face_frame_num;
560
0
        stream->read((char *)&face_frame_num, sizeof(uint8_t));
561
0
        result->faces_indices.resize(face_frame_num);
562
0
        for (uint32_t i = 0; i < face_frame_num; i++) {
563
0
            stream->read((char *)&result->faces_indices[i], sizeof(uint16_t));
564
0
        }
565
0
566
0
        // bone names
567
0
        uint8_t bone_disp_num;
568
0
        stream->read((char *)&bone_disp_num, sizeof(uint8_t));
569
0
        result->bone_disp_name.resize(bone_disp_num);
570
0
        for (uint32_t i = 0; i < bone_disp_num; i++) {
571
0
            result->bone_disp_name[i].Read(stream);
572
0
        }
573
0
574
0
        // bone frame
575
0
        uint32_t bone_frame_num;
576
0
        stream->read((char *)&bone_frame_num, sizeof(uint32_t));
577
0
        result->bone_disp.resize(bone_frame_num);
578
0
        for (uint32_t i = 0; i < bone_frame_num; i++) {
579
0
            result->bone_disp[i].Read(stream);
580
0
        }
581
0
582
0
        // english name
583
0
        bool english;
584
0
        stream->read((char *)&english, sizeof(char));
585
0
        if (english) {
586
0
            result->header.ReadExtension(stream);
587
0
            for (uint32_t i = 0; i < bone_num; i++) {
588
0
                result->bones[i].ReadExpantion(stream);
589
0
            }
590
0
            for (uint32_t i = 0; i < face_num; i++) {
591
0
                if (result->faces[i].type == pmd::FaceCategory::Base) {
592
0
                    continue;
593
0
                }
594
0
                result->faces[i].ReadExpantion(stream);
595
0
            }
596
0
            for (uint32_t i = 0; i < result->bone_disp_name.size(); i++) {
597
0
                result->bone_disp_name[i].ReadExpantion(stream);
598
0
            }
599
0
        }
600
0
601
0
        // toon textures
602
0
        if (stream->peek() == std::ios::traits_type::eof()) {
603
0
            result->toon_filenames.clear();
604
0
        } else {
605
0
            result->toon_filenames.resize(10);
606
0
            for (uint32_t i = 0; i < 10; i++) {
607
0
                stream->read(buffer, 100);
608
0
                result->toon_filenames[i] = std::string(buffer);
609
0
            }
610
0
        }
611
0
612
0
        // physics
613
0
        if (stream->peek() == std::ios::traits_type::eof()) {
614
0
            result->rigid_bodies.clear();
615
0
            result->constraints.clear();
616
0
        } else {
617
0
            uint32_t rigid_body_num;
618
0
            stream->read((char *)&rigid_body_num, sizeof(uint32_t));
619
0
            result->rigid_bodies.resize(rigid_body_num);
620
0
            for (uint32_t i = 0; i < rigid_body_num; i++) {
621
0
                result->rigid_bodies[i].Read(stream);
622
0
            }
623
0
            uint32_t constraint_num;
624
0
            stream->read((char *)&constraint_num, sizeof(uint32_t));
625
0
            result->constraints.resize(constraint_num);
626
0
            for (uint32_t i = 0; i < constraint_num; i++) {
627
0
                result->constraints[i].Read(stream);
628
0
            }
629
0
        }
630
0
631
0
        if (stream->peek() != std::ios::traits_type::eof()) {
632
0
            std::cerr << "there is unknown data" << std::endl;
633
0
        }
634
0
635
0
        return result;
636
0
    }
637
};
638
639
} // namespace pmd
640