Worldstone
 All Classes Files Functions Variables Enumerations Enumerator Macros Pages
dc6.cpp
1 //
2 // Created by Lectem on 23/10/2016.
3 //
4 
5 #include "dc6.h"
6 #include <FileStream.h>
7 #include <SystemUtils.h>
8 #include <fmt/format.h>
9 #include "Palette.h"
10 #include "utils.h"
11 
12 // TODO : Remove asserts and replace with proper error handling
13 
14 namespace WorldStone
15 {
16 
17 bool DC6::initDecoder(StreamPtr&& streamPtr)
18 {
19  assert(!stream);
20  stream = std::move(streamPtr);
21  if (stream && stream->good()) {
22  return extractHeaders();
23  }
24  return false;
25 }
26 
27 bool DC6::extractHeaders()
28 {
29  static_assert(std::is_trivially_copyable<Header>(), "DC6::Header must be trivially copyable");
30  static_assert(sizeof(Header) == 6 * sizeof(uint32_t), "DC6::Header struct needs to be packed");
31  stream->read(&header, sizeof(header));
32  if (stream->fail()) return false;
33 
34  size_t framesNumber = size_t(header.directions) * size_t(header.framesPerDir);
35  frameHeaders.resize(framesNumber);
36 
37  framePointers.resize(framesNumber);
38  stream->read(framePointers.data(), sizeof(uint32_t) * framesNumber);
39  if (stream->fail()) return false;
40 
41  for (size_t i = 0; i < framesNumber; ++i)
42  {
43  FrameHeader& frameHeader = frameHeaders[i];
44  stream->seek(framePointers[i], IStream::beg);
45 
46  static_assert(std::is_trivially_copyable<FrameHeader>(),
47  "DC6::FrameHeader must be trivially copyable");
48  static_assert(sizeof(FrameHeader) == 8 * sizeof(uint32_t),
49  "DC6::FrameHeader struct needs to be packed");
50  stream->read(&frameHeader, sizeof(frameHeader));
51  if (stream->fail()) return false;
52  }
53  return true;
54 }
55 
56 std::vector<uint8_t> DC6::decompressFrame(size_t frameNumber) const
57 {
58  const FrameHeader& fHeader = frameHeaders[frameNumber];
59  // Allocate memory for the decoded data
60  std::vector<uint8_t> data(static_cast<size_t>(fHeader.width * fHeader.height));
61 
62  if (decompressFrameIn(frameNumber, data.data()))return data;
63  else return {};
64 }
65 
66 bool DC6::decompressFrameIn(size_t frameNumber, uint8_t* data) const
67 {
68  assert(stream != nullptr);
69  stream->seek(framePointers[frameNumber] + sizeof(FrameHeader), IStream::beg);
70  const FrameHeader& fHeader = frameHeaders[frameNumber];
71  assert(fHeader.width > 0 && fHeader.height > 0);
72 
73  // TODO: figure if we should invert data here or let the renderer do it
74  // assert(!fHeader.flip);
75 
76  // We're reading it bottom to top, but save data with the y axis from top to
77  // bottom
78  int x = 0, y = fHeader.height - 1;
79  size_t rawIndex = 0;
80  while (rawIndex < fHeader.length)
81  {
82  int val = stream->getc();
83  rawIndex++;
84  if (stream->eof()) return false;
85  uint8_t chunkSize = static_cast<uint8_t>(val);
86  if (chunkSize == 0x80) // end of line
87  {
88  x = 0;
89  y--;
90  }
91  else if (chunkSize & 0x80) // chunkSize & 0x80 is the number of transparent pixels
92  {
93  x += chunkSize & 0x7F;
94  }
95  else // chunkSize is the number of colors to read
96  {
97  assert(chunkSize + x <= fHeader.width);
98  stream->read(data + x + fHeader.width * y, chunkSize);
99  rawIndex += chunkSize;
100  x += chunkSize;
101  if (stream->eof()) return false;
102  }
103  }
104  assert(fHeader.length == rawIndex);
105  return true;
106 }
107 
108 void DC6::exportToPPM(const char* ppmFilenameBase, const Palette& palette) const
109 {
110  for (size_t dir = 0; dir < header.directions; ++dir)
111  {
112  for (size_t frameInDir = 0; frameInDir < header.framesPerDir; ++frameInDir)
113  {
114  size_t frame = dir * header.framesPerDir + frameInDir;
115  auto data = decompressFrame(frame);
116  Utils::exportToPPM(fmt::format("{}{}-{}.ppm", ppmFilenameBase, dir, frameInDir).c_str(),
117  data.data(), frameHeaders[frame].width, frameHeaders[frame].height,
118  palette);
119  }
120  }
121 }
122 } // namespace WorldStone
uint32_t length
Length of the frame in chunks.
Definition: dc6.h:68
std::vector< uint8_t > decompressFrame(size_t frameNumber) const
Decompress the given frame.
Definition: dc6.cpp:56
int32_t width
Width in pixels.
Definition: dc6.h:62
bool initDecoder(StreamPtr &&streamPtr)
Start decoding the stream and preparing data.
Definition: dc6.cpp:17
uint32_t directions
Number of directions in this file.
Definition: dc6.h:55
bool decompressFrameIn(size_t frameNumber, uint8_t *data) const
Same as decompressFrame but will output the data in a given buffer.
Definition: dc6.cpp:66
uint32_t framesPerDir
Number of frames for each direction.
Definition: dc6.h:56
int32_t height
Height in pixels.
Definition: dc6.h:63
Helper to load a Diablo 2 palette (.pal/.dat format)
Definition: Palette.h:21