/
sceAtrac.cpp
2626 lines (2300 loc) · 90 KB
/
sceAtrac.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <algorithm>
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/MIPS/MIPS.h"
#include "Core/CoreTiming.h"
#include "Core/MemMapHelpers.h"
#include "Core/Reporting.h"
#include "Core/Config.h"
#include "Core/Debugger/MemBlockInfo.h"
#include "Core/HW/MediaEngine.h"
#include "Core/HW/BufferQueue.h"
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceUtility.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HLE/sceAtrac.h"
#include "Core/System.h"
// Notes about sceAtrac buffer management
//
// sceAtrac decodes from a buffer the game fills, where this buffer is one of:
// * Not yet initialized (state NO DATA = 1)
// * The entire size of the audio data, and filled with audio data (state ALL DATA LOADED = 2)
// * The entire size, but only partially filled so far (state HALFWAY BUFFER = 3)
// * Smaller than the audio, sliding without any loop (state STREAMED WITHOUT LOOP = 4)
// * Smaller than the audio, sliding with a loop at the end (state STREAMED WITH LOOP AT END = 5)
// * Smaller with a second buffer to help with a loop in the middle (state STREAMED WITH SECOND BUF = 6)
// * Not managed, decoding using "low level" manual looping etc. (LOW LEVEL = 8)
// * Not managed, reserved externally - possibly by sceSas - through low level (RESERVED = 16)
//
// This buffer is generally filled by sceAtracAddStreamData, and where to fill it is given by
// either sceAtracGetStreamDataInfo when continuing to move forwards in the stream of audio data,
// or sceAtracGetBufferInfoForResetting when seeking to a specific location in the audio stream.
//
// State 6 indicates a second buffer is needed. This buffer is used to manage looping correctly.
// To determine how to fill it, the game will call sceAtracGetSecondBufferInfo, then after filling
// the buffer it will call sceAtracSetSecondBuffer.
// The second buffer will just contain the data for the end of loop. The "first" buffer may manage
// only the looped portion, or some of the part after the loop (depending on second buf size.)
//
// Most files will be in RIFF format. It's also possible to load in an OMA/AA3 format file, but
// ultimately this will share the same buffer - it's just offset a bit more.
//
// Low level decoding doesn't use the buffer, and decodes only a single packet at a time.
//
// Lastly, sceSas has some integration with sceAtrac, which allows setting an Atrac id as
// a voice for an SAS core. In this mode, the game will directly modify some of the context,
// but will largely only interact using sceSas.
//
// Note that this buffer is THE view of the audio stream. On a PSP, the firmware does not manage
// any cache or separate version of the buffer - at most it manages decode state from earlier in
// the buffer.
#define ATRAC_ERROR_API_FAIL 0x80630002
#define ATRAC_ERROR_NO_ATRACID 0x80630003
#define ATRAC_ERROR_INVALID_CODECTYPE 0x80630004
#define ATRAC_ERROR_BAD_ATRACID 0x80630005
#define ATRAC_ERROR_UNKNOWN_FORMAT 0x80630006
#define ATRAC_ERROR_WRONG_CODECTYPE 0x80630007
#define ATRAC_ERROR_BAD_CODEC_PARAMS 0x80630008
#define ATRAC_ERROR_ALL_DATA_LOADED 0x80630009
#define ATRAC_ERROR_NO_DATA 0x80630010
#define ATRAC_ERROR_SIZE_TOO_SMALL 0x80630011
#define ATRAC_ERROR_SECOND_BUFFER_NEEDED 0x80630012
#define ATRAC_ERROR_INCORRECT_READ_SIZE 0x80630013
#define ATRAC_ERROR_BAD_SAMPLE 0x80630015
#define ATRAC_ERROR_BAD_FIRST_RESET_SIZE 0x80630016
#define ATRAC_ERROR_BAD_SECOND_RESET_SIZE 0x80630017
#define ATRAC_ERROR_ADD_DATA_IS_TOO_BIG 0x80630018
#define ATRAC_ERROR_NOT_MONO 0x80630019
#define ATRAC_ERROR_NO_LOOP_INFORMATION 0x80630021
#define ATRAC_ERROR_SECOND_BUFFER_NOT_NEEDED 0x80630022
#define ATRAC_ERROR_BUFFER_IS_EMPTY 0x80630023
#define ATRAC_ERROR_ALL_DATA_DECODED 0x80630024
#define ATRAC_ERROR_IS_LOW_LEVEL 0x80630031
#define ATRAC_ERROR_IS_FOR_SCESAS 0x80630040
#define ATRAC_ERROR_AA3_INVALID_DATA 0x80631003
#define ATRAC_ERROR_AA3_SIZE_TOO_SMALL 0x80631004
#define AT3_MAGIC 0x0270
#define AT3_PLUS_MAGIC 0xFFFE
#define PSP_MODE_AT_3_PLUS 0x00001000
#define PSP_MODE_AT_3 0x00001001
const int RIFF_CHUNK_MAGIC = 0x46464952;
const int RIFF_WAVE_MAGIC = 0x45564157;
const int FMT_CHUNK_MAGIC = 0x20746D66;
const int DATA_CHUNK_MAGIC = 0x61746164;
const int SMPL_CHUNK_MAGIC = 0x6C706D73;
const int FACT_CHUNK_MAGIC = 0x74636166;
const int PSP_ATRAC_ALLDATA_IS_ON_MEMORY = -1;
const int PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY = -2;
const int PSP_ATRAC_LOOP_STREAM_DATA_IS_ON_MEMORY = -3;
const u32 ATRAC3_MAX_SAMPLES = 0x400;
const u32 ATRAC3PLUS_MAX_SAMPLES = 0x800;
const size_t overAllocBytes = 16384;
static const int atracDecodeDelay = 2300;
#ifdef USE_FFMPEG
extern "C" {
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libavutil/samplefmt.h"
#include "libavcodec/avcodec.h"
#include "libavutil/version.h"
}
#include "Core/FFMPEGCompat.h"
#endif // USE_FFMPEG
enum AtracDecodeResult {
ATDECODE_FAILED = -1,
ATDECODE_FEEDME = 0,
ATDECODE_GOTFRAME = 1,
ATDECODE_BADFRAME = 2,
};
struct InputBuffer {
// Address of the buffer.
u32 addr;
// Size of data read so far into dataBuf_ (to be removed.)
u32 size;
// Offset into addr at which new data is added.
u32 offset;
// Last writableBytes number (to be removed.)
u32 writableBytes;
// Unused, always 0.
u32 neededBytes;
// Total size of the entire file data.
u32 filesize;
// Offset into the file at which new data is read.
u32 fileoffset;
};
struct Atrac;
int __AtracSetContext(Atrac *atrac);
void _AtracGenerateContext(Atrac *atrac);
struct AtracLoopInfo {
int cuePointID;
int type;
int startSample;
int endSample;
int fraction;
int playCount;
};
#ifndef USE_FFMPEG
struct AVPacket {
uint8_t *data;
int size;
int64_t pos;
};
#endif
struct Atrac {
Atrac() : atracID_(-1), dataBuf_(0), decodePos_(0), bufferPos_(0),
channels_(0), outputChannels_(2), bitrate_(64), bytesPerFrame_(0), bufferMaxSize_(0), jointStereo_(0),
currentSample_(0), endSample_(0), firstSampleOffset_(0), dataOff_(0),
loopStartSample_(-1), loopEndSample_(-1), loopNum_(0),
failedDecode_(false), ignoreDataBuf_(false), codecType_(0),
bufferState_(ATRAC_STATUS_NO_DATA) {
memset(&first_, 0, sizeof(first_));
memset(&second_, 0, sizeof(second_));
#ifdef USE_FFMPEG
codecCtx_ = nullptr;
swrCtx_ = nullptr;
frame_ = nullptr;
packet_ = nullptr;
#endif // USE_FFMPEG
context_ = 0;
}
~Atrac() {
ResetData();
}
void ResetData() {
#ifdef USE_FFMPEG
ReleaseFFMPEGContext();
#endif // USE_FFMPEG
if (dataBuf_)
delete [] dataBuf_;
dataBuf_ = 0;
ignoreDataBuf_ = false;
bufferState_ = ATRAC_STATUS_NO_DATA;
if (context_.IsValid())
kernelMemory.Free(context_.ptr);
// Clean slate time.
failedDecode_ = false;
}
void SetBufferState() {
if (bufferMaxSize_ >= first_.filesize) {
if (first_.size < first_.filesize) {
// The buffer is big enough, but we don't have all the data yet.
bufferState_ = ATRAC_STATUS_HALFWAY_BUFFER;
} else {
bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
}
} else {
if (loopEndSample_ <= 0) {
// There's no looping, but we need to stream the data in our buffer.
bufferState_ = ATRAC_STATUS_STREAMED_WITHOUT_LOOP;
} else if (loopEndSample_ == endSample_ + firstSampleOffset_ + (int)FirstOffsetExtra()) {
bufferState_ = ATRAC_STATUS_STREAMED_LOOP_FROM_END;
} else {
bufferState_ = ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER;
}
}
}
void DoState(PointerWrap &p) {
auto s = p.Section("Atrac", 1, 9);
if (!s)
return;
Do(p, channels_);
Do(p, outputChannels_);
if (s >= 5) {
Do(p, jointStereo_);
}
Do(p, atracID_);
Do(p, first_);
Do(p, bufferMaxSize_);
Do(p, codecType_);
Do(p, currentSample_);
Do(p, endSample_);
Do(p, firstSampleOffset_);
if (s >= 3) {
Do(p, dataOff_);
} else {
dataOff_ = firstSampleOffset_;
}
u32 hasDataBuf = dataBuf_ != nullptr;
Do(p, hasDataBuf);
if (hasDataBuf) {
if (p.mode == p.MODE_READ) {
if (dataBuf_)
delete [] dataBuf_;
dataBuf_ = new u8[first_.filesize + overAllocBytes];
memset(dataBuf_, 0, first_.filesize + overAllocBytes);
}
DoArray(p, dataBuf_, first_.filesize);
}
Do(p, second_);
Do(p, decodePos_);
if (s < 9) {
u32 oldDecodeEnd = 0;
Do(p, oldDecodeEnd);
}
if (s >= 4) {
Do(p, bufferPos_);
} else {
bufferPos_ = decodePos_;
}
Do(p, bitrate_);
Do(p, bytesPerFrame_);
Do(p, loopinfo_);
if (s < 9) {
int oldLoopInfoNum = 42;
Do(p, oldLoopInfoNum);
}
Do(p, loopStartSample_);
Do(p, loopEndSample_);
Do(p, loopNum_);
Do(p, context_);
if (s >= 6) {
Do(p, bufferState_);
} else {
if (dataBuf_ == nullptr) {
bufferState_ = ATRAC_STATUS_NO_DATA;
} else {
SetBufferState();
}
}
if (s >= 7) {
Do(p, ignoreDataBuf_);
} else {
ignoreDataBuf_ = false;
}
if (s >= 9) {
Do(p, bufferValidBytes_);
Do(p, bufferHeaderSize_);
} else {
bufferHeaderSize_ = dataOff_;
bufferValidBytes_ = std::min(first_.size - dataOff_, StreamBufferEnd() - dataOff_);
if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {
bufferPos_ = dataOff_;
}
}
if (s < 8 && bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
// We didn't actually allow the second buffer to be set this far back.
// Pretend it's a regular loop, we'll just try our best.
bufferState_ = ATRAC_STATUS_STREAMED_LOOP_FROM_END;
}
// Make sure to do this late; it depends on things like bytesPerFrame_.
if (p.mode == p.MODE_READ && bufferState_ != ATRAC_STATUS_NO_DATA) {
__AtracSetContext(this);
}
if (s >= 2 && s < 9) {
bool oldResetBuffer = false;
Do(p, oldResetBuffer);
}
}
int Analyze(u32 addr, u32 size);
int AnalyzeAA3(u32 addr, u32 size, u32 filesize);
u32 SamplesPerFrame() const {
return codecType_ == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES;
}
u32 FirstOffsetExtra() const {
return codecType_ == PSP_CODEC_AT3PLUS ? 368 : 69;
}
u32 DecodePosBySample(int sample) const {
return (u32)(firstSampleOffset_ + sample / (int)SamplesPerFrame() * bytesPerFrame_);
}
u32 FileOffsetBySample(int sample) const {
int offsetSample = sample + firstSampleOffset_;
int frameOffset = offsetSample / (int)SamplesPerFrame();
return (u32)(dataOff_ + bytesPerFrame_ + frameOffset * bytesPerFrame_);
}
int RemainingFrames() const {
if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {
// Meaning, infinite I guess? We've got it all.
return PSP_ATRAC_ALLDATA_IS_ON_MEMORY;
}
u32 currentFileOffset = FileOffsetBySample(currentSample_ - SamplesPerFrame() + FirstOffsetExtra());
if (first_.fileoffset >= first_.filesize) {
if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {
return PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY;
}
int loopEndAdjusted = loopEndSample_ - FirstOffsetExtra() - firstSampleOffset_;
if (bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER && currentSample_ > loopEndAdjusted) {
// No longer looping in this case, outside the loop.
return PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY;
}
if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK && loopNum_ == 0) {
return PSP_ATRAC_LOOP_STREAM_DATA_IS_ON_MEMORY;
}
}
if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {
// Since we're streaming, the remaining frames are what's valid in the buffer.
return bufferValidBytes_ / bytesPerFrame_;
}
// Since the first frame is shorter by this offset, add to round up at this offset.
const int remainingBytes = first_.fileoffset - currentFileOffset;
if (remainingBytes < 0) {
// Just in case. Shouldn't happen, but once did by mistake.
return 0;
}
return remainingBytes / bytesPerFrame_;
}
int atracID_;
u8 *dataBuf_;
u32 decodePos_;
// Used by low-level decoding and to track streaming.
u32 bufferPos_;
u32 bufferValidBytes_;
u32 bufferHeaderSize_ = 0;
u16 channels_;
u16 outputChannels_;
u32 bitrate_;
u16 bytesPerFrame_;
u32 bufferMaxSize_;
int jointStereo_;
int currentSample_;
int endSample_;
int firstSampleOffset_;
// Offset of the first sample in the input buffer
int dataOff_;
std::vector<AtracLoopInfo> loopinfo_;
int loopStartSample_;
int loopEndSample_;
int loopNum_;
bool failedDecode_;
// Indicates that the dataBuf_ array should not be used.
bool ignoreDataBuf_;
u32 codecType_;
AtracStatus bufferState_;
InputBuffer first_;
InputBuffer second_;
PSPPointer<SceAtracId> context_;
#ifdef USE_FFMPEG
AVCodecContext *codecCtx_ = nullptr;
SwrContext *swrCtx_ = nullptr;
AVFrame *frame_ = nullptr;
AVPacket *packet_ = nullptr;
#endif // USE_FFMPEG
#ifdef USE_FFMPEG
void ReleaseFFMPEGContext() {
// All of these allow null pointers.
av_freep(&frame_);
swr_free(&swrCtx_);
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 52, 0)
// If necessary, extradata is automatically freed.
avcodec_free_context(&codecCtx_);
#else
// Future versions may add other things to free, but avcodec_free_context didn't exist yet here.
// Some old versions crash when we try to free extradata and subtitle_header, so let's not. A minor
// leak is better than a segfault.
// av_freep(&codecCtx_->extradata);
// av_freep(&codecCtx_->subtitle_header);
avcodec_close(codecCtx_);
av_freep(&codecCtx_);
#endif
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
av_packet_free(&packet_);
#else
av_free_packet(packet_);
delete packet_;
packet_ = nullptr;
#endif
}
#endif // USE_FFMPEG
void ForceSeekToSample(int sample) {
#ifdef USE_FFMPEG
avcodec_flush_buffers(codecCtx_);
// Discard any pending packet data.
packet_->size = 0;
#endif
currentSample_ = sample;
}
u8 *BufferStart() {
return ignoreDataBuf_ ? Memory::GetPointerWrite(first_.addr) : dataBuf_;
}
void SeekToSample(int sample) {
#ifdef USE_FFMPEG
// Discard any pending packet data.
packet_->size = 0;
// It seems like the PSP aligns the sample position to 0x800...?
const u32 offsetSamples = firstSampleOffset_ + FirstOffsetExtra();
const u32 unalignedSamples = (offsetSamples + sample) % SamplesPerFrame();
int seekFrame = sample + offsetSamples - unalignedSamples;
if ((sample != currentSample_ || sample == 0) && codecCtx_ != nullptr) {
// Prefill the decode buffer with packets before the first sample offset.
avcodec_flush_buffers(codecCtx_);
int adjust = 0;
if (sample == 0) {
int offsetSamples = firstSampleOffset_ + FirstOffsetExtra();
adjust = -(int)(offsetSamples % SamplesPerFrame());
}
const u32 off = FileOffsetBySample(sample + adjust);
const u32 backfill = bytesPerFrame_ * 2;
const u32 start = off - dataOff_ < backfill ? dataOff_ : off - backfill;
for (u32 pos = start; pos < off; pos += bytesPerFrame_) {
av_init_packet(packet_);
packet_->data = BufferStart() + pos;
packet_->size = bytesPerFrame_;
packet_->pos = pos;
// Process the packet, we don't care about success.
DecodePacket();
}
}
#endif // USE_FFMPEG
currentSample_ = sample;
}
uint32_t CurBufferAddress(int adjust = 0) {
u32 off = FileOffsetBySample(currentSample_ + adjust);
if (off < first_.size && ignoreDataBuf_) {
return first_.addr + off;
}
// If it's in dataBug, it's not in PSP memory.
return 0;
}
bool FillPacket(int adjust = 0) {
u32 off = FileOffsetBySample(currentSample_ + adjust);
if (off < first_.size) {
#ifdef USE_FFMPEG
av_init_packet(packet_);
packet_->data = BufferStart() + off;
packet_->size = std::min((u32)bytesPerFrame_, first_.size - off);
packet_->pos = off;
#endif // USE_FFMPEG
return true;
} else {
return false;
}
return true;
}
bool FillLowLevelPacket(u8 *ptr) {
#ifdef USE_FFMPEG
av_init_packet(packet_);
packet_->data = ptr;
packet_->size = bytesPerFrame_;
packet_->pos = 0;
#endif // USE_FFMPEG
return true;
}
AtracDecodeResult DecodePacket() {
#ifdef USE_FFMPEG
if (codecCtx_ == nullptr) {
return ATDECODE_FAILED;
}
int got_frame = 0;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
if (packet_->size != 0) {
int err = avcodec_send_packet(codecCtx_, packet_);
if (err < 0) {
ERROR_LOG_REPORT(ME, "avcodec_send_packet: Error decoding audio %d / %08x", err, err);
failedDecode_ = true;
return ATDECODE_FAILED;
}
}
int err = avcodec_receive_frame(codecCtx_, frame_);
int bytes_read = 0;
if (err >= 0) {
bytes_read = frame_->pkt_size;
got_frame = 1;
} else if (err != AVERROR(EAGAIN)) {
bytes_read = err;
}
#else
int bytes_read = avcodec_decode_audio4(codecCtx_, frame_, &got_frame, packet_);
#endif
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
av_packet_unref(packet_);
#else
av_free_packet(packet_);
#endif
if (bytes_read == AVERROR_PATCHWELCOME) {
ERROR_LOG(ME, "Unsupported feature in ATRAC audio.");
// Let's try the next packet.
packet_->size = 0;
return ATDECODE_BADFRAME;
} else if (bytes_read < 0) {
ERROR_LOG_REPORT(ME, "avcodec_decode_audio4: Error decoding audio %d / %08x", bytes_read, bytes_read);
failedDecode_ = true;
return ATDECODE_FAILED;
}
return got_frame ? ATDECODE_GOTFRAME : ATDECODE_FEEDME;
#else
return ATDECODE_BADFRAME;
#endif // USE_FFMPEG
}
void CalculateStreamInfo(u32 *readOffset);
u32 StreamBufferEnd() const {
// The buffer is always aligned to a frame in size, not counting an optional header.
// The header will only initially exist after the data is first set.
u32 framesAfterHeader = (bufferMaxSize_ - bufferHeaderSize_) / bytesPerFrame_;
return framesAfterHeader * bytesPerFrame_ + bufferHeaderSize_;
}
void ConsumeFrame() {
bufferPos_ += bytesPerFrame_;
if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {
if (bufferValidBytes_ > bytesPerFrame_) {
bufferValidBytes_ -= bytesPerFrame_;
} else {
bufferValidBytes_ = 0;
}
}
if (bufferPos_ >= StreamBufferEnd()) {
// Wrap around... theoretically, this should only happen at exactly StreamBufferEnd.
bufferPos_ -= StreamBufferEnd();
bufferHeaderSize_ = 0;
}
}
private:
void AnalyzeReset();
};
struct AtracSingleResetBufferInfo {
u32_le writePosPtr;
u32_le writableBytes;
u32_le minWriteBytes;
u32_le filePos;
};
struct AtracResetBufferInfo {
AtracSingleResetBufferInfo first;
AtracSingleResetBufferInfo second;
};
const int PSP_NUM_ATRAC_IDS = 6;
static bool atracInited = true;
static Atrac *atracIDs[PSP_NUM_ATRAC_IDS];
static u32 atracIDTypes[PSP_NUM_ATRAC_IDS];
static int atracLibVersion = 0;
static u32 atracLibCrc = 0;
void __AtracInit() {
atracInited = true;
memset(atracIDs, 0, sizeof(atracIDs));
// Start with 2 of each in this order.
atracIDTypes[0] = PSP_MODE_AT_3_PLUS;
atracIDTypes[1] = PSP_MODE_AT_3_PLUS;
atracIDTypes[2] = PSP_MODE_AT_3;
atracIDTypes[3] = PSP_MODE_AT_3;
atracIDTypes[4] = 0;
atracIDTypes[5] = 0;
#ifdef USE_FFMPEG
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 18, 100)
avcodec_register_all();
#endif
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 12, 100)
av_register_all();
#endif
#endif // USE_FFMPEG
}
void __AtracLoadModule(int version, u32 crc) {
atracLibVersion = version;
atracLibCrc = crc;
INFO_LOG(ME, "AtracInit, atracLibVersion 0x%0x, atracLibcrc %x", atracLibVersion, atracLibCrc);
}
void __AtracDoState(PointerWrap &p) {
auto s = p.Section("sceAtrac", 1, 2);
if (!s)
return;
Do(p, atracInited);
for (int i = 0; i < PSP_NUM_ATRAC_IDS; ++i) {
bool valid = atracIDs[i] != NULL;
Do(p, valid);
if (valid) {
Do(p, atracIDs[i]);
} else {
delete atracIDs[i];
atracIDs[i] = NULL;
}
}
DoArray(p, atracIDTypes, PSP_NUM_ATRAC_IDS);
if (s < 2) {
atracLibVersion = 0;
atracLibCrc = 0;
}
else {
Do(p, atracLibVersion);
Do(p, atracLibCrc);
}
}
void __AtracShutdown() {
for (size_t i = 0; i < ARRAY_SIZE(atracIDs); ++i) {
delete atracIDs[i];
atracIDs[i] = NULL;
}
}
static Atrac *getAtrac(int atracID) {
if (atracID < 0 || atracID >= PSP_NUM_ATRAC_IDS) {
return NULL;
}
Atrac *atrac = atracIDs[atracID];
if (atrac && atrac->context_.IsValid()) {
// Read in any changes from the game to the context.
// TODO: Might be better to just always track in RAM.
atrac->bufferState_ = atrac->context_->info.state;
// This value is actually abused by games to store the SAS voice number.
atrac->loopNum_ = atrac->context_->info.loopNum;
}
return atrac;
}
static int createAtrac(Atrac *atrac) {
for (int i = 0; i < (int)ARRAY_SIZE(atracIDs); ++i) {
if (atracIDTypes[i] == atrac->codecType_ && atracIDs[i] == 0) {
atracIDs[i] = atrac;
atrac->atracID_ = i;
return i;
}
}
return ATRAC_ERROR_NO_ATRACID;
}
static int deleteAtrac(int atracID) {
if (atracID >= 0 && atracID < PSP_NUM_ATRAC_IDS) {
if (atracIDs[atracID] != nullptr) {
delete atracIDs[atracID];
atracIDs[atracID] = nullptr;
return 0;
}
}
return ATRAC_ERROR_BAD_ATRACID;
}
void Atrac::AnalyzeReset() {
// Reset some values.
codecType_ = 0;
currentSample_ = 0;
endSample_ = -1;
loopNum_ = 0;
loopinfo_.clear();
loopStartSample_ = -1;
loopEndSample_ = -1;
decodePos_ = 0;
bufferPos_ = 0;
channels_ = 2;
}
struct RIFFFmtChunk {
u16_le fmtTag;
u16_le channels;
u32_le samplerate;
u32_le avgBytesPerSec;
u16_le blockAlign;
};
int Atrac::Analyze(u32 addr, u32 size) {
first_.addr = addr;
first_.size = size;
AnalyzeReset();
// 72 is about the size of the minimum required data to even be valid.
if (first_.size < 72) {
return hleReportError(ME, ATRAC_ERROR_SIZE_TOO_SMALL, "buffer too small");
}
if (!Memory::IsValidAddress(first_.addr)) {
return hleReportWarning(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "invalid buffer address");
}
// TODO: Validate stuff.
if (Memory::Read_U32(first_.addr) != RIFF_CHUNK_MAGIC) {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid RIFF header");
}
u32 offset = 8;
firstSampleOffset_ = 0;
while (Memory::Read_U32(first_.addr + offset) != RIFF_WAVE_MAGIC) {
// Get the size preceding the magic.
int chunk = Memory::Read_U32(first_.addr + offset - 4);
// Round the chunk size up to the nearest 2.
offset += chunk + (chunk & 1);
if (offset + 12 > first_.size) {
return hleReportError(ME, ATRAC_ERROR_SIZE_TOO_SMALL, "too small for WAVE chunk at %d", offset);
}
if (Memory::Read_U32(first_.addr + offset) != RIFF_CHUNK_MAGIC) {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "RIFF chunk did not contain WAVE");
}
offset += 8;
}
offset += 4;
if (offset != 12) {
WARN_LOG_REPORT(ME, "RIFF chunk at offset: %d", offset);
}
// RIFF size excluding chunk header.
first_.filesize = Memory::Read_U32(first_.addr + offset - 8) + 8;
// Even if the RIFF size is too low, it may simply be incorrect. This works on real firmware.
u32 maxSize = std::max(first_.filesize, first_.size);
bool bfoundData = false;
u32 dataChunkSize = 0;
int sampleOffsetAdjust = 0;
while (maxSize >= offset + 8 && !bfoundData) {
int chunkMagic = Memory::Read_U32(first_.addr + offset);
u32 chunkSize = Memory::Read_U32(first_.addr + offset + 4);
// Account for odd sized chunks.
if (chunkSize & 1) {
WARN_LOG_REPORT_ONCE(oddchunk, ME, "RIFF chunk had uneven size");
}
chunkSize += (chunkSize & 1);
offset += 8;
if (chunkSize > maxSize - offset)
break;
switch (chunkMagic) {
case FMT_CHUNK_MAGIC:
{
if (codecType_ != 0) {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "multiple fmt definitions");
}
auto at3fmt = PSPPointer<const RIFFFmtChunk>::Create(first_.addr + offset);
if (chunkSize < 32 || (at3fmt->fmtTag == AT3_PLUS_MAGIC && chunkSize < 52)) {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "fmt definition too small (%d)", chunkSize);
}
if (at3fmt->fmtTag == AT3_MAGIC)
codecType_ = PSP_MODE_AT_3;
else if (at3fmt->fmtTag == AT3_PLUS_MAGIC)
codecType_ = PSP_MODE_AT_3_PLUS;
else {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid fmt magic: %04x", at3fmt->fmtTag);
}
channels_ = at3fmt->channels;
if (channels_ != 1 && channels_ != 2) {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid channel count: %d", channels_);
}
if (at3fmt->samplerate != 44100) {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "unsupported sample rate: %d", at3fmt->samplerate);
}
bitrate_ = at3fmt->avgBytesPerSec * 8;
bytesPerFrame_ = at3fmt->blockAlign;
if (bytesPerFrame_ == 0) {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid bytes per frame: %d", bytesPerFrame_);
}
// TODO: There are some format specific bytes here which seem to have fixed values?
// Probably don't need them.
if (at3fmt->fmtTag == AT3_MAGIC) {
// This is the offset to the jointStereo_ field.
jointStereo_ = Memory::Read_U32(first_.addr + offset + 24);
}
}
break;
case FACT_CHUNK_MAGIC:
{
endSample_ = Memory::Read_U32(first_.addr + offset);
if (chunkSize >= 8) {
firstSampleOffset_ = Memory::Read_U32(first_.addr + offset + 4);
}
if (chunkSize >= 12) {
u32 largerOffset = Memory::Read_U32(first_.addr + offset + 8);
sampleOffsetAdjust = firstSampleOffset_ - largerOffset;
}
}
break;
case SMPL_CHUNK_MAGIC:
{
if (chunkSize < 32) {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "smpl chunk too small (%d)", chunkSize);
}
int checkNumLoops = Memory::Read_U32(first_.addr + offset + 28);
if (checkNumLoops != 0 && chunkSize < 36 + 20) {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "smpl chunk too small for loop (%d, %d)", checkNumLoops, chunkSize);
}
if (checkNumLoops < 0) {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "bad checkNumLoops (%d)", checkNumLoops);
}
loopinfo_.resize(checkNumLoops);
u32 loopinfoAddr = first_.addr + offset + 36;
// The PSP only cares about the first loop start and end, it seems.
// Most likely can skip the rest of this data, but it's not hurting anyone.
for (int i = 0; i < checkNumLoops && 36 + (u32)i < chunkSize; i++, loopinfoAddr += 24) {
loopinfo_[i].cuePointID = Memory::Read_U32(loopinfoAddr);
loopinfo_[i].type = Memory::Read_U32(loopinfoAddr + 4);
loopinfo_[i].startSample = Memory::Read_U32(loopinfoAddr + 8);
loopinfo_[i].endSample = Memory::Read_U32(loopinfoAddr + 12);
loopinfo_[i].fraction = Memory::Read_U32(loopinfoAddr + 16);
loopinfo_[i].playCount = Memory::Read_U32(loopinfoAddr + 20);
if (loopinfo_[i].startSample >= loopinfo_[i].endSample) {
return hleReportError(ME, ATRAC_ERROR_BAD_CODEC_PARAMS, "loop starts after it ends");
}
}
}
break;
case DATA_CHUNK_MAGIC:
{
bfoundData = true;
dataOff_ = offset;
dataChunkSize = chunkSize;
if (first_.filesize < offset + chunkSize) {
WARN_LOG_REPORT(ME, "Atrac data chunk extends beyond riff chunk");
first_.filesize = offset + chunkSize;
}
}
break;
}
offset += chunkSize;
}
if (codecType_ == 0) {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "could not detect codec");
}
if (!bfoundData) {
return hleReportError(ME, ATRAC_ERROR_SIZE_TOO_SMALL, "no data chunk");
}
// set the loopStartSample_ and loopEndSample_ by loopinfo_
if (loopinfo_.size() > 0) {
loopStartSample_ = loopinfo_[0].startSample + FirstOffsetExtra() + sampleOffsetAdjust;
loopEndSample_ = loopinfo_[0].endSample + FirstOffsetExtra() + sampleOffsetAdjust;
} else {
loopStartSample_ = -1;
loopEndSample_ = -1;
}
// if there is no correct endsample, try to guess it
if (endSample_ <= 0 && bytesPerFrame_ != 0) {
endSample_ = (dataChunkSize / bytesPerFrame_) * SamplesPerFrame();
endSample_ -= firstSampleOffset_ + FirstOffsetExtra();
}
endSample_ -= 1;
if (loopEndSample_ != -1 && loopEndSample_ > endSample_ + firstSampleOffset_ + (int)FirstOffsetExtra()) {
return hleReportError(ME, ATRAC_ERROR_BAD_CODEC_PARAMS, "loop after end of data");
}
return 0;
}
int Atrac::AnalyzeAA3(u32 addr, u32 size, u32 filesize) {
first_.addr = addr;
first_.size = size;
first_.filesize = filesize;
AnalyzeReset();
if (first_.size < 10) {
return hleReportError(ME, ATRAC_ERROR_AA3_SIZE_TOO_SMALL, "buffer too small");
}
// TODO: Make sure this validation is correct, more testing.
const u8 *buffer = Memory::GetPointer(first_.addr);
if (buffer[0] != 'e' || buffer[1] != 'a' || buffer[2] != '3') {
return hleReportError(ME, ATRAC_ERROR_AA3_INVALID_DATA, "invalid ea3 magic bytes");
}