|
1 | 1 | /***************************************************************************
|
2 | 2 | * fheroes2: https://github.com/ihhub/fheroes2 *
|
3 |
| - * Copyright (C) 2022 * |
| 3 | + * Copyright (C) 2022 - 2023 * |
4 | 4 | * *
|
5 | 5 | * Free Heroes2 Engine: http://sourceforge.net/projects/fheroes2 *
|
6 | 6 | * Copyright (C) 2009 by Andrey Afletdinov <[email protected]> *
|
|
23 | 23 |
|
24 | 24 | #include <cstdint>
|
25 | 25 | #include <cstdlib>
|
| 26 | +#include <filesystem> |
26 | 27 | #include <iomanip>
|
27 | 28 | #include <iostream>
|
28 | 29 | #include <memory>
|
29 | 30 | #include <sstream> // IWYU pragma: keep
|
30 | 31 | #include <string>
|
| 32 | +#include <system_error> |
| 33 | +#include <type_traits> |
31 | 34 | #include <vector>
|
32 | 35 |
|
33 | 36 | #include "agg_file.h"
|
|
39 | 42 |
|
40 | 43 | int main( int argc, char ** argv )
|
41 | 44 | {
|
42 |
| - if ( argc != 4 ) { |
43 |
| - std::cout << argv[0] << " inputFile.icn destinationDirectory paletteFileName.pal" << std::endl; |
| 45 | + if ( argc < 4 ) { |
| 46 | + std::cerr << argv[0] << " destinationDirectory paletteFileName.pal inputFile.icn ..." << std::endl; |
44 | 47 | return EXIT_FAILURE;
|
45 | 48 | }
|
46 | 49 |
|
47 |
| - std::string inputFileName( argv[1] ); |
48 |
| - std::string prefix( argv[2] ); |
49 |
| - |
50 |
| - std::string paletteFileName( argv[3] ); |
| 50 | + const char * dstDir = argv[1]; |
| 51 | + const char * paletteFileName = argv[2]; |
51 | 52 |
|
52 | 53 | StreamFile paletteFile;
|
53 | 54 | if ( !paletteFile.open( paletteFileName, "rb" ) ) {
|
54 |
| - std::cout << "Cannot open " << paletteFileName << std::endl; |
| 55 | + std::cerr << "Cannot open " << paletteFileName << std::endl; |
55 | 56 | return EXIT_FAILURE;
|
56 | 57 | }
|
57 | 58 |
|
58 |
| - std::vector<uint8_t> palette = paletteFile.getRaw(); |
| 59 | + const std::vector<uint8_t> palette = paletteFile.getRaw(); |
59 | 60 | if ( palette.size() != 768 ) {
|
60 |
| - std::cout << "Invalid palette size of " << palette.size() << " instead of 768." << std::endl; |
| 61 | + std::cerr << "Invalid palette size of " << palette.size() << " instead of 768." << std::endl; |
61 | 62 | return EXIT_FAILURE;
|
62 | 63 | }
|
63 | 64 |
|
64 | 65 | fheroes2::setGamePalette( palette );
|
65 | 66 |
|
66 |
| - StreamFile sf; |
67 |
| - |
68 |
| - if ( !sf.open( inputFileName, "rb" ) ) { |
69 |
| - std::cout << "Cannot open " << inputFileName << std::endl; |
70 |
| - return EXIT_FAILURE; |
| 67 | + std::vector<std::string> inputFileNames; |
| 68 | + for ( int i = 3; i < argc; ++i ) { |
| 69 | + if ( System::isShellLevelGlobbingSupported() ) { |
| 70 | + inputFileNames.emplace_back( argv[i] ); |
| 71 | + } |
| 72 | + else { |
| 73 | + System::globFiles( argv[i], inputFileNames ); |
| 74 | + } |
71 | 75 | }
|
72 | 76 |
|
73 |
| - int count_sprite = sf.getLE16(); |
74 |
| - int total_size = sf.getLE32(); |
| 77 | + for ( const std::string & inputFileName : inputFileNames ) { |
| 78 | + StreamFile sf; |
75 | 79 |
|
76 |
| - inputFileName.replace( inputFileName.find( ".icn" ), 4, "" ); |
77 |
| - prefix = System::concatPath( prefix, inputFileName ); |
| 80 | + if ( !sf.open( inputFileName, "rb" ) ) { |
| 81 | + std::cerr << "Cannot open " << inputFileName << std::endl; |
| 82 | + return EXIT_FAILURE; |
| 83 | + } |
78 | 84 |
|
79 |
| - if ( 0 != System::MakeDirectory( prefix ) ) { |
80 |
| - std::cout << "error mkdir: " << prefix << std::endl; |
81 |
| - return EXIT_SUCCESS; |
82 |
| - } |
| 85 | + const std::filesystem::path prefix = std::filesystem::path( dstDir ) / std::filesystem::path( inputFileName ).stem(); |
| 86 | + |
| 87 | + std::error_code ec; |
| 88 | + |
| 89 | + // Using the non-throwing overloads |
| 90 | + if ( !std::filesystem::exists( prefix, ec ) && !std::filesystem::create_directories( prefix, ec ) ) { |
| 91 | + std::cerr << "Cannot create directory " << prefix << std::endl; |
| 92 | + return EXIT_FAILURE; |
| 93 | + } |
| 94 | + |
| 95 | + std::cout << std::endl << "Processing " << inputFileName << "..." << std::endl << std::endl; |
83 | 96 |
|
84 |
| - size_t save_pos = sf.tell(); |
| 97 | + const uint16_t spritesCount = sf.getLE16(); |
| 98 | + const uint32_t totalSize = sf.getLE32(); |
85 | 99 |
|
86 |
| - std::vector<fheroes2::ICNHeader> headers( count_sprite ); |
87 |
| - for ( int ii = 0; ii < count_sprite; ++ii ) |
88 |
| - sf >> headers[ii]; |
| 100 | + const size_t beginPos = sf.tell(); |
89 | 101 |
|
90 |
| - for ( int ii = 0; ii < count_sprite; ++ii ) { |
91 |
| - const fheroes2::ICNHeader & head = headers[ii]; |
| 102 | + std::vector<fheroes2::ICNHeader> headers( spritesCount ); |
| 103 | + for ( fheroes2::ICNHeader & header : headers ) { |
| 104 | + sf >> header; |
| 105 | + } |
92 | 106 |
|
93 |
| - uint32_t data_size = ( ii + 1 != count_sprite ? headers[ii + 1].offsetData - head.offsetData : total_size - head.offsetData ); |
94 |
| - sf.seek( save_pos + head.offsetData ); |
95 |
| - std::cerr << data_size << std::endl; |
96 |
| - std::vector<uint8_t> buf = sf.getRaw( data_size ); |
| 107 | + for ( uint16_t spriteIdx = 0; spriteIdx < spritesCount; ++spriteIdx ) { |
| 108 | + const fheroes2::ICNHeader & header = headers[spriteIdx]; |
97 | 109 |
|
98 |
| - if ( !buf.empty() ) { |
99 |
| - const fheroes2::Sprite image = fheroes2::decodeICNSprite( &buf[0], data_size, head.width, head.height, head.offsetX, head.offsetY ); |
| 110 | + sf.seek( beginPos + header.offsetData ); |
| 111 | + |
| 112 | + const uint32_t dataSize = ( spriteIdx + 1 < spritesCount ? headers[spriteIdx + 1].offsetData - header.offsetData : totalSize - header.offsetData ); |
| 113 | + |
| 114 | + const std::vector<uint8_t> buf = sf.getRaw( dataSize ); |
| 115 | + if ( buf.empty() ) { |
| 116 | + continue; |
| 117 | + } |
| 118 | + |
| 119 | + const fheroes2::Sprite image = fheroes2::decodeICNSprite( buf.data(), dataSize, header.width, header.height, header.offsetX, header.offsetY ); |
100 | 120 |
|
101 | 121 | std::ostringstream os;
|
102 |
| - os << std::setw( 3 ) << std::setfill( '0' ) << ii; |
| 122 | + os << std::setw( 3 ) << std::setfill( '0' ) << spriteIdx; |
103 | 123 |
|
104 |
| - std::string dstfile = System::concatPath( prefix, os.str() ); |
| 124 | + std::string dstFileName = ( prefix / os.str() ).string(); |
105 | 125 |
|
106 | 126 | if ( fheroes2::isPNGFormatSupported() ) {
|
107 |
| - dstfile += ".png"; |
| 127 | + dstFileName += ".png"; |
108 | 128 | }
|
109 | 129 | else {
|
110 |
| - dstfile += ".bmp"; |
| 130 | + dstFileName += ".bmp"; |
111 | 131 | }
|
112 | 132 |
|
113 |
| - std::cout << "Image " << ii + 1 << " has offset of [" << static_cast<int32_t>( head.offsetX ) << ", " << static_cast<int32_t>( head.offsetY ) << "]" |
114 |
| - << std::endl; |
| 133 | + static_assert( std::is_same_v<decltype( header.offsetX ), uint16_t> && std::is_same_v<decltype( header.offsetY ), uint16_t>, |
| 134 | + "Offset types have been changed, check the casts below" ); |
115 | 135 |
|
116 |
| - fheroes2::Save( image, dstfile, 23 ); |
| 136 | + std::cout << "Image " << spriteIdx + 1 << " has offset of [" << static_cast<int16_t>( header.offsetX ) << ", " << static_cast<int16_t>( header.offsetY ) |
| 137 | + << "]" << std::endl; |
| 138 | + |
| 139 | + fheroes2::Save( image, dstFileName, 23 ); |
117 | 140 | }
|
118 | 141 | }
|
119 | 142 |
|
120 |
| - sf.close(); |
121 |
| - |
122 | 143 | return EXIT_SUCCESS;
|
123 | 144 | }
|
0 commit comments